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_schedule_task_add(i->schedule, 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   SILC_LOG_DEBUG(("Adding new channel entry"));
562
563   channel = silc_calloc(1, sizeof(*channel));
564   channel->channel_name = channel_name;
565   channel->mode = mode;
566   channel->id = id;
567   channel->router = router;
568   channel->channel_key = channel_key;
569   channel->hmac = hmac;
570   if (!channel->hmac)
571     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
572       silc_free(channel);
573       return NULL;
574     }
575
576   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
577                                              NULL, NULL, NULL, TRUE);
578
579   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
580                         (void *)channel->id, (void *)channel, FALSE)) {
581     silc_hmac_free(channel->hmac);
582     silc_hash_table_free(channel->user_list);
583     silc_free(channel);
584     return NULL;
585   }
586
587   return channel;
588 }
589
590 /* Foreach callbcak to free all users from the channel when deleting a
591    channel entry. */
592
593 static void silc_idlist_del_channel_foreach(void *key, void *context,
594                                             void *user_context)
595 {
596   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
597
598   /* Remove the context from the client's channel hash table as that
599      table and channel's user_list hash table share this same context. */
600   silc_hash_table_del(chl->client->channels, chl->channel);
601   silc_free(chl);
602 }
603
604 /* Free channel entry.  This free's everything. */
605
606 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
607 {
608   SILC_LOG_DEBUG(("Start"));
609
610   if (entry) {
611     /* Remove from cache */
612     if (entry->id)
613       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
614         return FALSE;
615
616     /* Free data */
617     if (entry->channel_name)
618       silc_free(entry->channel_name);
619     if (entry->id)
620       silc_free(entry->id);
621     if (entry->topic)
622       silc_free(entry->topic);
623     if (entry->channel_key)
624       silc_cipher_free(entry->channel_key);
625     if (entry->key) {
626       memset(entry->key, 0, entry->key_len / 8);
627       silc_free(entry->key);
628     }
629     if (entry->cipher)
630       silc_free(entry->cipher);
631     if (entry->hmac_name)
632       silc_free(entry->hmac_name);
633     if (entry->rekey)
634       silc_free(entry->rekey);
635
636     /* Free all client entrys from the users list. The silc_hash_table_free
637        will free all the entries so they are not freed at the foreach 
638        callback. */
639     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
640                             NULL);
641     silc_hash_table_free(entry->user_list);
642
643     memset(entry, 'F', sizeof(*entry));
644     silc_free(entry);
645     return TRUE;
646   }
647
648   return FALSE;
649 }
650
651 /* Finds channel by channel name. Channel names are unique and they
652    are not case-sensitive. */
653
654 SilcChannelEntry
655 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
656                                  SilcIDCacheEntry *ret_entry)
657 {
658   SilcIDCacheEntry id_cache = NULL;
659
660   SILC_LOG_DEBUG(("Channel by name"));
661
662   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
663     return NULL;
664
665   if (ret_entry)
666     *ret_entry = id_cache;
667
668   SILC_LOG_DEBUG(("Found"));
669
670   return id_cache->context;
671 }
672
673 /* Finds channel by Channel ID. */
674
675 SilcChannelEntry
676 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
677                                SilcIDCacheEntry *ret_entry)
678 {
679   SilcIDCacheEntry id_cache = NULL;
680   SilcChannelEntry channel;
681
682   if (!id)
683     return NULL;
684
685   SILC_LOG_DEBUG(("Channel ID (%s)",
686                   silc_id_render(id, SILC_ID_CHANNEL)));
687
688   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
689     return NULL;
690
691   channel = (SilcChannelEntry)id_cache->context;
692
693   if (ret_entry)
694     *ret_entry = id_cache;
695
696   SILC_LOG_DEBUG(("Found"));
697
698   return channel;
699 }
700
701 /* Replaces old Channel ID with new one. This is done when router forces
702    normal server to change Channel ID. */
703
704 SilcChannelEntry
705 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
706                                SilcChannelID *new_id)
707 {
708   SilcIDCacheEntry id_cache = NULL;
709   SilcChannelEntry channel;
710
711   if (!old_id || !new_id)
712     return NULL;
713
714   SILC_LOG_DEBUG(("Replacing Channel ID"));
715
716   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
717                                    &id_cache))
718     return NULL;
719
720   channel = (SilcChannelEntry)id_cache->context;
721
722   /* Remove the old entry and add a new one */
723
724   silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
725
726   silc_free(channel->id);
727   channel->id = new_id;
728
729   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
730                    channel, FALSE);
731
732   SILC_LOG_DEBUG(("Replaced"));
733
734   return channel;
735 }
736
737 /* Returns channels from the ID list. If the `channel_id' is NULL then
738    all channels are returned. */
739
740 SilcChannelEntry *
741 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
742                          uint32 *channels_count)
743 {
744   SilcIDCacheList list = NULL;
745   SilcIDCacheEntry id_cache = NULL;
746   SilcChannelEntry *channels = NULL;
747   int i = 0;
748
749   SILC_LOG_DEBUG(("Start"));
750
751   if (!channel_id) {
752     if (!silc_idcache_get_all(id_list->channels, &list))
753       return NULL;
754
755     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
756     
757     i = 0;
758     silc_idcache_list_first(list, &id_cache);
759     channels[i++] = (SilcChannelEntry)id_cache->context;
760     
761     while (silc_idcache_list_next(list, &id_cache))
762       channels[i++] = (SilcChannelEntry)id_cache->context;
763     
764     silc_idcache_list_free(list);
765   } else {
766     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
767       return NULL;
768
769     i = 1;
770     channels = silc_calloc(1, sizeof(*channels));
771     channels[0] = (SilcChannelEntry)id_cache->context;
772   }
773
774   if (channels_count)
775     *channels_count = i;
776
777   return channels;
778 }