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