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   SILC_LOG_DEBUG(("Found %d clients", *clients_count));
387
388   return TRUE;
389 }
390
391 /* Returns all clients matching requested nickname hash. Number of clients
392    is returned to `clients_count'. Caller must free the returned table. */
393
394 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
395                                     SilcHash md5hash,
396                                     SilcClientEntry **clients,
397                                     uint32 *clients_count)
398 {
399   SilcIDCacheList list = NULL;
400   SilcIDCacheEntry id_cache = NULL;
401   unsigned char hash[32];
402   int i;
403   SilcClientID client_id;
404
405   SILC_LOG_DEBUG(("Start"));
406
407   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
408
409   /* As the Client ID is hashed in the ID cache by hashing only the hash
410      from the Client ID, we can do a lookup with only the hash not the
411      other parts of the ID and get all the clients with that hash, ie.
412      with that nickname, as the hash is from the nickname. */
413   memset(&client_id, 0, sizeof(client_id));
414   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
415   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
416     return FALSE;
417
418   *clients = silc_realloc(*clients, 
419                           (silc_idcache_list_count(list) + *clients_count) * 
420                           sizeof(**clients));
421
422   i = 0;
423   silc_idcache_list_first(list, &id_cache);
424   (*clients)[i++] = (SilcClientEntry)id_cache->context;
425
426   while (silc_idcache_list_next(list, &id_cache))
427     (*clients)[i++] = (SilcClientEntry)id_cache->context;
428   
429   silc_idcache_list_free(list);
430   
431   *clients_count += i;
432
433   SILC_LOG_DEBUG(("Found %d clients", *clients_count));
434
435   return TRUE;
436 }
437
438 /* Finds client by Client ID */
439
440 SilcClientEntry
441 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
442                               SilcIDCacheEntry *ret_entry)
443 {
444   SilcIDCacheEntry id_cache = NULL;
445   SilcClientEntry client;
446
447   if (!id)
448     return NULL;
449
450   SILC_LOG_DEBUG(("Client ID (%s)", 
451                   silc_id_render(id, SILC_ID_CLIENT)));
452
453   /* Do extended search since the normal ID comparison function for
454      Client ID's compares only the hash from the Client ID and not the
455      entire ID. The silc_hash_client_id_compare compares the entire
456      Client ID as we want to find one specific Client ID. */
457   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
458                                        NULL, NULL, 
459                                        silc_hash_client_id_compare, NULL,
460                                        &id_cache))
461     return NULL;
462
463   client = (SilcClientEntry)id_cache->context;
464
465   if (ret_entry)
466     *ret_entry = id_cache;
467
468   SILC_LOG_DEBUG(("Found"));
469
470   return client;
471 }
472
473 /* Replaces old Client ID with new one */
474
475 SilcClientEntry
476 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
477                               SilcClientID *new_id)
478 {
479   SilcIDCacheEntry id_cache = NULL;
480   SilcClientEntry client;
481
482   if (!old_id || !new_id)
483     return NULL;
484
485   SILC_LOG_DEBUG(("Replacing Client ID"));
486
487   /* Do extended search since the normal ID comparison function for
488      Client ID's compares only the hash from the Client ID and not the
489      entire ID. The silc_hash_client_id_compare compares the entire
490      Client ID as we want to find one specific Client ID. */
491   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
492                                        NULL, NULL, 
493                                        silc_hash_client_id_compare, NULL,
494                                        &id_cache))
495     return NULL;
496
497   client = (SilcClientEntry)id_cache->context;
498   silc_free(client->id);
499   client->id = new_id;
500
501   /* Remove the old entry and add a new one */
502   silc_idcache_del_by_context(id_list->clients, client);
503   silc_idcache_add(id_list->clients, client->nickname, client->id, 
504                    client, FALSE);
505
506   SILC_LOG_DEBUG(("Replaced"));
507
508   return client;
509 }
510
511 /* Client cache entry destructor that is called when the cache is purged. */
512
513 void silc_idlist_client_destructor(SilcIDCache cache,
514                                    SilcIDCacheEntry entry)
515 {
516   SilcClientEntry client;
517
518   SILC_LOG_DEBUG(("Start"));
519
520   client = (SilcClientEntry)entry->context;
521   if (client) {
522     if (client->nickname)
523       silc_free(client->nickname);
524     if (client->username)
525       silc_free(client->username);
526     if (client->userinfo)
527       silc_free(client->userinfo);
528     if (client->id)
529       silc_free(client->id);
530
531     memset(client, 'F', sizeof(*client));
532     silc_free(client);
533   }
534 }
535
536 /******************************************************************************
537
538                           Channel entry functions
539
540 ******************************************************************************/
541
542 /* Add new channel entry. This add the new channel entry to the ID cache
543    system and returns the allocated entry or NULL on error. */
544
545 SilcChannelEntry
546 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
547                         SilcChannelID *id, SilcServerEntry router,
548                         SilcCipher channel_key, SilcHmac hmac)
549 {
550   SilcChannelEntry channel;
551
552   channel = silc_calloc(1, sizeof(*channel));
553   channel->channel_name = channel_name;
554   channel->mode = mode;
555   channel->id = id;
556   channel->router = router;
557   channel->channel_key = channel_key;
558   channel->hmac = hmac;
559   if (!channel->hmac)
560     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
561       silc_free(channel);
562       return NULL;
563     }
564
565   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
566                  channel_list);
567
568   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
569                         (void *)channel->id, (void *)channel, FALSE)) {
570     silc_free(channel);
571     return NULL;
572   }
573
574   return channel;
575 }
576
577 /* Free channel entry.  This free's everything. */
578
579 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
580 {
581   SILC_LOG_DEBUG(("Start"));
582
583   if (entry) {
584     SilcChannelClientEntry chl;
585
586     /* Remove from cache */
587     if (entry->id)
588       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
589         return FALSE;
590
591     /* Free data */
592     if (entry->channel_name)
593       silc_free(entry->channel_name);
594     if (entry->id)
595       silc_free(entry->id);
596     if (entry->topic)
597       silc_free(entry->topic);
598     if (entry->channel_key)
599       silc_cipher_free(entry->channel_key);
600     if (entry->key) {
601       memset(entry->key, 0, entry->key_len / 8);
602       silc_free(entry->key);
603     }
604     if (entry->cipher)
605       silc_free(entry->cipher);
606     if (entry->hmac_name)
607       silc_free(entry->hmac_name);
608     if (entry->rekey)
609       silc_free(entry->rekey);
610
611     /* Free all data, free also any reference from the client's channel
612        list since they share the same memory. */
613     silc_list_start(entry->user_list);
614     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
615       silc_list_del(chl->client->channels, chl);
616       silc_list_del(entry->user_list, chl);
617       silc_free(chl);
618     }
619
620     memset(entry, 'F', sizeof(*entry));
621     silc_free(entry);
622     return TRUE;
623   }
624
625   return FALSE;
626 }
627
628 /* Finds channel by channel name. Channel names are unique and they
629    are not case-sensitive. */
630
631 SilcChannelEntry
632 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
633                                  SilcIDCacheEntry *ret_entry)
634 {
635   SilcIDCacheEntry id_cache = NULL;
636
637   SILC_LOG_DEBUG(("Channel by name"));
638
639   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
640     return NULL;
641
642   if (ret_entry)
643     *ret_entry = id_cache;
644
645   SILC_LOG_DEBUG(("Found"));
646
647   return id_cache->context;
648 }
649
650 /* Finds channel by Channel ID. */
651
652 SilcChannelEntry
653 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
654                                SilcIDCacheEntry *ret_entry)
655 {
656   SilcIDCacheEntry id_cache = NULL;
657   SilcChannelEntry channel;
658
659   if (!id)
660     return NULL;
661
662   SILC_LOG_DEBUG(("Channel ID (%s)",
663                   silc_id_render(id, SILC_ID_CHANNEL)));
664
665   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
666     return NULL;
667
668   channel = (SilcChannelEntry)id_cache->context;
669
670   if (ret_entry)
671     *ret_entry = id_cache;
672
673   SILC_LOG_DEBUG(("Found"));
674
675   return channel;
676 }
677
678 /* Replaces old Channel ID with new one. This is done when router forces
679    normal server to change Channel ID. */
680
681 SilcChannelEntry
682 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
683                                SilcChannelID *new_id)
684 {
685   SilcIDCacheEntry id_cache = NULL;
686   SilcChannelEntry channel;
687
688   if (!old_id || !new_id)
689     return NULL;
690
691   SILC_LOG_DEBUG(("Replacing Channel ID"));
692
693   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
694                                    &id_cache))
695     return NULL;
696
697   channel = (SilcChannelEntry)id_cache->context;
698   silc_free(channel->id);
699   channel->id = new_id;
700
701   /* Remove the old entry and add a new one */
702   silc_idcache_del_by_context(id_list->channels, channel);
703   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
704                    channel, FALSE);
705
706   SILC_LOG_DEBUG(("Replaced"));
707
708   return channel;
709 }
710
711 /* Returns channels from the ID list. If the `channel_id' is NULL then
712    all channels are returned. */
713
714 SilcChannelEntry *
715 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
716                          uint32 *channels_count)
717 {
718   SilcIDCacheList list = NULL;
719   SilcIDCacheEntry id_cache = NULL;
720   SilcChannelEntry *channels = NULL;
721   int i = 0;
722
723   SILC_LOG_DEBUG(("Start"));
724
725   if (!channel_id) {
726     if (!silc_idcache_get_all(id_list->channels, &list))
727       return NULL;
728
729     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
730     
731     i = 0;
732     silc_idcache_list_first(list, &id_cache);
733     channels[i++] = (SilcChannelEntry)id_cache->context;
734     
735     while (silc_idcache_list_next(list, &id_cache))
736       channels[i++] = (SilcChannelEntry)id_cache->context;
737     
738     silc_idcache_list_free(list);
739   } else {
740     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
741       return NULL;
742
743     i = 1;
744     channels = silc_calloc(1, sizeof(*channels));
745     channels[0] = (SilcChannelEntry)id_cache->context;
746   }
747
748   if (channels_count)
749     *channels_count = i;
750
751   return channels;
752 }