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)) {
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)) {
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
552 /******************************************************************************
553
554                           Channel entry functions
555
556 ******************************************************************************/
557
558 /* Add new channel entry. This add the new channel entry to the ID cache
559    system and returns the allocated entry or NULL on error. */
560
561 SilcChannelEntry
562 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
563                         SilcChannelID *id, SilcServerEntry router,
564                         SilcCipher channel_key, char *hmac)
565 {
566   SilcChannelEntry channel;
567
568   channel = silc_calloc(1, sizeof(*channel));
569   channel->channel_name = channel_name;
570   channel->mode = mode;
571   channel->id = id;
572   channel->router = router;
573   channel->channel_key = channel_key;
574   channel->hmac = hmac ? strdup(hmac) : strdup("hmac-sha1-96");
575   silc_list_init(channel->user_list, struct SilcChannelClientEntryStruct, 
576                  channel_list);
577
578   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
579                         SILC_ID_CHANNEL, (void *)channel->id, 
580                         (void *)channel, TRUE)) {
581     silc_free(channel);
582     return NULL;
583   }
584
585   return channel;
586 }
587
588 /* Free channel entry.  This free's everything. */
589
590 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
591 {
592   if (entry) {
593     SilcChannelClientEntry chl;
594
595     /* Remove from cache */
596     if (entry->id)
597       if (!silc_idcache_del_by_id(id_list->channels, SILC_ID_CHANNEL, 
598                                   (void *)entry->id))
599         return FALSE;
600
601     /* Free data */
602     if (entry->channel_name)
603       silc_free(entry->channel_name);
604     if (entry->id)
605       silc_free(entry->id);
606     if (entry->topic)
607       silc_free(entry->topic);
608     if (entry->channel_key)
609       silc_cipher_free(entry->channel_key);
610     if (entry->key) {
611       memset(entry->key, 0, entry->key_len / 8);
612       silc_free(entry->key);
613     }
614
615     /* Free all data, free also any reference from the client's channel
616        list since they share the same memory. */
617     silc_list_start(entry->user_list);
618     while ((chl = silc_list_get(entry->user_list)) != SILC_LIST_END) {
619       silc_list_del(chl->client->channels, chl);
620       silc_list_del(entry->user_list, chl);
621       silc_free(chl);
622     }
623
624     memset(entry, 'F', sizeof(*entry));
625     silc_free(entry);
626     return TRUE;
627   }
628
629   return FALSE;
630 }
631
632 /* Finds channel by channel name. Channel names are unique and they
633    are not case-sensitive. */
634
635 SilcChannelEntry
636 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
637                                  SilcIDCacheEntry *ret_entry)
638 {
639   SilcIDCacheList list = NULL;
640   SilcIDCacheEntry id_cache = NULL;
641   SilcChannelEntry channel;
642
643   SILC_LOG_DEBUG(("Channel by name"));
644
645   if (!silc_idcache_find_by_data_loose(id_list->channels, name, &list))
646     return NULL;
647   
648   if (!silc_idcache_list_first(list, &id_cache)) {
649     silc_idcache_list_free(list);
650     return NULL;
651   }
652
653   channel = (SilcChannelEntry)id_cache->context;
654
655   if (ret_entry)
656     *ret_entry = id_cache;
657
658   silc_idcache_list_free(list);
659
660   SILC_LOG_DEBUG(("Found"));
661
662   return channel;
663 }
664
665 /* Finds channel by Channel ID. */
666
667 SilcChannelEntry
668 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
669                                SilcIDCacheEntry *ret_entry)
670 {
671   SilcIDCacheEntry id_cache = NULL;
672   SilcChannelEntry channel;
673
674   if (!id)
675     return NULL;
676
677   SILC_LOG_DEBUG(("Channel ID (%s)",
678                   silc_id_render(id, SILC_ID_CHANNEL)));
679
680   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, 
681                                    SILC_ID_CHANNEL, &id_cache))
682     return NULL;
683
684   channel = (SilcChannelEntry)id_cache->context;
685
686   if (ret_entry)
687     *ret_entry = id_cache;
688
689   SILC_LOG_DEBUG(("Found"));
690
691   return channel;
692 }
693
694 /* Replaces old Channel ID with new one. This is done when router forces
695    normal server to change Channel ID. */
696
697 SilcChannelEntry
698 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
699                                SilcChannelID *new_id)
700 {
701   SilcIDCacheEntry id_cache = NULL;
702   SilcChannelEntry channel;
703
704   if (!old_id || !new_id)
705     return NULL;
706
707   SILC_LOG_DEBUG(("Replacing Channel ID"));
708
709   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
710                                    SILC_ID_CHANNEL, &id_cache))
711     return NULL;
712
713   channel = (SilcChannelEntry)id_cache->context;
714   silc_free(channel->id);
715   channel->id = new_id;
716   id_cache->id = (void *)new_id;
717
718   return channel;
719 }