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