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                        char *username, char *userinfo, SilcClientID *id, 
291                        SilcServerEntry router, void *connection)
292 {
293   SilcClientEntry client;
294
295   SILC_LOG_DEBUG(("Adding new client entry"));
296
297   client = silc_calloc(1, sizeof(*client));
298   client->nickname = nickname;
299   client->username = username;
300   client->userinfo = userinfo;
301   client->id = id;
302   client->router = router;
303   client->connection = connection;
304   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
305                  client_list);
306
307   if (!silc_idcache_add(id_list->clients, nickname, 
308                         nickname ? strlen(nickname) : 0,
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
386 SilcClientEntry *
387 silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
388                                 SilcHash md5hash,
389                                 unsigned int *clients_count)
390 {
391   SilcIDCacheList list = NULL;
392   SilcIDCacheEntry id_cache = NULL;
393   SilcClientEntry *clients;
394   unsigned char hash[32];
395   int i;
396
397   SILC_LOG_DEBUG(("Start"));
398
399   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
400
401   if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
402     return NULL;
403
404   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
405
406   i = 0;
407   silc_idcache_list_first(list, &id_cache);
408   clients[i++] = (SilcClientEntry)id_cache->context;
409
410   while (silc_idcache_list_next(list, &id_cache))
411     clients[i++] = (SilcClientEntry)id_cache->context;
412   
413   silc_idcache_list_free(list);
414   
415   if (clients_count)
416     *clients_count = i;
417
418   return clients;
419 }
420
421 /* Finds client entry by nickname. */
422
423 SilcClientEntry
424 silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
425                                     char *server, SilcIDCacheEntry *ret_entry)
426 {
427   SilcIDCacheList list = NULL;
428   SilcIDCacheEntry id_cache = NULL;
429   SilcClientEntry client = NULL;
430
431   SILC_LOG_DEBUG(("Client by nickname"));
432
433   if (server) {
434     if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
435       return NULL;
436
437 #if 0
438     while (silc_idcache_list_next(list, &id_cache)) {
439       client = (SilcClientEntry)id_cache->context;
440
441       if (!strcmp(server, XXX, strlen(server)))
442         break;
443
444       client = NULL;
445     }
446 #endif
447
448    silc_idcache_list_free(list);
449
450    if (!client)
451      return NULL;
452   } else {
453     if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache))
454       return NULL;
455
456     client = (SilcClientEntry)id_cache->context;
457
458     if (ret_entry)
459       *ret_entry = id_cache;
460   }
461
462   SILC_LOG_DEBUG(("Found"));
463
464   return client;
465 }
466
467 /* Finds client by nickname hash. */
468
469 SilcClientEntry
470 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
471                                 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
472 {
473   SilcIDCacheList list = NULL;
474   SilcIDCacheEntry id_cache = NULL;
475   SilcClientEntry client = NULL;
476   unsigned char hash[32];
477
478   SILC_LOG_DEBUG(("Client by hash"));
479
480   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
481
482   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
483                                SILC_ID_CLIENT, &list))
484     return NULL;
485
486   if (!silc_idcache_list_first(list, &id_cache)) {
487     silc_idcache_list_free(list);
488     return NULL;
489   }
490
491   while (id_cache) {
492     client = (SilcClientEntry)id_cache->context;
493     
494     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
495       break;
496
497     id_cache = NULL;
498     client = NULL;
499
500     if (!silc_idcache_list_next(list, &id_cache))
501       break;
502   }
503   
504   silc_idcache_list_free(list);
505
506   if (ret_entry)
507     *ret_entry = id_cache;
508
509   SILC_LOG_DEBUG(("Found"));
510
511   return client;
512 }
513
514 /* Finds client by Client ID */
515
516 SilcClientEntry
517 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
518                               SilcIDCacheEntry *ret_entry)
519 {
520   SilcIDCacheEntry id_cache = NULL;
521   SilcClientEntry client;
522
523   if (!id)
524     return NULL;
525
526   SILC_LOG_DEBUG(("Client ID (%s)", 
527                   silc_id_render(id, SILC_ID_CLIENT)));
528
529   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
530                                    SILC_ID_CLIENT, &id_cache))
531     return NULL;
532
533   client = (SilcClientEntry)id_cache->context;
534
535   if (ret_entry)
536     *ret_entry = id_cache;
537
538   SILC_LOG_DEBUG(("Found"));
539
540   return client;
541 }
542
543 /* Replaces old Client ID with new one */
544
545 SilcClientEntry
546 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
547                               SilcClientID *new_id)
548 {
549   SilcIDCacheEntry id_cache = NULL;
550   SilcClientEntry client;
551
552   if (!old_id || !new_id)
553     return NULL;
554
555   SILC_LOG_DEBUG(("Replacing Client ID"));
556
557   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
558                                    SILC_ID_CLIENT, &id_cache))
559     return NULL;
560
561   client = (SilcClientEntry)id_cache->context;
562   silc_free(client->id);
563   client->id = new_id;
564   id_cache->id = (void *)new_id;
565
566   /* If the old ID Cache data was the hash value of the old Client ID
567      replace it with the hash of new Client ID */
568   if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
569     silc_free(id_cache->data);
570     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
571     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
572     silc_idcache_sort_by_data(id_list->clients);
573   }
574
575   SILC_LOG_DEBUG(("Replaced"));
576
577   return client;
578 }
579
580 /* Client cache entry destructor that is called when the cache is purged. */
581
582 void silc_idlist_client_destructor(SilcIDCache cache,
583                                    SilcIDCacheEntry entry)
584 {
585   SilcClientEntry client;
586
587   SILC_LOG_DEBUG(("Start"));
588
589   client = (SilcClientEntry)entry->context;
590   if (client) {
591     if (client->nickname)
592       silc_free(client->nickname);
593     if (client->username)
594       silc_free(client->username);
595     if (client->userinfo)
596       silc_free(client->userinfo);
597     if (client->id)
598       silc_free(client->id);
599
600     memset(client, 'F', sizeof(*client));
601     silc_free(client);
602   }
603 }
604
605 /******************************************************************************
606
607                           Channel entry functions
608
609 ******************************************************************************/
610
611 /* Add new channel entry. This add the new channel entry to the ID cache
612    system and returns the allocated entry or NULL on error. */
613
614 SilcChannelEntry
615 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
616                         SilcChannelID *id, SilcServerEntry router,
617                         SilcCipher channel_key, SilcHmac hmac)
618 {
619   SilcChannelEntry channel;
620
621   channel = silc_calloc(1, sizeof(*channel));
622   channel->channel_name = channel_name;
623   channel->mode = mode;
624   channel->id = id;
625   channel->router = router;
626   channel->channel_key = channel_key;
627   channel->hmac = hmac;
628   if (!channel->hmac)
629     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
630       silc_free(channel);
631       return NULL;
632     }
633
634   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
635                  channel_list);
636
637   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
638                         channel->channel_name ? strlen(channel->channel_name) :
639                         0, SILC_ID_CHANNEL, 
640                         (void *)channel->id, (void *)channel, TRUE, FALSE)) {
641     silc_free(channel);
642     return NULL;
643   }
644
645   return channel;
646 }
647
648 /* Free channel entry.  This free's everything. */
649
650 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
651 {
652   if (entry) {
653     SilcChannelClientEntry chl;
654
655     /* Remove from cache */
656     if (entry->id)
657       if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
658                                   (void *)entry->id))
659         return FALSE;
660
661     /* Free data */
662     if (entry->channel_name)
663       silc_free(entry->channel_name);
664     if (entry->id)
665       silc_free(entry->id);
666     if (entry->topic)
667       silc_free(entry->topic);
668     if (entry->channel_key)
669       silc_cipher_free(entry->channel_key);
670     if (entry->key) {
671       memset(entry->key, 0, entry->key_len / 8);
672       silc_free(entry->key);
673     }
674
675     /* Free all data, free also any reference from the client's channel
676        list since they share the same memory. */
677     silc_list_start(entry->user_list);
678     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
679       silc_list_del(chl->client->channels, chl);
680       silc_list_del(entry->user_list, chl);
681       silc_free(chl);
682     }
683
684     memset(entry, 'F', sizeof(*entry));
685     silc_free(entry);
686     return TRUE;
687   }
688
689   return FALSE;
690 }
691
692 /* Finds channel by channel name. Channel names are unique and they
693    are not case-sensitive. */
694
695 SilcChannelEntry
696 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
697                                  SilcIDCacheEntry *ret_entry)
698 {
699   SilcIDCacheList list = NULL;
700   SilcIDCacheEntry id_cache = NULL;
701   SilcChannelEntry channel;
702
703   SILC_LOG_DEBUG(("Channel by name"));
704
705   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
706     return NULL;
707   
708   if (!silc_idcache_list_first(list, &id_cache)) {
709     silc_idcache_list_free(list);
710     return NULL;
711   }
712
713   channel = (SilcChannelEntry)id_cache->context;
714
715   if (ret_entry)
716     *ret_entry = id_cache;
717
718   silc_idcache_list_free(list);
719
720   SILC_LOG_DEBUG(("Found"));
721
722   return channel;
723 }
724
725 /* Finds channel by Channel ID. */
726
727 SilcChannelEntry
728 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
729                                SilcIDCacheEntry *ret_entry)
730 {
731   SilcIDCacheEntry id_cache = NULL;
732   SilcChannelEntry channel;
733
734   if (!id)
735     return NULL;
736
737   SILC_LOG_DEBUG(("Channel ID (%s)",
738                   silc_id_render(id, SILC_ID_CHANNEL)));
739
740   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
741                                    SILC_ID_CHANNEL, &id_cache))
742     return NULL;
743
744   channel = (SilcChannelEntry)id_cache->context;
745
746   if (ret_entry)
747     *ret_entry = id_cache;
748
749   SILC_LOG_DEBUG(("Found"));
750
751   return channel;
752 }
753
754 /* Replaces old Channel ID with new one. This is done when router forces
755    normal server to change Channel ID. */
756
757 SilcChannelEntry
758 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
759                                SilcChannelID *new_id)
760 {
761   SilcIDCacheEntry id_cache = NULL;
762   SilcChannelEntry channel;
763
764   if (!old_id || !new_id)
765     return NULL;
766
767   SILC_LOG_DEBUG(("Replacing Channel ID"));
768
769   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
770                                    SILC_ID_CHANNEL, &id_cache))
771     return NULL;
772
773   channel = (SilcChannelEntry)id_cache->context;
774   silc_free(channel->id);
775   channel->id = new_id;
776   id_cache->id = (void *)new_id;
777
778   return channel;
779 }