updates.
[silc.git] / apps / silcd / idlist.c
1 /*
2
3   idlist.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21
22 #include "serverincludes.h"
23 #include "idlist.h"
24
25 /******************************************************************************
26
27                              Common functions
28
29 ******************************************************************************/
30
31 /* This function is used to add keys and stuff to common ID entry data
32    structure. */
33
34 void silc_idlist_add_data(void *entry, SilcIDListData idata)
35 {
36   SilcIDListData data = (SilcIDListData)entry;
37   data->send_key = idata->send_key;
38   data->receive_key = idata->receive_key;
39   data->rekey = idata->rekey;
40   data->hash = idata->hash;
41   data->hmac_send = idata->hmac_send;
42   data->hmac_receive = idata->hmac_receive;
43   data->public_key = idata->public_key;
44   data->last_receive = idata->last_receive;
45   data->last_sent = idata->last_sent;
46   data->registered = idata->registered;
47 }
48
49 /* Free's all data in the common ID entry data structure. */
50
51 void silc_idlist_del_data(void *entry)
52 {
53   SilcIDListData idata = (SilcIDListData)entry;
54   if (idata->send_key)
55     silc_cipher_free(idata->send_key);
56   if (idata->receive_key)
57     silc_cipher_free(idata->receive_key);
58   if (idata->rekey) {
59     if (idata->rekey->send_enc_key) {
60       memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
61       silc_free(idata->rekey->send_enc_key);
62     }
63     silc_free(idata->rekey);
64   }
65   if (idata->hmac_send)         /* Same as idata->hmac_receive */
66     silc_hmac_free(idata->hmac_send);
67   if (idata->public_key)
68     silc_pkcs_public_key_free(idata->public_key);
69 }
70
71 /* Purges ID cache */
72
73 SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
74 {
75   SilcIDListPurge i = (SilcIDListPurge)context;
76
77   SILC_LOG_DEBUG(("Start"));
78
79   silc_idcache_purge(i->cache);
80   silc_task_register(i->timeout_queue, 0, 
81                      silc_idlist_purge,
82                      (void *)i, 600, 0,
83                      SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
84 }
85
86 /******************************************************************************
87
88                           Server entry functions
89
90 ******************************************************************************/
91
92 /* Add new server entry. This adds the new server entry to ID cache and
93    returns the allocated entry object or NULL on error. This is called
94    when new server connects to us. We also add ourselves to cache with
95    this function. */
96
97 SilcServerEntry 
98 silc_idlist_add_server(SilcIDList id_list, 
99                        char *server_name, int server_type,
100                        SilcServerID *id, SilcServerEntry router,
101                        void *connection)
102 {
103   SilcServerEntry server;
104
105   SILC_LOG_DEBUG(("Adding new server entry"));
106
107   server = silc_calloc(1, sizeof(*server));
108   server->server_name = server_name;
109   server->server_type = server_type;
110   server->id = id;
111   server->router = router;
112   server->connection = connection;
113
114   if (!silc_idcache_add(id_list->servers, server->server_name, 
115                         server->server_name ? strlen(server->server_name) : 0,
116                         (void *)server->id, (void *)server, FALSE)) {
117     silc_free(server);
118     return NULL;
119   }
120
121   return server;
122 }
123
124 /* Finds server by Server ID */
125
126 SilcServerEntry
127 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
128                               SilcIDCacheEntry *ret_entry)
129 {
130   SilcIDCacheEntry id_cache = NULL;
131   SilcServerEntry server;
132
133   if (!id)
134     return NULL;
135
136   SILC_LOG_DEBUG(("Server ID (%s)",
137                   silc_id_render(id, SILC_ID_SERVER)));
138
139   if (!silc_idcache_find_by_id(id_list->servers, (void *)id, 
140                                &id_cache))
141     return NULL;
142
143   server = (SilcServerEntry)id_cache->context;
144
145   if (ret_entry)
146     *ret_entry = id_cache;
147
148   return server;
149 }
150
151 /* Find server by name */
152
153 SilcServerEntry
154 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
155                                 SilcIDCacheEntry *ret_entry)
156 {
157   SilcIDCacheEntry id_cache = NULL;
158   SilcServerEntry server;
159
160   SILC_LOG_DEBUG(("Server by name `%s'", name));
161
162   if (!silc_idcache_find_by_data_one(id_list->servers, name, strlen(name),
163                                      &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_get_all(id_list->servers, &list))
190     return NULL;
191
192   if (!silc_idcache_list_first(list, &id_cache)) {
193     silc_idcache_list_free(list);
194     return NULL;
195   }
196
197   while (id_cache) {
198     server = (SilcServerEntry)id_cache->context;
199     sock = (SilcSocketConnection)server->connection;
200     
201     if (sock && ((sock->hostname && !strcmp(sock->hostname, hostname)) ||
202                  (sock->ip && !strcmp(sock->ip, hostname)))
203         && sock->port == port)
204       break;
205
206     id_cache = NULL;
207     server = NULL;
208
209     if (!silc_idcache_list_next(list, &id_cache))
210       break;
211   }
212   
213   silc_idcache_list_free(list);
214
215   if (ret_entry)
216     *ret_entry = id_cache;
217
218   SILC_LOG_DEBUG(("Found"));
219
220   return server;
221 }
222
223 /* Replaces old Server ID with new one */ 
224
225 SilcServerEntry
226 silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
227                               SilcServerID *new_id)
228 {
229   SilcIDCacheEntry id_cache = NULL;
230   SilcServerEntry server;
231
232   if (!old_id || !new_id)
233     return NULL;
234
235   SILC_LOG_DEBUG(("Replacing Server ID"));
236
237   if (!silc_idcache_find_by_id(id_list->servers, (void *)old_id, &id_cache))
238     return NULL;
239
240   server = (SilcServerEntry)id_cache->context;
241   silc_free(server->id);
242   server->id = new_id;
243   id_cache->id = (void *)new_id;
244
245   SILC_LOG_DEBUG(("Found"));
246
247   return server;
248 }
249
250 /* Removes and free's server entry from ID list */
251
252 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
253 {
254   SILC_LOG_DEBUG(("Start"));
255
256   if (entry) {
257     /* Remove from cache */
258     if (entry->id)
259       if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
260         return FALSE;
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     return TRUE;
271   }
272
273   return FALSE;
274 }
275
276 /******************************************************************************
277
278                           Client entry functions
279
280 ******************************************************************************/
281
282 /* Add new client entry. This adds the client entry to ID cache system
283    and returns the allocated client entry or NULL on error.  This is
284    called when new client connection is accepted to the server. If The
285    `router' is provided then the all server routines assume that the client
286    is not directly connected local client but it has router set and is
287    remote.  If this is the case then `connection' must be NULL.  If, on the
288    other hand, the `connection' is provided then the client is assumed
289    to be directly connected local client and `router' must be NULL. */
290
291 SilcClientEntry
292 silc_idlist_add_client(SilcIDList id_list, unsigned char *nickname, 
293                        uint32 nickname_len, char *username, 
294                        char *userinfo, SilcClientID *id, 
295                        SilcServerEntry router, void *connection)
296 {
297   SilcClientEntry client;
298
299   SILC_LOG_DEBUG(("Adding new client entry"));
300
301   client = silc_calloc(1, sizeof(*client));
302   client->nickname = nickname;
303   client->username = username;
304   client->userinfo = userinfo;
305   client->id = id;
306   client->router = router;
307   client->connection = connection;
308   silc_list_init(client->channels, struct SilcChannelClientEntryStruct, 
309                  client_list);
310
311   if (!silc_idcache_add(id_list->clients, nickname,  nickname_len,
312                         (void *)client->id, (void *)client, FALSE)) {
313     silc_free(client);
314     return NULL;
315   }
316
317   return client;
318 }
319
320 /* Free client entry. This free's everything and removes the entry
321    from ID cache. Call silc_idlist_del_data before calling this one. */
322
323 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
324 {
325   SILC_LOG_DEBUG(("Start"));
326
327   if (entry) {
328     /* Remove from cache */
329     if (entry->id)
330       if (!silc_idcache_del_by_id(id_list->clients, (void *)entry->id))
331         return FALSE;
332
333     /* Free data */
334     if (entry->nickname)
335       silc_free(entry->nickname);
336     if (entry->username)
337       silc_free(entry->username);
338     if (entry->userinfo)
339       silc_free(entry->userinfo);
340     if (entry->id)
341       silc_free(entry->id);
342
343     memset(entry, 'F', sizeof(*entry));
344     silc_free(entry);
345
346     return TRUE;
347   }
348
349   return FALSE;
350 }
351
352 /* Returns all clients matching requested nickname. Number of clients is
353    returned to `clients_count'. Caller must free the returned table. */
354
355 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
356                                         char *server, 
357                                         SilcClientEntry **clients,
358                                         uint32 *clients_count)
359 {
360   SilcIDCacheList list = NULL;
361   SilcIDCacheEntry id_cache = NULL;
362   int i;
363
364   SILC_LOG_DEBUG(("Start"));
365
366   if (!silc_idcache_find_by_data(id_list->clients, nickname, strlen(nickname),
367                                  &list))
368     return FALSE;
369
370   *clients = silc_realloc(*clients, 
371                           (silc_idcache_list_count(list) + *clients_count) * 
372                           sizeof(**clients));
373
374   i = 0;
375   silc_idcache_list_first(list, &id_cache);
376   (*clients)[i++] = (SilcClientEntry)id_cache->context;
377
378   while (silc_idcache_list_next(list, &id_cache))
379     (*clients)[i++] = (SilcClientEntry)id_cache->context;
380   
381   silc_idcache_list_free(list);
382   
383   *clients_count += i;
384
385   return TRUE;
386 }
387
388 /* Returns all clients matching requested nickname. Number of clients is
389    returned to `clients_count'. Caller must free the returned table. */
390 /* XXX This actually checks the data, which can be hash of the nickname
391    but is not if the client is local client. Global client on global
392    list may have hash.  Thus, this is not fully reliable function.
393    Instead this should probably check the hash from the list of client ID's. */
394
395 int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
396                                     SilcHash md5hash,
397                                     SilcClientEntry **clients,
398                                     uint32 *clients_count)
399 {
400   SilcIDCacheList list = NULL;
401   SilcIDCacheEntry id_cache = NULL;
402   unsigned char hash[32];
403   int i;
404
405   SILC_LOG_DEBUG(("Start"));
406
407   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
408
409   if (!silc_idcache_find_by_data(id_list->clients, hash, 
410                                  md5hash->hash->hash_len, &list))
411     return FALSE;
412
413   *clients = silc_realloc(*clients, 
414                           (silc_idcache_list_count(list) + *clients_count) * 
415                           sizeof(**clients));
416
417   i = 0;
418   silc_idcache_list_first(list, &id_cache);
419   (*clients)[i++] = (SilcClientEntry)id_cache->context;
420
421   while (silc_idcache_list_next(list, &id_cache))
422     (*clients)[i++] = (SilcClientEntry)id_cache->context;
423   
424   silc_idcache_list_free(list);
425   
426   *clients_count += i;
427
428   return TRUE;
429 }
430
431 /* Finds client by Client ID */
432
433 SilcClientEntry
434 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
435                               SilcIDCacheEntry *ret_entry)
436 {
437   SilcIDCacheEntry id_cache = NULL;
438   SilcClientEntry client;
439
440   if (!id)
441     return NULL;
442
443   SILC_LOG_DEBUG(("Client ID (%s)", 
444                   silc_id_render(id, SILC_ID_CLIENT)));
445
446   if (!silc_idcache_find_by_id(id_list->clients, (void *)id, &id_cache))
447     return NULL;
448
449   client = (SilcClientEntry)id_cache->context;
450
451   if (ret_entry)
452     *ret_entry = id_cache;
453
454   SILC_LOG_DEBUG(("Found"));
455
456   return client;
457 }
458
459 /* Replaces old Client ID with new one */
460
461 SilcClientEntry
462 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
463                               SilcClientID *new_id)
464 {
465   SilcIDCacheEntry id_cache = NULL;
466   SilcClientEntry client;
467
468   if (!old_id || !new_id)
469     return NULL;
470
471   SILC_LOG_DEBUG(("Replacing Client ID"));
472
473   if (!silc_idcache_find_by_id(id_list->clients, (void *)old_id, &id_cache))
474     return NULL;
475
476   client = (SilcClientEntry)id_cache->context;
477   silc_free(client->id);
478   client->id = new_id;
479   id_cache->id = (void *)new_id;
480
481   /* XXX does not work correctly with the new ID Cache */
482
483   /* If the old ID Cache data was the hash value of the old Client ID
484      replace it with the hash of new Client ID */
485   if (id_cache->data && SILC_ID_COMPARE_HASH(old_id, id_cache->data)) {
486     silc_free(id_cache->data);
487     id_cache->data = silc_calloc(sizeof(new_id->hash), sizeof(unsigned char));
488     memcpy(id_cache->data, new_id->hash, sizeof(new_id->hash));
489   }
490
491   SILC_LOG_DEBUG(("Replaced"));
492
493   return client;
494 }
495
496 /* Client cache entry destructor that is called when the cache is purged. */
497
498 void silc_idlist_client_destructor(SilcIDCache cache,
499                                    SilcIDCacheEntry entry)
500 {
501   SilcClientEntry client;
502
503   SILC_LOG_DEBUG(("Start"));
504
505   client = (SilcClientEntry)entry->context;
506   if (client) {
507     if (client->nickname)
508       silc_free(client->nickname);
509     if (client->username)
510       silc_free(client->username);
511     if (client->userinfo)
512       silc_free(client->userinfo);
513     if (client->id)
514       silc_free(client->id);
515
516     memset(client, 'F', sizeof(*client));
517     silc_free(client);
518   }
519 }
520
521 /******************************************************************************
522
523                           Channel entry functions
524
525 ******************************************************************************/
526
527 /* Add new channel entry. This add the new channel entry to the ID cache
528    system and returns the allocated entry or NULL on error. */
529
530 SilcChannelEntry
531 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
532                         SilcChannelID *id, SilcServerEntry router,
533                         SilcCipher channel_key, SilcHmac hmac)
534 {
535   SilcChannelEntry channel;
536
537   channel = silc_calloc(1, sizeof(*channel));
538   channel->channel_name = channel_name;
539   channel->mode = mode;
540   channel->id = id;
541   channel->router = router;
542   channel->channel_key = channel_key;
543   channel->hmac = hmac;
544   if (!channel->hmac)
545     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
546       silc_free(channel);
547       return NULL;
548     }
549
550   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
551                  channel_list);
552
553   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
554                         strlen(channel->channel_name),
555                         (void *)channel->id, (void *)channel, FALSE)) {
556     silc_free(channel);
557     return NULL;
558   }
559
560   return channel;
561 }
562
563 /* Free channel entry.  This free's everything. */
564
565 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
566 {
567   SILC_LOG_DEBUG(("Start"));
568
569   if (entry) {
570     SilcChannelClientEntry chl;
571
572     /* Remove from cache */
573     if (entry->id)
574       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
575         return FALSE;
576
577     /* Free data */
578     if (entry->channel_name)
579       silc_free(entry->channel_name);
580     if (entry->id)
581       silc_free(entry->id);
582     if (entry->topic)
583       silc_free(entry->topic);
584     if (entry->channel_key)
585       silc_cipher_free(entry->channel_key);
586     if (entry->key) {
587       memset(entry->key, 0, entry->key_len / 8);
588       silc_free(entry->key);
589     }
590     if (entry->cipher)
591       silc_free(entry->cipher);
592     if (entry->hmac_name)
593       silc_free(entry->hmac_name);
594     if (entry->rekey)
595       silc_free(entry->rekey);
596
597     /* Free all data, free also any reference from the client's channel
598        list since they share the same memory. */
599     silc_list_start(entry->user_list);
600     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
601       silc_list_del(chl->client->channels, chl);
602       silc_list_del(entry->user_list, chl);
603       silc_free(chl);
604     }
605
606     memset(entry, 'F', sizeof(*entry));
607     silc_free(entry);
608     return TRUE;
609   }
610
611   return FALSE;
612 }
613
614 /* Finds channel by channel name. Channel names are unique and they
615    are not case-sensitive. */
616
617 SilcChannelEntry
618 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
619                                  SilcIDCacheEntry *ret_entry)
620 {
621   SilcIDCacheList list = NULL;
622   SilcIDCacheEntry id_cache = NULL;
623   SilcChannelEntry channel;
624
625   SILC_LOG_DEBUG(("Channel by name"));
626
627   if (!silc_idcache_find_by_data(id_list->channels, name, strlen(name), &list))
628     return NULL;
629   
630   if (!silc_idcache_list_first(list, &id_cache)) {
631     silc_idcache_list_free(list);
632     return NULL;
633   }
634
635   channel = (SilcChannelEntry)id_cache->context;
636
637   if (ret_entry)
638     *ret_entry = id_cache;
639
640   silc_idcache_list_free(list);
641
642   SILC_LOG_DEBUG(("Found"));
643
644   return channel;
645 }
646
647 /* Finds channel by Channel ID. */
648
649 SilcChannelEntry
650 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
651                                SilcIDCacheEntry *ret_entry)
652 {
653   SilcIDCacheEntry id_cache = NULL;
654   SilcChannelEntry channel;
655
656   if (!id)
657     return NULL;
658
659   SILC_LOG_DEBUG(("Channel ID (%s)",
660                   silc_id_render(id, SILC_ID_CHANNEL)));
661
662   if (!silc_idcache_find_by_id(id_list->channels, (void *)id, &id_cache))
663     return NULL;
664
665   channel = (SilcChannelEntry)id_cache->context;
666
667   if (ret_entry)
668     *ret_entry = id_cache;
669
670   SILC_LOG_DEBUG(("Found"));
671
672   return channel;
673 }
674
675 /* Replaces old Channel ID with new one. This is done when router forces
676    normal server to change Channel ID. */
677
678 SilcChannelEntry
679 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
680                                SilcChannelID *new_id)
681 {
682   SilcIDCacheEntry id_cache = NULL;
683   SilcChannelEntry channel;
684
685   if (!old_id || !new_id)
686     return NULL;
687
688   SILC_LOG_DEBUG(("Replacing Channel ID"));
689
690   if (!silc_idcache_find_by_id(id_list->channels, (void *)old_id, &id_cache))
691     return NULL;
692
693   channel = (SilcChannelEntry)id_cache->context;
694   silc_free(channel->id);
695   channel->id = new_id;
696   id_cache->id = (void *)new_id;
697
698   SILC_LOG_DEBUG(("Replaced"));
699
700   return channel;
701 }
702
703 /* Returns channels from the ID list. If the `channel_id' is NULL then
704    all channels are returned. */
705
706 SilcChannelEntry *
707 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
708                          uint32 *channels_count)
709 {
710   SilcIDCacheList list = NULL;
711   SilcIDCacheEntry id_cache = NULL;
712   SilcChannelEntry *channels = NULL;
713   int i = 0;
714
715   SILC_LOG_DEBUG(("Start"));
716
717   if (!channel_id) {
718     if (!silc_idcache_get_all(id_list->channels, &list))
719       return NULL;
720
721     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
722     
723     i = 0;
724     silc_idcache_list_first(list, &id_cache);
725     channels[i++] = (SilcChannelEntry)id_cache->context;
726     
727     while (silc_idcache_list_next(list, &id_cache))
728       channels[i++] = (SilcChannelEntry)id_cache->context;
729     
730     silc_idcache_list_free(list);
731   } else {
732     if (!silc_idcache_find_by_id(id_list->channels, channel_id, &id_cache))
733       return NULL;
734
735     i = 1;
736     channels = silc_calloc(1, sizeof(*channels));
737     channels[0] = (SilcChannelEntry)id_cache->context;
738   }
739
740   if (channels_count)
741     *channels_count = i;
742
743   return channels;
744 }