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