updates.
[silc.git] / apps / silcd / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "serverincludes.h"
23 #include "idlist.h"
24
25 /******************************************************************************
26
27                              Common functions
28
29 ******************************************************************************/
30
31 /* This function is used to add keys and stuff to common ID entry data
32    structure. */
33
34 void silc_idlist_add_data(void *entry, SilcIDListData idata)
35 {
36   SilcIDListData data = (SilcIDListData)entry;
37   data->send_key = idata->send_key;
38   data->receive_key = idata->receive_key;
39   data->rekey = idata->rekey;
40   data->hash = idata->hash;
41   data->hmac_send = idata->hmac_send;
42   data->hmac_receive = idata->hmac_receive;
43   data->public_key = idata->public_key;
44   data->last_receive = idata->last_receive;
45   data->last_sent = idata->last_sent;
46   data->registered = idata->registered;
47 }
48
49 /* Free's all data in the common ID entry data structure. */
50
51 void silc_idlist_del_data(void *entry)
52 {
53   SilcIDListData idata = (SilcIDListData)entry;
54   if (idata->send_key)
55     silc_cipher_free(idata->send_key);
56   if (idata->receive_key)
57     silc_cipher_free(idata->receive_key);
58   if (idata->rekey) {
59     if (idata->rekey->send_enc_key) {
60       memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
61       silc_free(idata->rekey->send_enc_key);
62     }
63     silc_free(idata->rekey);
64   }
65   if (idata->hmac_send)         /* Same as idata->hmac_receive */
66     silc_hmac_free(idata->hmac_send);
67   if (idata->public_key)
68     silc_pkcs_public_key_free(idata->public_key);
69 }
70
71 /* Purges ID cache */
72
73 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
74 {
75   SilcIDListPurge i = (SilcIDListPurge)context;
76
77   SILC_LOG_DEBUG(("Start"));
78
79   silc_idcache_purge(i->cache);
80   silc_task_register(i->timeout_queue, 0, 
81                      silc_idlist_purge,
82                      (void *)i, 600, 0,
83                      SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
84 }
85
86 /******************************************************************************
87
88                           Server entry functions
89
90 ******************************************************************************/
91
92 /* Add new server entry. This adds the new server entry to ID cache and
93    returns the allocated entry object or NULL on error. This is called
94    when new server connects to us. We also add ourselves to cache with
95    this function. */
96
97 SilcServerEntry 
98 silc_idlist_add_server(SilcIDList id_list, 
99                        char *server_name, int server_type,
100                        SilcServerID *id, SilcServerEntry router,
101                        void *connection)
102 {
103   SilcServerEntry server;
104
105   SILC_LOG_DEBUG(("Adding new server entry"));
106
107   server = silc_calloc(1, sizeof(*server));
108   server->server_name = server_name;
109   server->server_type = server_type;
110   server->id = id;
111   server->router = router;
112   server->connection = connection;
113
114   if (!silc_idcache_add(id_list->servers, server->server_name, 
115                         (void *)server->id, (void *)server, FALSE)) {
116     silc_free(server);
117     return NULL;
118   }
119
120   return server;
121 }
122
123 /* Finds server by Server ID */
124
125 SilcServerEntry
126 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
127                               SilcIDCacheEntry *ret_entry)
128 {
129   SilcIDCacheEntry id_cache = NULL;
130   SilcServerEntry server;
131
132   if (!id)
133     return NULL;
134
135   SILC_LOG_DEBUG(("Server ID (%s)",
136                   silc_id_render(id, SILC_ID_SERVER)));
137
138   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
139                                    &id_cache))
140     return NULL;
141
142   server = (SilcServerEntry)id_cache->context;
143
144   if (ret_entry)
145     *ret_entry = id_cache;
146
147   return server;
148 }
149
150 /* Find server by name */
151
152 SilcServerEntry
153 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
154                                 SilcIDCacheEntry *ret_entry)
155 {
156   SilcIDCacheEntry id_cache = NULL;
157   SilcServerEntry server;
158
159   SILC_LOG_DEBUG(("Server by name `%s'", name));
160
161   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
162     return NULL;
163
164   server = (SilcServerEntry)id_cache->context;
165   
166   if (ret_entry)
167     *ret_entry = id_cache;
168
169   SILC_LOG_DEBUG(("Found"));
170
171   return server;
172 }
173
174 /* Find server by connection parameters, hostname and port */
175
176 SilcServerEntry
177 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
178                                 int port, SilcIDCacheEntry *ret_entry)
179 {
180   SilcIDCacheList list = NULL;
181   SilcIDCacheEntry id_cache = NULL;
182   SilcServerEntry server = NULL;
183   SilcSocketConnection sock;
184  
185   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
186
187   if (!silc_idcache_get_all(id_list->servers, &list))
188     return NULL;
189
190   if (!silc_idcache_list_first(list, &id_cache)) {
191     silc_idcache_list_free(list);
192     return NULL;
193   }
194
195   while (id_cache) {
196     server = (SilcServerEntry)id_cache->context;
197     sock = (SilcSocketConnection)server->connection;
198     
199     if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
200                  (sock->ip && !strcasecmp(sock->ip, hostname)))
201         && sock->port == port)
202       break;
203
204     id_cache = NULL;
205     server = NULL;
206
207     if (!silc_idcache_list_next(list, &id_cache))
208       break;
209   }
210   
211   silc_idcache_list_free(list);
212
213   if (ret_entry)
214     *ret_entry = id_cache;
215
216   SILC_LOG_DEBUG(("Found"));
217
218   return server;
219 }
220
221 /* Replaces old Server ID with new one */ 
222
223 SilcServerEntry
224 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
225                               SilcServerID *new_id)
226 {
227   SilcIDCacheEntry id_cache = NULL;
228   SilcServerEntry server;
229
230   if (!old_id || !new_id)
231     return NULL;
232
233   SILC_LOG_DEBUG(("Replacing Server ID"));
234
235   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
236                                    &id_cache))
237     return NULL;
238
239   server = (SilcServerEntry)id_cache->context;
240   silc_free(server->id);
241   server->id = new_id;
242
243   /* Remove the old entry and add a new one */
244   silc_idcache_del_by_context(id_list->servers, server);
245   silc_idcache_add(id_list->servers, server->server_name, server->id, 
246                    server, FALSE);
247
248   SILC_LOG_DEBUG(("Found"));
249
250   return server;
251 }
252
253 /* Removes and free's server entry from ID list */
254
255 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
256 {
257   SILC_LOG_DEBUG(("Start"));
258
259   if (entry) {
260     /* Remove from cache */
261     if (entry->id)
262       if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
263         return FALSE;
264
265     /* Free data */
266     if (entry->server_name)
267       silc_free(entry->server_name);
268     if (entry->id)
269       silc_free(entry->id);
270
271     memset(entry, 'F', sizeof(*entry));
272     silc_free(entry);
273     return TRUE;
274   }
275
276   return FALSE;
277 }
278
279 /******************************************************************************
280
281                           Client entry functions
282
283 ******************************************************************************/
284
285 /* Add new client entry. This adds the client entry to ID cache system
286    and returns the allocated client entry or NULL on error.  This is
287    called when new client connection is accepted to the server. If The
288    `router' is provided then the all server routines assume that the client
289    is not directly connected local client but it has router set and is
290    remote.  If this is the case then `connection' must be NULL.  If, on the
291    other hand, the `connection' is provided then the client is assumed
292    to be directly connected local client and `router' must be NULL. */
293
294 SilcClientEntry
295 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
296                        char *userinfo, SilcClientID *id, 
297                        SilcServerEntry router, void *connection)
298 {
299   SilcClientEntry client;
300
301   SILC_LOG_DEBUG(("Adding new client entry"));
302
303   client = silc_calloc(1, sizeof(*client));
304   client->nickname = nickname;
305   client->username = username;
306   client->userinfo = userinfo;
307   client->id = id;
308   client->router = router;
309   client->connection = connection;
310   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
311                  client_list);
312
313   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
314                         (void *)client, FALSE)) {
315     silc_free(client);
316     return NULL;
317   }
318
319   return client;
320 }
321
322 /* Free client entry. This free's everything and removes the entry
323    from ID cache. Call silc_idlist_del_data before calling this one. */
324
325 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
326 {
327   SILC_LOG_DEBUG(("Start"));
328
329   if (entry) {
330     /* Remove from cache */
331     if (entry->id)
332       if (!silc_idcache_del_by_context(id_list->clients, entry))
333         return FALSE;
334
335     /* Free data */
336     if (entry->nickname)
337       silc_free(entry->nickname);
338     if (entry->username)
339       silc_free(entry->username);
340     if (entry->userinfo)
341       silc_free(entry->userinfo);
342     if (entry->id)
343       silc_free(entry->id);
344
345     memset(entry, 'F', sizeof(*entry));
346     silc_free(entry);
347
348     return TRUE;
349   }
350
351   return FALSE;
352 }
353
354 /* Returns all clients matching requested nickname. Number of clients is
355    returned to `clients_count'. Caller must free the returned table. */
356
357 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
358                                         char *server, 
359                                         SilcClientEntry **clients,
360                                         uint32 *clients_count)
361 {
362   SilcIDCacheList list = NULL;
363   SilcIDCacheEntry id_cache = NULL;
364   int i;
365
366   SILC_LOG_DEBUG(("Start"));
367
368   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
369     return FALSE;
370
371   *clients = silc_realloc(*clients, 
372                           (silc_idcache_list_count(list) + *clients_count) * 
373                           sizeof(**clients));
374
375   i = 0;
376   silc_idcache_list_first(list, &id_cache);
377   (*clients)[i++] = (SilcClientEntry)id_cache->context;
378
379   while (silc_idcache_list_next(list, &id_cache))
380     (*clients)[i++] = (SilcClientEntry)id_cache->context;
381   
382   silc_idcache_list_free(list);
383   
384   *clients_count += i;
385
386   return TRUE;
387 }
388
389 /* Returns all clients matching requested nickname hash. Number of clients
390    is returned to `clients_count'. Caller must free the returned table. */
391
392 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
393                                     SilcHash md5hash,
394                                     SilcClientEntry **clients,
395                                     uint32 *clients_count)
396 {
397   SilcIDCacheList list = NULL;
398   SilcIDCacheEntry id_cache = NULL;
399   unsigned char hash[32];
400   int i;
401   SilcClientID client_id;
402
403   SILC_LOG_DEBUG(("Start"));
404
405   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
406
407   memset(&client_id, 0, sizeof(client_id));
408   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
409   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
410     return FALSE;
411
412   *clients = silc_realloc(*clients, 
413                           (silc_idcache_list_count(list) + *clients_count) * 
414                           sizeof(**clients));
415
416   i = 0;
417   silc_idcache_list_first(list, &id_cache);
418   (*clients)[i++] = (SilcClientEntry)id_cache->context;
419
420   while (silc_idcache_list_next(list, &id_cache))
421     (*clients)[i++] = (SilcClientEntry)id_cache->context;
422   
423   silc_idcache_list_free(list);
424   
425   *clients_count += i;
426
427   return TRUE;
428 }
429
430 /* Finds client by Client ID */
431
432 SilcClientEntry
433 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
434                               SilcIDCacheEntry *ret_entry)
435 {
436   SilcIDCacheEntry id_cache = NULL;
437   SilcClientEntry client;
438
439   if (!id)
440     return NULL;
441
442   SILC_LOG_DEBUG(("Client ID (%s)", 
443                   silc_id_render(id, SILC_ID_CLIENT)));
444
445   /* Do extended search since the normal ID comparison function for
446      Client ID's compares only the hash from the Client ID and not the
447      entire ID. The silc_hash_client_id_compare compares the entire
448      Client ID as we want to find one specific Client ID. */
449   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
450                                        NULL, NULL, 
451                                        silc_hash_client_id_compare, NULL,
452                                        &id_cache))
453     return NULL;
454
455   client = (SilcClientEntry)id_cache->context;
456
457   if (ret_entry)
458     *ret_entry = id_cache;
459
460   SILC_LOG_DEBUG(("Found"));
461
462   return client;
463 }
464
465 /* Replaces old Client ID with new one */
466
467 SilcClientEntry
468 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
469                               SilcClientID *new_id)
470 {
471   SilcIDCacheEntry id_cache = NULL;
472   SilcClientEntry client;
473
474   if (!old_id || !new_id)
475     return NULL;
476
477   SILC_LOG_DEBUG(("Replacing Client ID"));
478
479   /* Do extended search since the normal ID comparison function for
480      Client ID's compares only the hash from the Client ID and not the
481      entire ID. The silc_hash_client_id_compare compares the entire
482      Client ID as we want to find one specific Client ID. */
483   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
484                                        NULL, NULL, 
485                                        silc_hash_client_id_compare, NULL,
486                                        &id_cache))
487     return NULL;
488
489   client = (SilcClientEntry)id_cache->context;
490   silc_free(client->id);
491   client->id = new_id;
492
493   /* Remove the old entry and add a new one */
494   silc_idcache_del_by_context(id_list->clients, client);
495   silc_idcache_add(id_list->clients, client->nickname, client->id, 
496                    client, FALSE);
497
498   SILC_LOG_DEBUG(("Replaced"));
499
500   return client;
501 }
502
503 /* Client cache entry destructor that is called when the cache is purged. */
504
505 void silc_idlist_client_destructor(SilcIDCache cache,
506                                    SilcIDCacheEntry entry)
507 {
508   SilcClientEntry client;
509
510   SILC_LOG_DEBUG(("Start"));
511
512   client = (SilcClientEntry)entry->context;
513   if (client) {
514     if (client->nickname)
515       silc_free(client->nickname);
516     if (client->username)
517       silc_free(client->username);
518     if (client->userinfo)
519       silc_free(client->userinfo);
520     if (client->id)
521       silc_free(client->id);
522
523     memset(client, 'F', sizeof(*client));
524     silc_free(client);
525   }
526 }
527
528 /******************************************************************************
529
530                           Channel entry functions
531
532 ******************************************************************************/
533
534 /* Add new channel entry. This add the new channel entry to the ID cache
535    system and returns the allocated entry or NULL on error. */
536
537 SilcChannelEntry
538 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
539                         SilcChannelID *id, SilcServerEntry router,
540                         SilcCipher channel_key, SilcHmac hmac)
541 {
542   SilcChannelEntry channel;
543
544   channel = silc_calloc(1, sizeof(*channel));
545   channel->channel_name = channel_name;
546   channel->mode = mode;
547   channel->id = id;
548   channel->router = router;
549   channel->channel_key = channel_key;
550   channel->hmac = hmac;
551   if (!channel->hmac)
552     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
553       silc_free(channel);
554       return NULL;
555     }
556
557   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
558                  channel_list);
559
560   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
561                         (void *)channel->id, (void *)channel, FALSE)) {
562     silc_free(channel);
563     return NULL;
564   }
565
566   return channel;
567 }
568
569 /* Free channel entry.  This free's everything. */
570
571 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
572 {
573   SILC_LOG_DEBUG(("Start"));
574
575   if (entry) {
576     SilcChannelClientEntry chl;
577
578     /* Remove from cache */
579     if (entry->id)
580       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
581         return FALSE;
582
583     /* Free data */
584     if (entry->channel_name)
585       silc_free(entry->channel_name);
586     if (entry->id)
587       silc_free(entry->id);
588     if (entry->topic)
589       silc_free(entry->topic);
590     if (entry->channel_key)
591       silc_cipher_free(entry->channel_key);
592     if (entry->key) {
593       memset(entry->key, 0, entry->key_len / 8);
594       silc_free(entry->key);
595     }
596     if (entry->cipher)
597       silc_free(entry->cipher);
598     if (entry->hmac_name)
599       silc_free(entry->hmac_name);
600     if (entry->rekey)
601       silc_free(entry->rekey);
602
603     /* Free all data, free also any reference from the client's channel
604        list since they share the same memory. */
605     silc_list_start(entry->user_list);
606     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
607       silc_list_del(chl->client->channels, chl);
608       silc_list_del(entry->user_list, chl);
609       silc_free(chl);
610     }
611
612     memset(entry, 'F', sizeof(*entry));
613     silc_free(entry);
614     return TRUE;
615   }
616
617   return FALSE;
618 }
619
620 /* Finds channel by channel name. Channel names are unique and they
621    are not case-sensitive. */
622
623 SilcChannelEntry
624 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
625                                  SilcIDCacheEntry *ret_entry)
626 {
627   SilcIDCacheEntry id_cache = NULL;
628
629   SILC_LOG_DEBUG(("Channel by name"));
630
631   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
632     return NULL;
633
634   if (ret_entry)
635     *ret_entry = id_cache;
636
637   SILC_LOG_DEBUG(("Found"));
638
639   return id_cache->context;
640 }
641
642 /* Finds channel by Channel ID. */
643
644 SilcChannelEntry
645 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
646                                SilcIDCacheEntry *ret_entry)
647 {
648   SilcIDCacheEntry id_cache = NULL;
649   SilcChannelEntry channel;
650
651   if (!id)
652     return NULL;
653
654   SILC_LOG_DEBUG(("Channel ID (%s)",
655                   silc_id_render(id, SILC_ID_CHANNEL)));
656
657   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
658     return NULL;
659
660   channel = (SilcChannelEntry)id_cache->context;
661
662   if (ret_entry)
663     *ret_entry = id_cache;
664
665   SILC_LOG_DEBUG(("Found"));
666
667   return channel;
668 }
669
670 /* Replaces old Channel ID with new one. This is done when router forces
671    normal server to change Channel ID. */
672
673 SilcChannelEntry
674 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
675                                SilcChannelID *new_id)
676 {
677   SilcIDCacheEntry id_cache = NULL;
678   SilcChannelEntry channel;
679
680   if (!old_id || !new_id)
681     return NULL;
682
683   SILC_LOG_DEBUG(("Replacing Channel ID"));
684
685   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
686                                    &id_cache))
687     return NULL;
688
689   channel = (SilcChannelEntry)id_cache->context;
690   silc_free(channel->id);
691   channel->id = new_id;
692
693   /* Remove the old entry and add a new one */
694   silc_idcache_del_by_context(id_list->channels, channel);
695   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
696                    channel, FALSE);
697
698   SILC_LOG_DEBUG(("Replaced"));
699
700   return channel;
701 }
702
703 /* Returns channels from the ID list. If the `channel_id' is NULL then
704    all channels are returned. */
705
706 SilcChannelEntry *
707 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
708                          uint32 *channels_count)
709 {
710   SilcIDCacheList list = NULL;
711   SilcIDCacheEntry id_cache = NULL;
712   SilcChannelEntry *channels = NULL;
713   int i = 0;
714
715   SILC_LOG_DEBUG(("Start"));
716
717   if (!channel_id) {
718     if (!silc_idcache_get_all(id_list->channels, &list))
719       return NULL;
720
721     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
722     
723     i = 0;
724     silc_idcache_list_first(list, &id_cache);
725     channels[i++] = (SilcChannelEntry)id_cache->context;
726     
727     while (silc_idcache_list_next(list, &id_cache))
728       channels[i++] = (SilcChannelEntry)id_cache->context;
729     
730     silc_idcache_list_free(list);
731   } else {
732     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
733       return NULL;
734
735     i = 1;
736     channels = silc_calloc(1, sizeof(*channels));
737     channels[0] = (SilcChannelEntry)id_cache->context;
738   }
739
740   if (channels_count)
741     *channels_count = i;
742
743   return channels;
744 }