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