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     if (entry->rekey)
634       silc_free(entry->rekey);
635
636     /* Free all data, free also any reference from the client's channel
637        list since they share the same memory. */
638     silc_list_start(entry->user_list);
639     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
640       silc_list_del(chl->client->channels, chl);
641       silc_list_del(entry->user_list, chl);
642       silc_free(chl);
643     }
644
645     memset(entry, 'F', sizeof(*entry));
646     silc_free(entry);
647     return TRUE;
648   }
649
650   return FALSE;
651 }
652
653 /* Finds channel by channel name. Channel names are unique and they
654    are not case-sensitive. */
655
656 SilcChannelEntry
657 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
658                                  SilcIDCacheEntry *ret_entry)
659 {
660   SilcIDCacheList list = NULL;
661   SilcIDCacheEntry id_cache = NULL;
662   SilcChannelEntry channel;
663
664   SILC_LOG_DEBUG(("Channel by name"));
665
666   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
667     return NULL;
668   
669   if (!silc_idcache_list_first(list, &id_cache)) {
670     silc_idcache_list_free(list);
671     return NULL;
672   }
673
674   channel = (SilcChannelEntry)id_cache->context;
675
676   if (ret_entry)
677     *ret_entry = id_cache;
678
679   silc_idcache_list_free(list);
680
681   SILC_LOG_DEBUG(("Found"));
682
683   return channel;
684 }
685
686 /* Finds channel by Channel ID. */
687
688 SilcChannelEntry
689 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
690                                SilcIDCacheEntry *ret_entry)
691 {
692   SilcIDCacheEntry id_cache = NULL;
693   SilcChannelEntry channel;
694
695   if (!id)
696     return NULL;
697
698   SILC_LOG_DEBUG(("Channel ID (%s)",
699                   silc_id_render(id, SILC_ID_CHANNEL)));
700
701   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
702                                    SILC_ID_CHANNEL, &id_cache))
703     return NULL;
704
705   channel = (SilcChannelEntry)id_cache->context;
706
707   if (ret_entry)
708     *ret_entry = id_cache;
709
710   SILC_LOG_DEBUG(("Found"));
711
712   return channel;
713 }
714
715 /* Replaces old Channel ID with new one. This is done when router forces
716    normal server to change Channel ID. */
717
718 SilcChannelEntry
719 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
720                                SilcChannelID *new_id)
721 {
722   SilcIDCacheEntry id_cache = NULL;
723   SilcChannelEntry channel;
724
725   if (!old_id || !new_id)
726     return NULL;
727
728   SILC_LOG_DEBUG(("Replacing Channel ID"));
729
730   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
731                                    SILC_ID_CHANNEL, &id_cache))
732     return NULL;
733
734   channel = (SilcChannelEntry)id_cache->context;
735   silc_free(channel->id);
736   channel->id = new_id;
737   id_cache->id = (void *)new_id;
738
739   SILC_LOG_DEBUG(("Replaced"));
740
741   return channel;
742 }
743
744 /* Returns channels from the ID list. If the `channel_id' is NULL then
745    all channels are returned. */
746
747 SilcChannelEntry *
748 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
749                          unsigned int *channels_count)
750 {
751   SilcIDCacheList list = NULL;
752   SilcIDCacheEntry id_cache = NULL;
753   SilcChannelEntry *channels;
754   int i;
755
756   SILC_LOG_DEBUG(("Start"));
757
758   if (!silc_idcache_find_by_id(id_list->channels, channel_id ? channel_id :
759                                SILC_ID_CACHE_ANY, SILC_ID_CHANNEL, &list))
760     return NULL;
761
762   channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
763
764   i = 0;
765   silc_idcache_list_first(list, &id_cache);
766   channels[i++] = (SilcChannelEntry)id_cache->context;
767
768   while (silc_idcache_list_next(list, &id_cache))
769     channels[i++] = (SilcChannelEntry)id_cache->context;
770   
771   silc_idcache_list_free(list);
772   
773   if (channels_count)
774     *channels_count = i;
775
776   return channels;
777 }