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                         server->server_name ? strlen(server->server_name) : 0,
116                         SILC_ID_SERVER, (void *)server->id, 
117                         (void *)server, TRUE, FALSE)) {
118     silc_free(server);
119     return NULL;
120   }
121
122   return server;
123 }
124
125 /* Finds server by Server ID */
126
127 SilcServerEntry
128 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
129                               SilcIDCacheEntry *ret_entry)
130 {
131   SilcIDCacheEntry id_cache = NULL;
132   SilcServerEntry server;
133
134   if (!id)
135     return NULL;
136
137   SILC_LOG_DEBUG(("Server ID (%s)",
138                   silc_id_render(id, SILC_ID_SERVER)));
139
140   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
141                                    SILC_ID_SERVER, &id_cache))
142     return NULL;
143
144   server = (SilcServerEntry)id_cache->context;
145
146   if (ret_entry)
147     *ret_entry = id_cache;
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_data_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_find_by_id(id_list->servers, SILC_ID_CACHE_ANY, 
190                                SILC_ID_SERVER, &list))
191     return NULL;
192
193   if (!silc_idcache_list_first(list, &id_cache)) {
194     silc_idcache_list_free(list);
195     return NULL;
196   }
197
198   while (id_cache) {
199     server = (SilcServerEntry)id_cache->context;
200     sock = (SilcSocketConnection)server->connection;
201     
202     if (sock && ((sock->hostname && !strcmp(sock->hostname, hostname)) ||
203                  (sock->ip && !strcmp(sock->ip, hostname)))
204         && sock->port == port)
205       break;
206
207     id_cache = NULL;
208     server = NULL;
209
210     if (!silc_idcache_list_next(list, &id_cache))
211       break;
212   }
213   
214   silc_idcache_list_free(list);
215
216   if (ret_entry)
217     *ret_entry = id_cache;
218
219   SILC_LOG_DEBUG(("Found"));
220
221   return server;
222 }
223
224 /* Replaces old Server ID with new one */ 
225
226 SilcServerEntry
227 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
228                               SilcServerID *new_id)
229 {
230   SilcIDCacheEntry id_cache = NULL;
231   SilcServerEntry server;
232
233   if (!old_id || !new_id)
234     return NULL;
235
236   SILC_LOG_DEBUG(("Replacing Server ID"));
237
238   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
239                                    SILC_ID_SERVER, &id_cache))
240     return NULL;
241
242   server = (SilcServerEntry)id_cache->context;
243   silc_free(server->id);
244   server->id = new_id;
245   id_cache->id = (void *)new_id;
246
247   SILC_LOG_DEBUG(("Found"));
248
249   return server;
250 }
251
252 /* Removes and free's server entry from ID list */
253
254 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
255 {
256   SILC_LOG_DEBUG(("Start"));
257
258   if (entry) {
259     /* Remove from cache */
260     if (entry->id)
261       if (!silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER, 
262                                   (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, unsigned char *nickname, 
296                        uint32 nickname_len, char *username, 
297                        char *userinfo, SilcClientID *id, 
298                        SilcServerEntry router, void *connection)
299 {
300   SilcClientEntry client;
301
302   SILC_LOG_DEBUG(("Adding new client entry"));
303
304   client = silc_calloc(1, sizeof(*client));
305   client->nickname = nickname;
306   client->username = username;
307   client->userinfo = userinfo;
308   client->id = id;
309   client->router = router;
310   client->connection = connection;
311   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
312                  client_list);
313
314   if (!silc_idcache_add(id_list->clients, nickname,  nickname_len,
315                         SILC_ID_CLIENT, (void *)client->id, 
316                         (void *)client, TRUE, FALSE)) {
317     silc_free(client);
318     return NULL;
319   }
320
321   return client;
322 }
323
324 /* Free client entry. This free's everything and removes the entry
325    from ID cache. Call silc_idlist_del_data before calling this one. */
326
327 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
328 {
329   SILC_LOG_DEBUG(("Start"));
330
331   if (entry) {
332     /* Remove from cache */
333     if (entry->id)
334       if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, 
335                                   (void *)entry->id))
336         return FALSE;
337
338     /* Free data */
339     if (entry->nickname)
340       silc_free(entry->nickname);
341     if (entry->username)
342       silc_free(entry->username);
343     if (entry->userinfo)
344       silc_free(entry->userinfo);
345     if (entry->id)
346       silc_free(entry->id);
347
348     memset(entry, 'F', sizeof(*entry));
349     silc_free(entry);
350
351     return TRUE;
352   }
353
354   return FALSE;
355 }
356
357 /* Returns all clients matching requested nickname. Number of clients is
358    returned to `clients_count'. Caller must free the returned table. */
359
360 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
361                                         char *server, 
362                                         SilcClientEntry **clients,
363                                         uint32 *clients_count)
364 {
365   SilcIDCacheList list = NULL;
366   SilcIDCacheEntry id_cache = NULL;
367   int i;
368
369   SILC_LOG_DEBUG(("Start"));
370
371   if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
372     return FALSE;
373
374   *clients = silc_realloc(*clients, 
375                           (silc_idcache_list_count(list) + *clients_count) * 
376                           sizeof(**clients));
377
378   i = 0;
379   silc_idcache_list_first(list, &id_cache);
380   (*clients)[i++] = (SilcClientEntry)id_cache->context;
381
382   while (silc_idcache_list_next(list, &id_cache))
383     (*clients)[i++] = (SilcClientEntry)id_cache->context;
384   
385   silc_idcache_list_free(list);
386   
387   *clients_count += i;
388
389   return TRUE;
390 }
391
392 /* Returns all clients matching requested nickname. Number of clients is
393    returned to `clients_count'. Caller must free the returned table. */
394 /* XXX This actually checks the data, which can be hash of the nickname
395    but is not if the client is local client. Global client on global
396    list may have hash.  Thus, this is not fully reliable function.
397    Instead this should probably check the hash from the list of client ID's. */
398
399 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
400                                     SilcHash md5hash,
401                                     SilcClientEntry **clients,
402                                     uint32 *clients_count)
403 {
404   SilcIDCacheList list = NULL;
405   SilcIDCacheEntry id_cache = NULL;
406   unsigned char hash[32];
407   int i;
408
409   SILC_LOG_DEBUG(("Start"));
410
411   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
412
413   if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
414     return FALSE;
415
416   *clients = silc_realloc(*clients, 
417                           (silc_idcache_list_count(list) + *clients_count) * 
418                           sizeof(**clients));
419
420   i = 0;
421   silc_idcache_list_first(list, &id_cache);
422   (*clients)[i++] = (SilcClientEntry)id_cache->context;
423
424   while (silc_idcache_list_next(list, &id_cache))
425     (*clients)[i++] = (SilcClientEntry)id_cache->context;
426   
427   silc_idcache_list_free(list);
428   
429   *clients_count += i;
430
431   return TRUE;
432 }
433
434 /* Finds client by Client ID */
435
436 SilcClientEntry
437 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
438                               SilcIDCacheEntry *ret_entry)
439 {
440   SilcIDCacheEntry id_cache = NULL;
441   SilcClientEntry client;
442
443   if (!id)
444     return NULL;
445
446   SILC_LOG_DEBUG(("Client ID (%s)", 
447                   silc_id_render(id, SILC_ID_CLIENT)));
448
449   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
450                                    SILC_ID_CLIENT, &id_cache))
451     return NULL;
452
453   client = (SilcClientEntry)id_cache->context;
454
455   if (ret_entry)
456     *ret_entry = id_cache;
457
458   SILC_LOG_DEBUG(("Found"));
459
460   return client;
461 }
462
463 /* Replaces old Client ID with new one */
464
465 SilcClientEntry
466 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
467                               SilcClientID *new_id)
468 {
469   SilcIDCacheEntry id_cache = NULL;
470   SilcClientEntry client;
471
472   if (!old_id || !new_id)
473     return NULL;
474
475   SILC_LOG_DEBUG(("Replacing Client ID"));
476
477   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
478                                    SILC_ID_CLIENT, &id_cache))
479     return NULL;
480
481   client = (SilcClientEntry)id_cache->context;
482   silc_free(client->id);
483   client->id = new_id;
484   id_cache->id = (void *)new_id;
485
486   /* If the old ID Cache data was the hash value of the old Client ID
487      replace it with the hash of new Client ID */
488   if (id_cache->data && SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
489     silc_free(id_cache->data);
490     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
491     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
492     silc_idcache_sort_by_data(id_list->clients);
493   }
494
495   SILC_LOG_DEBUG(("Replaced"));
496
497   return client;
498 }
499
500 /* Client cache entry destructor that is called when the cache is purged. */
501
502 void silc_idlist_client_destructor(SilcIDCache cache,
503                                    SilcIDCacheEntry entry)
504 {
505   SilcClientEntry client;
506
507   SILC_LOG_DEBUG(("Start"));
508
509   client = (SilcClientEntry)entry->context;
510   if (client) {
511     if (client->nickname)
512       silc_free(client->nickname);
513     if (client->username)
514       silc_free(client->username);
515     if (client->userinfo)
516       silc_free(client->userinfo);
517     if (client->id)
518       silc_free(client->id);
519
520     memset(client, 'F', sizeof(*client));
521     silc_free(client);
522   }
523 }
524
525 /******************************************************************************
526
527                           Channel entry functions
528
529 ******************************************************************************/
530
531 /* Add new channel entry. This add the new channel entry to the ID cache
532    system and returns the allocated entry or NULL on error. */
533
534 SilcChannelEntry
535 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
536                         SilcChannelID *id, SilcServerEntry router,
537                         SilcCipher channel_key, SilcHmac hmac)
538 {
539   SilcChannelEntry channel;
540
541   channel = silc_calloc(1, sizeof(*channel));
542   channel->channel_name = channel_name;
543   channel->mode = mode;
544   channel->id = id;
545   channel->router = router;
546   channel->channel_key = channel_key;
547   channel->hmac = hmac;
548   if (!channel->hmac)
549     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
550       silc_free(channel);
551       return NULL;
552     }
553
554   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
555                  channel_list);
556
557   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
558                         channel->channel_name ? strlen(channel->channel_name) :
559                         0, SILC_ID_CHANNEL, 
560                         (void *)channel->id, (void *)channel, TRUE, FALSE)) {
561     silc_free(channel);
562     return NULL;
563   }
564
565   return channel;
566 }
567
568 /* Free channel entry.  This free's everything. */
569
570 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
571 {
572   SILC_LOG_DEBUG(("Start"));
573
574   if (entry) {
575     SilcChannelClientEntry chl;
576
577     /* Remove from cache */
578     if (entry->id)
579       if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
580                                   (void *)entry->id))
581         return FALSE;
582
583     /* Free data */
584     if (entry->channel_name)
585       silc_free(entry->channel_name);
586     if (entry->id)
587       silc_free(entry->id);
588     if (entry->topic)
589       silc_free(entry->topic);
590     if (entry->channel_key)
591       silc_cipher_free(entry->channel_key);
592     if (entry->key) {
593       memset(entry->key, 0, entry->key_len / 8);
594       silc_free(entry->key);
595     }
596     if (entry->cipher)
597       silc_free(entry->cipher);
598     if (entry->hmac_name)
599       silc_free(entry->hmac_name);
600     if (entry->rekey)
601       silc_free(entry->rekey);
602
603     /* Free all data, free also any reference from the client's channel
604        list since they share the same memory. */
605     silc_list_start(entry->user_list);
606     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
607       silc_list_del(chl->client->channels, chl);
608       silc_list_del(entry->user_list, chl);
609       silc_free(chl);
610     }
611
612     memset(entry, 'F', sizeof(*entry));
613     silc_free(entry);
614     return TRUE;
615   }
616
617   return FALSE;
618 }
619
620 /* Finds channel by channel name. Channel names are unique and they
621    are not case-sensitive. */
622
623 SilcChannelEntry
624 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
625                                  SilcIDCacheEntry *ret_entry)
626 {
627   SilcIDCacheList list = NULL;
628   SilcIDCacheEntry id_cache = NULL;
629   SilcChannelEntry channel;
630
631   SILC_LOG_DEBUG(("Channel by name"));
632
633   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
634     return NULL;
635   
636   if (!silc_idcache_list_first(list, &id_cache)) {
637     silc_idcache_list_free(list);
638     return NULL;
639   }
640
641   channel = (SilcChannelEntry)id_cache->context;
642
643   if (ret_entry)
644     *ret_entry = id_cache;
645
646   silc_idcache_list_free(list);
647
648   SILC_LOG_DEBUG(("Found"));
649
650   return channel;
651 }
652
653 /* Finds channel by Channel ID. */
654
655 SilcChannelEntry
656 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
657                                SilcIDCacheEntry *ret_entry)
658 {
659   SilcIDCacheEntry id_cache = NULL;
660   SilcChannelEntry channel;
661
662   if (!id)
663     return NULL;
664
665   SILC_LOG_DEBUG(("Channel ID (%s)",
666                   silc_id_render(id, SILC_ID_CHANNEL)));
667
668   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
669                                    SILC_ID_CHANNEL, &id_cache))
670     return NULL;
671
672   channel = (SilcChannelEntry)id_cache->context;
673
674   if (ret_entry)
675     *ret_entry = id_cache;
676
677   SILC_LOG_DEBUG(("Found"));
678
679   return channel;
680 }
681
682 /* Replaces old Channel ID with new one. This is done when router forces
683    normal server to change Channel ID. */
684
685 SilcChannelEntry
686 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
687                                SilcChannelID *new_id)
688 {
689   SilcIDCacheEntry id_cache = NULL;
690   SilcChannelEntry channel;
691
692   if (!old_id || !new_id)
693     return NULL;
694
695   SILC_LOG_DEBUG(("Replacing Channel ID"));
696
697   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
698                                    SILC_ID_CHANNEL, &id_cache))
699     return NULL;
700
701   channel = (SilcChannelEntry)id_cache->context;
702   silc_free(channel->id);
703   channel->id = new_id;
704   id_cache->id = (void *)new_id;
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;
721   int i;
722
723   SILC_LOG_DEBUG(("Start"));
724
725   if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
726                                SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &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   
740   if (channels_count)
741     *channels_count = i;
742
743   return channels;
744 }