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 /******************************************************************************
72
73                           Server entry functions
74
75 ******************************************************************************/
76
77 /* Add new server entry. This adds the new server entry to ID cache and
78    returns the allocated entry object or NULL on error. This is called
79    when new server connects to us. We also add ourselves to cache with
80    this function. */
81
82 SilcServerEntry 
83 silc_idlist_add_server(SilcIDList id_list, 
84                        char *server_name, int server_type,
85                        SilcServerID *id, SilcServerEntry router,
86                        void *connection)
87 {
88   SilcServerEntry server;
89
90   SILC_LOG_DEBUG(("Adding new server entry"));
91
92   server = silc_calloc(1, sizeof(*server));
93   server->server_name = server_name;
94   server->server_type = server_type;
95   server->id = id;
96   server->router = router;
97   server->connection = connection;
98
99   if (!silc_idcache_add(id_list->servers, server->server_name, SILC_ID_SERVER,
100                         (void *)server->id, (void *)server, TRUE, FALSE)) {
101     silc_free(server);
102     return NULL;
103   }
104
105   return server;
106 }
107
108 /* Finds server by Server ID */
109
110 SilcServerEntry
111 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
112                               SilcIDCacheEntry *ret_entry)
113 {
114   SilcIDCacheEntry id_cache = NULL;
115   SilcServerEntry server;
116
117   if (!id)
118     return NULL;
119
120   SILC_LOG_DEBUG(("Server ID (%s)",
121                   silc_id_render(id, SILC_ID_SERVER)));
122
123   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
124                                    SILC_ID_SERVER, &id_cache))
125     return NULL;
126
127   server = (SilcServerEntry)id_cache->context;
128
129   if (ret_entry)
130     *ret_entry = id_cache;
131
132   return server;
133 }
134
135 /* Find server by name */
136
137 SilcServerEntry
138 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
139                                 SilcIDCacheEntry *ret_entry)
140 {
141   SilcIDCacheEntry id_cache = NULL;
142   SilcServerEntry server;
143
144   SILC_LOG_DEBUG(("Server by name `%s'", name));
145
146   if (!silc_idcache_find_by_data_one(id_list->servers, name, &id_cache))
147     return NULL;
148
149   server = (SilcServerEntry)id_cache->context;
150   
151   if (ret_entry)
152     *ret_entry = id_cache;
153
154   return server;
155 }
156
157 /* Find server by connection parameters, hostname and port */
158
159 SilcServerEntry
160 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
161                                 int port, SilcIDCacheEntry *ret_entry)
162 {
163   SilcIDCacheList list = NULL;
164   SilcIDCacheEntry id_cache = NULL;
165   SilcServerEntry server = NULL;
166   SilcSocketConnection sock;
167  
168   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
169
170   if (!silc_idcache_find_by_id(id_list->servers, SILC_ID_CACHE_ANY, 
171                                SILC_ID_SERVER, &list))
172     return NULL;
173
174   if (!silc_idcache_list_first(list, &id_cache)) {
175     silc_idcache_list_free(list);
176     return NULL;
177   }
178
179   while (id_cache) {
180     server = (SilcServerEntry)id_cache->context;
181     sock = (SilcSocketConnection)server->connection;
182     
183     if (sock && ((sock->hostname && !strcmp(sock->hostname, hostname)) ||
184                  (sock->ip && !strcmp(sock->ip, hostname)))
185         && sock->port == port)
186       break;
187
188     id_cache = NULL;
189     server = NULL;
190
191     if (!silc_idcache_list_next(list, &id_cache))
192       break;
193   }
194   
195   silc_idcache_list_free(list);
196
197   if (ret_entry)
198     *ret_entry = id_cache;
199
200   SILC_LOG_DEBUG(("Found"));
201
202   return server;
203 }
204
205 /* Replaces old Server ID with new one */ 
206
207 SilcServerEntry
208 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
209                               SilcServerID *new_id)
210 {
211   SilcIDCacheEntry id_cache = NULL;
212   SilcServerEntry server;
213
214   if (!old_id || !new_id)
215     return NULL;
216
217   SILC_LOG_DEBUG(("Replacing Server ID"));
218
219   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
220                                    SILC_ID_SERVER, &id_cache))
221     return NULL;
222
223   server = (SilcServerEntry)id_cache->context;
224   silc_free(server->id);
225   server->id = new_id;
226   id_cache->id = (void *)new_id;
227
228   return server;
229 }
230
231 /* Removes and free's server entry from ID list */
232
233 void silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
234 {
235   if (entry) {
236     /* Remove from cache */
237     if (entry->id)
238       silc_idcache_del_by_id(id_list->servers, SILC_ID_SERVER, 
239                              (void *)entry->id);
240
241     /* Free data */
242     if (entry->server_name)
243       silc_free(entry->server_name);
244     if (entry->id)
245       silc_free(entry->id);
246
247     memset(entry, 'F', sizeof(*entry));
248     silc_free(entry);
249   }
250 }
251
252 /******************************************************************************
253
254                           Client entry functions
255
256 ******************************************************************************/
257
258 /* Add new client entry. This adds the client entry to ID cache system
259    and returns the allocated client entry or NULL on error.  This is
260    called when new client connection is accepted to the server. If The
261    `router' is provided then the all server routines assume that the client
262    is not directly connected local client but it has router set and is
263    remote.  If this is the case then `connection' must be NULL.  If, on the
264    other hand, the `connection' is provided then the client is assumed
265    to be directly connected local client and `router' must be NULL. */
266
267 SilcClientEntry
268 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname, 
269                        char *username, char *userinfo, SilcClientID *id, 
270                        SilcServerEntry router, void *connection)
271 {
272   SilcClientEntry client;
273
274   SILC_LOG_DEBUG(("Adding new client entry"));
275
276   client = silc_calloc(1, sizeof(*client));
277   client->nickname = nickname;
278   client->username = username;
279   client->userinfo = userinfo;
280   client->id = id;
281   client->router = router;
282   client->connection = connection;
283   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
284                  client_list);
285
286   if (!silc_idcache_add(id_list->clients, nickname, SILC_ID_CLIENT,
287                         (void *)client->id, (void *)client, TRUE, FALSE)) {
288     silc_free(client);
289     return NULL;
290   }
291
292   return client;
293 }
294
295 /* Free client entry. This free's everything and removes the entry
296    from ID cache. Call silc_idlist_del_data before calling this one. */
297
298 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
299 {
300   if (entry) {
301     /* Remove from cache */
302     if (entry->id)
303       if (!silc_idcache_del_by_id(id_list->clients, SILC_ID_CLIENT, 
304                                   (void *)entry->id))
305         return FALSE;
306
307     /* Free data */
308     if (entry->nickname)
309       silc_free(entry->nickname);
310     if (entry->username)
311       silc_free(entry->username);
312     if (entry->userinfo)
313       silc_free(entry->userinfo);
314     if (entry->id)
315       silc_free(entry->id);
316
317     memset(entry, 'F', sizeof(*entry));
318     silc_free(entry);
319
320     return TRUE;
321   }
322
323   return FALSE;
324 }
325
326 /* Returns all clients matching requested nickname. Number of clients is
327    returned to `clients_count'. Caller must free the returned table. */
328
329 SilcClientEntry *
330 silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
331                                     char *server, unsigned int *clients_count)
332 {
333   SilcIDCacheList list = NULL;
334   SilcIDCacheEntry id_cache = NULL;
335   SilcClientEntry *clients;
336   int i;
337
338   if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
339     return NULL;
340
341   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
342
343   i = 0;
344   silc_idcache_list_first(list, &id_cache);
345   clients[i++] = (SilcClientEntry)id_cache->context;
346
347   while (silc_idcache_list_next(list, &id_cache))
348     clients[i++] = (SilcClientEntry)id_cache->context;
349   
350   silc_idcache_list_free(list);
351   
352   if (clients_count)
353     *clients_count = i;
354
355   return clients;
356 }
357
358 /* Returns all clients matching requested nickname. Number of clients is
359    returned to `clients_count'. Caller must free the returned table. */
360
361 SilcClientEntry *
362 silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
363                                 SilcHash md5hash,
364                                 unsigned int *clients_count)
365 {
366   SilcIDCacheList list = NULL;
367   SilcIDCacheEntry id_cache = NULL;
368   SilcClientEntry *clients;
369   unsigned char hash[32];
370   int i;
371
372   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
373
374   if (!silc_idcache_find_by_data(id_list->clients, hash, &list))
375     return NULL;
376
377   clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
378
379   i = 0;
380   silc_idcache_list_first(list, &id_cache);
381   clients[i++] = (SilcClientEntry)id_cache->context;
382
383   while (silc_idcache_list_next(list, &id_cache))
384     clients[i++] = (SilcClientEntry)id_cache->context;
385   
386   silc_idcache_list_free(list);
387   
388   if (clients_count)
389     *clients_count = i;
390
391   return clients;
392 }
393
394 /* Finds client entry by nickname. */
395
396 SilcClientEntry
397 silc_idlist_find_client_by_nickname(SilcIDList id_list, char *nickname,
398                                     char *server, SilcIDCacheEntry *ret_entry)
399 {
400   SilcIDCacheList list = NULL;
401   SilcIDCacheEntry id_cache = NULL;
402   SilcClientEntry client = NULL;
403
404   SILC_LOG_DEBUG(("Client by nickname"));
405
406   if (server) {
407     if (!silc_idcache_find_by_data(id_list->clients, nickname, &list))
408       return NULL;
409
410 #if 0
411     while (silc_idcache_list_next(list, &id_cache)) {
412       client = (SilcClientEntry)id_cache->context;
413
414       if (!strcmp(server, XXX, strlen(server)))
415         break;
416
417       client = NULL;
418     }
419 #endif
420
421    silc_idcache_list_free(list);
422
423    if (!client)
424      return NULL;
425   } else {
426     if (!silc_idcache_find_by_data_one(id_list->clients, nickname, &id_cache))
427       return NULL;
428
429     client = (SilcClientEntry)id_cache->context;
430
431     if (ret_entry)
432       *ret_entry = id_cache;
433   }
434
435   SILC_LOG_DEBUG(("Found"));
436
437   return client;
438 }
439
440 /* Finds client by nickname hash. */
441
442 SilcClientEntry
443 silc_idlist_find_client_by_hash(SilcIDList id_list, char *nickname,
444                                 SilcHash md5hash, SilcIDCacheEntry *ret_entry)
445 {
446   SilcIDCacheList list = NULL;
447   SilcIDCacheEntry id_cache = NULL;
448   SilcClientEntry client = NULL;
449   unsigned char hash[32];
450
451   SILC_LOG_DEBUG(("Client by hash"));
452
453   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
454
455   if (!silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY, 
456                                SILC_ID_CLIENT, &list))
457     return NULL;
458
459   if (!silc_idcache_list_first(list, &id_cache)) {
460     silc_idcache_list_free(list);
461     return NULL;
462   }
463
464   while (id_cache) {
465     client = (SilcClientEntry)id_cache->context;
466     
467     if (client && !SILC_ID_COMPARE_HASH(client->id, hash))
468       break;
469
470     id_cache = NULL;
471     client = NULL;
472
473     if (!silc_idcache_list_next(list, &id_cache))
474       break;
475   }
476   
477   silc_idcache_list_free(list);
478
479   if (ret_entry)
480     *ret_entry = id_cache;
481
482   SILC_LOG_DEBUG(("Found"));
483
484   return client;
485 }
486
487 /* Finds client by Client ID */
488
489 SilcClientEntry
490 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
491                               SilcIDCacheEntry *ret_entry)
492 {
493   SilcIDCacheEntry id_cache = NULL;
494   SilcClientEntry client;
495
496   if (!id)
497     return NULL;
498
499   SILC_LOG_DEBUG(("Client ID (%s)", 
500                   silc_id_render(id, SILC_ID_CLIENT)));
501
502   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)id, 
503                                    SILC_ID_CLIENT, &id_cache))
504     return NULL;
505
506   client = (SilcClientEntry)id_cache->context;
507
508   if (ret_entry)
509     *ret_entry = id_cache;
510
511   SILC_LOG_DEBUG(("Found"));
512
513   return client;
514 }
515
516 /* Replaces old Client ID with new one */
517
518 SilcClientEntry
519 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
520                               SilcClientID *new_id)
521 {
522   SilcIDCacheEntry id_cache = NULL;
523   SilcClientEntry client;
524
525   if (!old_id || !new_id)
526     return NULL;
527
528   SILC_LOG_DEBUG(("Replacing Client ID"));
529
530   if (!silc_idcache_find_by_id_one(id_list->clients, (void *)old_id, 
531                                    SILC_ID_CLIENT, &id_cache))
532     return NULL;
533
534   client = (SilcClientEntry)id_cache->context;
535   silc_free(client->id);
536   client->id = new_id;
537   id_cache->id = (void *)new_id;
538
539   /* If the old ID Cache data was the hash value of the old Client ID
540      replace it with the hash of new Client ID */
541   if (id_cache->data && !SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
542     silc_free(id_cache->data);
543     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
544     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
545     silc_idcache_sort_by_data(id_list->clients);
546   }
547
548   SILC_LOG_DEBUG(("Replaced"));
549
550   return client;
551 }
552
553 /* Client cache entry destructor that is called when the cache is purged. */
554
555 void silc_idlist_client_destructor(SilcIDCache cache,
556                                    SilcIDCacheEntry entry)
557 {
558   SilcClientEntry client;
559
560   SILC_LOG_DEBUG(("Start"));
561
562   client = (SilcClientEntry)entry->context;
563   if (client) {
564     if (client->nickname)
565       silc_free(client->nickname);
566     if (client->username)
567       silc_free(client->username);
568     if (client->userinfo)
569       silc_free(client->userinfo);
570     if (client->id)
571       silc_free(client->id);
572
573     memset(client, 'F', sizeof(*client));
574     silc_free(client);
575   }
576 }
577
578 /******************************************************************************
579
580                           Channel entry functions
581
582 ******************************************************************************/
583
584 /* Add new channel entry. This add the new channel entry to the ID cache
585    system and returns the allocated entry or NULL on error. */
586
587 SilcChannelEntry
588 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
589                         SilcChannelID *id, SilcServerEntry router,
590                         SilcCipher channel_key, SilcHmac hmac)
591 {
592   SilcChannelEntry channel;
593
594   channel = silc_calloc(1, sizeof(*channel));
595   channel->channel_name = channel_name;
596   channel->mode = mode;
597   channel->id = id;
598   channel->router = router;
599   channel->channel_key = channel_key;
600   channel->hmac = hmac;
601   if (!channel->hmac)
602     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
603       silc_free(channel);
604       return NULL;
605     }
606
607   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
608                  channel_list);
609
610   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
611                         SILC_ID_CHANNEL, (void *)channel->id, 
612                         (void *)channel, TRUE, FALSE)) {
613     silc_free(channel);
614     return NULL;
615   }
616
617   return channel;
618 }
619
620 /* Free channel entry.  This free's everything. */
621
622 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
623 {
624   if (entry) {
625     SilcChannelClientEntry chl;
626
627     /* Remove from cache */
628     if (entry->id)
629       if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
630                                   (void *)entry->id))
631         return FALSE;
632
633     /* Free data */
634     if (entry->channel_name)
635       silc_free(entry->channel_name);
636     if (entry->id)
637       silc_free(entry->id);
638     if (entry->topic)
639       silc_free(entry->topic);
640     if (entry->channel_key)
641       silc_cipher_free(entry->channel_key);
642     if (entry->key) {
643       memset(entry->key, 0, entry->key_len / 8);
644       silc_free(entry->key);
645     }
646
647     /* Free all data, free also any reference from the client's channel
648        list since they share the same memory. */
649     silc_list_start(entry->user_list);
650     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
651       silc_list_del(chl->client->channels, chl);
652       silc_list_del(entry->user_list, chl);
653       silc_free(chl);
654     }
655
656     memset(entry, 'F', sizeof(*entry));
657     silc_free(entry);
658     return TRUE;
659   }
660
661   return FALSE;
662 }
663
664 /* Finds channel by channel name. Channel names are unique and they
665    are not case-sensitive. */
666
667 SilcChannelEntry
668 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
669                                  SilcIDCacheEntry *ret_entry)
670 {
671   SilcIDCacheList list = NULL;
672   SilcIDCacheEntry id_cache = NULL;
673   SilcChannelEntry channel;
674
675   SILC_LOG_DEBUG(("Channel by name"));
676
677   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
678     return NULL;
679   
680   if (!silc_idcache_list_first(list, &id_cache)) {
681     silc_idcache_list_free(list);
682     return NULL;
683   }
684
685   channel = (SilcChannelEntry)id_cache->context;
686
687   if (ret_entry)
688     *ret_entry = id_cache;
689
690   silc_idcache_list_free(list);
691
692   SILC_LOG_DEBUG(("Found"));
693
694   return channel;
695 }
696
697 /* Finds channel by Channel ID. */
698
699 SilcChannelEntry
700 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
701                                SilcIDCacheEntry *ret_entry)
702 {
703   SilcIDCacheEntry id_cache = NULL;
704   SilcChannelEntry channel;
705
706   if (!id)
707     return NULL;
708
709   SILC_LOG_DEBUG(("Channel ID (%s)",
710                   silc_id_render(id, SILC_ID_CHANNEL)));
711
712   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
713                                    SILC_ID_CHANNEL, &id_cache))
714     return NULL;
715
716   channel = (SilcChannelEntry)id_cache->context;
717
718   if (ret_entry)
719     *ret_entry = id_cache;
720
721   SILC_LOG_DEBUG(("Found"));
722
723   return channel;
724 }
725
726 /* Replaces old Channel ID with new one. This is done when router forces
727    normal server to change Channel ID. */
728
729 SilcChannelEntry
730 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
731                                SilcChannelID *new_id)
732 {
733   SilcIDCacheEntry id_cache = NULL;
734   SilcChannelEntry channel;
735
736   if (!old_id || !new_id)
737     return NULL;
738
739   SILC_LOG_DEBUG(("Replacing Channel ID"));
740
741   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
742                                    SILC_ID_CHANNEL, &id_cache))
743     return NULL;
744
745   channel = (SilcChannelEntry)id_cache->context;
746   silc_free(channel->id);
747   channel->id = new_id;
748   id_cache->id = (void *)new_id;
749
750   return channel;
751 }