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   SILC_LOG_DEBUG(("Found"));
148
149   return server;
150 }
151
152 /* Find server by name */
153
154 SilcServerEntry
155 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
156                                 SilcIDCacheEntry *ret_entry)
157 {
158   SilcIDCacheEntry id_cache = NULL;
159   SilcServerEntry server;
160
161   SILC_LOG_DEBUG(("Server by name `%s'", name));
162
163   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
164     return NULL;
165
166   server = (SilcServerEntry)id_cache->context;
167   
168   if (ret_entry)
169     *ret_entry = id_cache;
170
171   SILC_LOG_DEBUG(("Found"));
172
173   return server;
174 }
175
176 /* Find server by connection parameters, hostname and port */
177
178 SilcServerEntry
179 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
180                                 int port, SilcIDCacheEntry *ret_entry)
181 {
182   SilcIDCacheList list = NULL;
183   SilcIDCacheEntry id_cache = NULL;
184   SilcServerEntry server = NULL;
185   SilcSocketConnection sock;
186  
187   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
188
189   if (!silc_idcache_get_all(id_list->servers, &list))
190     return NULL;
191
192   if (!silc_idcache_list_first(list, &id_cache)) {
193     silc_idcache_list_free(list);
194     return NULL;
195   }
196
197   while (id_cache) {
198     server = (SilcServerEntry)id_cache->context;
199     sock = (SilcSocketConnection)server->connection;
200     
201     if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
202                  (sock->ip && !strcasecmp(sock->ip, hostname)))
203         && sock->port == port)
204       break;
205
206     id_cache = NULL;
207     server = NULL;
208
209     if (!silc_idcache_list_next(list, &id_cache))
210       break;
211   }
212   
213   silc_idcache_list_free(list);
214
215   if (ret_entry)
216     *ret_entry = id_cache;
217
218   SILC_LOG_DEBUG(("Found"));
219
220   return server;
221 }
222
223 /* Replaces old Server ID with new one */ 
224
225 SilcServerEntry
226 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
227                               SilcServerID *new_id)
228 {
229   SilcIDCacheEntry id_cache = NULL;
230   SilcServerEntry server;
231
232   if (!old_id || !new_id)
233     return NULL;
234
235   SILC_LOG_DEBUG(("Replacing Server ID"));
236
237   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
238                                    &id_cache))
239     return NULL;
240
241   server = (SilcServerEntry)id_cache->context;
242
243   /* Remove the old entry and add a new one */
244
245   silc_idcache_del_by_id(id_list->servers, (void *)server->id);
246
247   silc_free(server->id);
248   server->id = new_id;
249
250   silc_idcache_add(id_list->servers, server->server_name, server->id, 
251                    server, FALSE);
252
253   SILC_LOG_DEBUG(("Found"));
254
255   return server;
256 }
257
258 /* Removes and free's server entry from ID list */
259
260 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
261 {
262   SILC_LOG_DEBUG(("Start"));
263
264   if (entry) {
265     /* Remove from cache */
266     if (entry->id)
267       if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
268         return FALSE;
269
270     /* Free data */
271     if (entry->server_name)
272       silc_free(entry->server_name);
273     if (entry->id)
274       silc_free(entry->id);
275
276     memset(entry, 'F', sizeof(*entry));
277     silc_free(entry);
278     return TRUE;
279   }
280
281   return FALSE;
282 }
283
284 /******************************************************************************
285
286                           Client entry functions
287
288 ******************************************************************************/
289
290 /* Add new client entry. This adds the client entry to ID cache system
291    and returns the allocated client entry or NULL on error.  This is
292    called when new client connection is accepted to the server. If The
293    `router' is provided then the all server routines assume that the client
294    is not directly connected local client but it has router set and is
295    remote.  If this is the case then `connection' must be NULL.  If, on the
296    other hand, the `connection' is provided then the client is assumed
297    to be directly connected local client and `router' must be NULL. */
298
299 SilcClientEntry
300 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
301                        char *userinfo, SilcClientID *id, 
302                        SilcServerEntry router, void *connection)
303 {
304   SilcClientEntry client;
305
306   SILC_LOG_DEBUG(("Adding new client entry"));
307
308   client = silc_calloc(1, sizeof(*client));
309   client->nickname = nickname;
310   client->username = username;
311   client->userinfo = userinfo;
312   client->id = id;
313   client->router = router;
314   client->connection = connection;
315   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
316                                            NULL, NULL, NULL, NULL, TRUE);
317
318   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
319                         (void *)client, FALSE)) {
320     silc_hash_table_free(client->channels);
321     silc_free(client);
322     return NULL;
323   }
324
325   return client;
326 }
327
328 /* Free client entry. This free's everything and removes the entry
329    from ID cache. Call silc_idlist_del_data before calling this one. */
330
331 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
332 {
333   SILC_LOG_DEBUG(("Start"));
334
335   if (entry) {
336     /* Remove from cache */
337     if (entry->id)
338       if (!silc_idcache_del_by_context(id_list->clients, entry))
339         return FALSE;
340
341     /* Free data */
342     if (entry->nickname)
343       silc_free(entry->nickname);
344     if (entry->username)
345       silc_free(entry->username);
346     if (entry->userinfo)
347       silc_free(entry->userinfo);
348     if (entry->id)
349       silc_free(entry->id);
350
351     memset(entry, 'F', sizeof(*entry));
352     silc_free(entry);
353
354     return TRUE;
355   }
356
357   return FALSE;
358 }
359
360 /* Returns all clients matching requested nickname. Number of clients is
361    returned to `clients_count'. Caller must free the returned table. */
362
363 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
364                                         char *server, 
365                                         SilcClientEntry **clients,
366                                         uint32 *clients_count)
367 {
368   SilcIDCacheList list = NULL;
369   SilcIDCacheEntry id_cache = NULL;
370   int i;
371
372   SILC_LOG_DEBUG(("Start"));
373
374   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
375     return FALSE;
376
377   *clients = silc_realloc(*clients, 
378                           (silc_idcache_list_count(list) + *clients_count) * 
379                           sizeof(**clients));
380
381   i = 0;
382   silc_idcache_list_first(list, &id_cache);
383   (*clients)[i++] = (SilcClientEntry)id_cache->context;
384
385   while (silc_idcache_list_next(list, &id_cache))
386     (*clients)[i++] = (SilcClientEntry)id_cache->context;
387   
388   silc_idcache_list_free(list);
389   
390   *clients_count += i;
391
392   SILC_LOG_DEBUG(("Found %d clients", *clients_count));
393
394   return TRUE;
395 }
396
397 /* Returns all clients matching requested nickname hash. Number of clients
398    is returned to `clients_count'. Caller must free the returned table. */
399
400 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
401                                     SilcHash md5hash,
402                                     SilcClientEntry **clients,
403                                     uint32 *clients_count)
404 {
405   SilcIDCacheList list = NULL;
406   SilcIDCacheEntry id_cache = NULL;
407   unsigned char hash[32];
408   int i;
409   SilcClientID client_id;
410
411   SILC_LOG_DEBUG(("Start"));
412
413   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
414
415   /* As the Client ID is hashed in the ID cache by hashing only the hash
416      from the Client ID, we can do a lookup with only the hash not the
417      other parts of the ID and get all the clients with that hash, ie.
418      with that nickname, as the hash is from the nickname. */
419   memset(&client_id, 0, sizeof(client_id));
420   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
421   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
422     return FALSE;
423
424   *clients = silc_realloc(*clients, 
425                           (silc_idcache_list_count(list) + *clients_count) * 
426                           sizeof(**clients));
427
428   i = 0;
429   silc_idcache_list_first(list, &id_cache);
430   (*clients)[i++] = (SilcClientEntry)id_cache->context;
431
432   while (silc_idcache_list_next(list, &id_cache))
433     (*clients)[i++] = (SilcClientEntry)id_cache->context;
434   
435   silc_idcache_list_free(list);
436   
437   *clients_count += i;
438
439   SILC_LOG_DEBUG(("Found %d clients", *clients_count));
440
441   return TRUE;
442 }
443
444 /* Finds client by Client ID */
445
446 SilcClientEntry
447 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
448                               SilcIDCacheEntry *ret_entry)
449 {
450   SilcIDCacheEntry id_cache = NULL;
451   SilcClientEntry client;
452
453   if (!id)
454     return NULL;
455
456   SILC_LOG_DEBUG(("Client ID (%s)", 
457                   silc_id_render(id, SILC_ID_CLIENT)));
458
459   /* Do extended search since the normal ID comparison function for
460      Client ID's compares only the hash from the Client ID and not the
461      entire ID. The silc_hash_client_id_compare compares the entire
462      Client ID as we want to find one specific Client ID. */
463   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
464                                        NULL, NULL, 
465                                        silc_hash_client_id_compare, NULL,
466                                        &id_cache))
467     return NULL;
468
469   client = (SilcClientEntry)id_cache->context;
470
471   if (ret_entry)
472     *ret_entry = id_cache;
473
474   SILC_LOG_DEBUG(("Found"));
475
476   return client;
477 }
478
479 /* Replaces old Client ID with new one */
480
481 SilcClientEntry
482 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
483                               SilcClientID *new_id)
484 {
485   SilcIDCacheEntry id_cache = NULL;
486   SilcClientEntry client;
487
488   if (!old_id || !new_id)
489     return NULL;
490
491   SILC_LOG_DEBUG(("Replacing Client ID"));
492
493   /* Do extended search since the normal ID comparison function for
494      Client ID's compares only the hash from the Client ID and not the
495      entire ID. The silc_hash_client_id_compare compares the entire
496      Client ID as we want to find one specific Client ID. */
497   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
498                                        NULL, NULL, 
499                                        silc_hash_client_id_compare, NULL,
500                                        &id_cache))
501     return NULL;
502
503   client = (SilcClientEntry)id_cache->context;
504
505   /* Remove the old entry and add a new one */
506
507   silc_idcache_del_by_context(id_list->clients, client);
508
509   silc_free(client->id);
510   client->id = new_id;
511
512   silc_idcache_add(id_list->clients, client->nickname, client->id, 
513                    client, FALSE);
514
515   SILC_LOG_DEBUG(("Replaced"));
516
517   return client;
518 }
519
520 /* Client cache entry destructor that is called when the cache is purged. */
521
522 void silc_idlist_client_destructor(SilcIDCache cache,
523                                    SilcIDCacheEntry entry)
524 {
525   SilcClientEntry client;
526
527   SILC_LOG_DEBUG(("Start"));
528
529   client = (SilcClientEntry)entry->context;
530   if (client) {
531     if (client->nickname)
532       silc_free(client->nickname);
533     if (client->username)
534       silc_free(client->username);
535     if (client->userinfo)
536       silc_free(client->userinfo);
537     if (client->id)
538       silc_free(client->id);
539
540     memset(client, 'F', sizeof(*client));
541     silc_free(client);
542   }
543 }
544
545 /******************************************************************************
546
547                           Channel entry functions
548
549 ******************************************************************************/
550
551 /* Add new channel entry. This add the new channel entry to the ID cache
552    system and returns the allocated entry or NULL on error. */
553
554 SilcChannelEntry
555 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
556                         SilcChannelID *id, SilcServerEntry router,
557                         SilcCipher channel_key, SilcHmac hmac)
558 {
559   SilcChannelEntry channel;
560
561   channel = silc_calloc(1, sizeof(*channel));
562   channel->channel_name = channel_name;
563   channel->mode = mode;
564   channel->id = id;
565   channel->router = router;
566   channel->channel_key = channel_key;
567   channel->hmac = hmac;
568   if (!channel->hmac)
569     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
570       silc_free(channel);
571       return NULL;
572     }
573
574   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
575                                              NULL, NULL, NULL, TRUE);
576
577   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
578                         (void *)channel->id, (void *)channel, FALSE)) {
579     silc_hmac_free(channel->hmac);
580     silc_hash_table_free(channel->user_list);
581     silc_free(channel);
582     return NULL;
583   }
584
585   return channel;
586 }
587
588 /* Foreach callbcak to free all users from the channel when deleting a
589    channel entry. */
590
591 static void silc_idlist_del_channel_foreach(void *key, void *context,
592                                             void *user_context)
593 {
594   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
595
596   /* Remove the context from the client's channel hash table as that
597      table and channel's user_list hash table share this same context. */
598   silc_hash_table_del(chl->client->channels, chl->channel);
599   silc_free(chl);
600 }
601
602 /* Free channel entry.  This free's everything. */
603
604 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
605 {
606   SILC_LOG_DEBUG(("Start"));
607
608   if (entry) {
609     /* Remove from cache */
610     if (entry->id)
611       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
612         return FALSE;
613
614     /* Free data */
615     if (entry->channel_name)
616       silc_free(entry->channel_name);
617     if (entry->id)
618       silc_free(entry->id);
619     if (entry->topic)
620       silc_free(entry->topic);
621     if (entry->channel_key)
622       silc_cipher_free(entry->channel_key);
623     if (entry->key) {
624       memset(entry->key, 0, entry->key_len / 8);
625       silc_free(entry->key);
626     }
627     if (entry->cipher)
628       silc_free(entry->cipher);
629     if (entry->hmac_name)
630       silc_free(entry->hmac_name);
631     if (entry->rekey)
632       silc_free(entry->rekey);
633
634     /* Free all client entrys from the users list. The silc_hash_table_free
635        will free all the entries so they are not freed at the foreach 
636        callback. */
637     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
638                             NULL);
639     silc_hash_table_free(entry->user_list);
640
641     memset(entry, 'F', sizeof(*entry));
642     silc_free(entry);
643     return TRUE;
644   }
645
646   return FALSE;
647 }
648
649 /* Finds channel by channel name. Channel names are unique and they
650    are not case-sensitive. */
651
652 SilcChannelEntry
653 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
654                                  SilcIDCacheEntry *ret_entry)
655 {
656   SilcIDCacheEntry id_cache = NULL;
657
658   SILC_LOG_DEBUG(("Channel by name"));
659
660   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
661     return NULL;
662
663   if (ret_entry)
664     *ret_entry = id_cache;
665
666   SILC_LOG_DEBUG(("Found"));
667
668   return id_cache->context;
669 }
670
671 /* Finds channel by Channel ID. */
672
673 SilcChannelEntry
674 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
675                                SilcIDCacheEntry *ret_entry)
676 {
677   SilcIDCacheEntry id_cache = NULL;
678   SilcChannelEntry channel;
679
680   if (!id)
681     return NULL;
682
683   SILC_LOG_DEBUG(("Channel ID (%s)",
684                   silc_id_render(id, SILC_ID_CHANNEL)));
685
686   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
687     return NULL;
688
689   channel = (SilcChannelEntry)id_cache->context;
690
691   if (ret_entry)
692     *ret_entry = id_cache;
693
694   SILC_LOG_DEBUG(("Found"));
695
696   return channel;
697 }
698
699 /* Replaces old Channel ID with new one. This is done when router forces
700    normal server to change Channel ID. */
701
702 SilcChannelEntry
703 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
704                                SilcChannelID *new_id)
705 {
706   SilcIDCacheEntry id_cache = NULL;
707   SilcChannelEntry channel;
708
709   if (!old_id || !new_id)
710     return NULL;
711
712   SILC_LOG_DEBUG(("Replacing Channel ID"));
713
714   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
715                                    &id_cache))
716     return NULL;
717
718   channel = (SilcChannelEntry)id_cache->context;
719
720   /* Remove the old entry and add a new one */
721
722   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
723
724   silc_free(channel->id);
725   channel->id = new_id;
726
727   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
728                    channel, FALSE);
729
730   SILC_LOG_DEBUG(("Replaced"));
731
732   return channel;
733 }
734
735 /* Returns channels from the ID list. If the `channel_id' is NULL then
736    all channels are returned. */
737
738 SilcChannelEntry *
739 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
740                          uint32 *channels_count)
741 {
742   SilcIDCacheList list = NULL;
743   SilcIDCacheEntry id_cache = NULL;
744   SilcChannelEntry *channels = NULL;
745   int i = 0;
746
747   SILC_LOG_DEBUG(("Start"));
748
749   if (!channel_id) {
750     if (!silc_idcache_get_all(id_list->channels, &list))
751       return NULL;
752
753     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
754     
755     i = 0;
756     silc_idcache_list_first(list, &id_cache);
757     channels[i++] = (SilcChannelEntry)id_cache->context;
758     
759     while (silc_idcache_list_next(list, &id_cache))
760       channels[i++] = (SilcChannelEntry)id_cache->context;
761     
762     silc_idcache_list_free(list);
763   } else {
764     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
765       return NULL;
766
767     i = 1;
768     channels = silc_calloc(1, sizeof(*channels));
769     channels[0] = (SilcChannelEntry)id_cache->context;
770   }
771
772   if (channels_count)
773     *channels_count = i;
774
775   return channels;
776 }