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                         (void *)server->id, (void *)server, FALSE)) {
116     silc_free(server);
117     return NULL;
118   }
119
120   return server;
121 }
122
123 /* Finds server by Server ID */
124
125 SilcServerEntry
126 silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
127                               SilcIDCacheEntry *ret_entry)
128 {
129   SilcIDCacheEntry id_cache = NULL;
130   SilcServerEntry server;
131
132   if (!id)
133     return NULL;
134
135   SILC_LOG_DEBUG(("Server ID (%s)",
136                   silc_id_render(id, SILC_ID_SERVER)));
137
138   if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
139                                    &id_cache))
140     return NULL;
141
142   server = (SilcServerEntry)id_cache->context;
143
144   if (ret_entry)
145     *ret_entry = id_cache;
146
147   return server;
148 }
149
150 /* Find server by name */
151
152 SilcServerEntry
153 silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
154                                 SilcIDCacheEntry *ret_entry)
155 {
156   SilcIDCacheEntry id_cache = NULL;
157   SilcServerEntry server;
158
159   SILC_LOG_DEBUG(("Server by name `%s'", name));
160
161   if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
162     return NULL;
163
164   server = (SilcServerEntry)id_cache->context;
165   
166   if (ret_entry)
167     *ret_entry = id_cache;
168
169   SILC_LOG_DEBUG(("Found"));
170
171   return server;
172 }
173
174 /* Find server by connection parameters, hostname and port */
175
176 SilcServerEntry
177 silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
178                                 int port, SilcIDCacheEntry *ret_entry)
179 {
180   SilcIDCacheList list = NULL;
181   SilcIDCacheEntry id_cache = NULL;
182   SilcServerEntry server = NULL;
183   SilcSocketConnection sock;
184  
185   SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
186
187   if (!silc_idcache_get_all(id_list->servers, &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 && !strcasecmp(sock->hostname, hostname)) ||
200                  (sock->ip && !strcasecmp(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                                    &id_cache))
237     return NULL;
238
239   server = (SilcServerEntry)id_cache->context;
240   silc_free(server->id);
241   server->id = new_id;
242
243   /* Remove the old entry and add a new one */
244   silc_idcache_del_by_context(id_list->servers, server);
245   silc_idcache_add(id_list->servers, server->server_name, server->id, 
246                    server, FALSE);
247
248   SILC_LOG_DEBUG(("Found"));
249
250   return server;
251 }
252
253 /* Removes and free's server entry from ID list */
254
255 int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
256 {
257   SILC_LOG_DEBUG(("Start"));
258
259   if (entry) {
260     /* Remove from cache */
261     if (entry->id)
262       if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
263         return FALSE;
264
265     /* Free data */
266     if (entry->server_name)
267       silc_free(entry->server_name);
268     if (entry->id)
269       silc_free(entry->id);
270
271     memset(entry, 'F', sizeof(*entry));
272     silc_free(entry);
273     return TRUE;
274   }
275
276   return FALSE;
277 }
278
279 /******************************************************************************
280
281                           Client entry functions
282
283 ******************************************************************************/
284
285 /* Add new client entry. This adds the client entry to ID cache system
286    and returns the allocated client entry or NULL on error.  This is
287    called when new client connection is accepted to the server. If The
288    `router' is provided then the all server routines assume that the client
289    is not directly connected local client but it has router set and is
290    remote.  If this is the case then `connection' must be NULL.  If, on the
291    other hand, the `connection' is provided then the client is assumed
292    to be directly connected local client and `router' must be NULL. */
293
294 SilcClientEntry
295 silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
296                        char *userinfo, SilcClientID *id, 
297                        SilcServerEntry router, void *connection)
298 {
299   SilcClientEntry client;
300
301   SILC_LOG_DEBUG(("Adding new client entry"));
302
303   client = silc_calloc(1, sizeof(*client));
304   client->nickname = nickname;
305   client->username = username;
306   client->userinfo = userinfo;
307   client->id = id;
308   client->router = router;
309   client->connection = connection;
310   client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
311                                            NULL, NULL, NULL, NULL, TRUE);
312
313   if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
314                         (void *)client, FALSE)) {
315     silc_hash_table_free(client->channels);
316     silc_free(client);
317     return NULL;
318   }
319
320   return client;
321 }
322
323 /* Free client entry. This free's everything and removes the entry
324    from ID cache. Call silc_idlist_del_data before calling this one. */
325
326 int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
327 {
328   SILC_LOG_DEBUG(("Start"));
329
330   if (entry) {
331     /* Remove from cache */
332     if (entry->id)
333       if (!silc_idcache_del_by_context(id_list->clients, entry))
334         return FALSE;
335
336     /* Free data */
337     if (entry->nickname)
338       silc_free(entry->nickname);
339     if (entry->username)
340       silc_free(entry->username);
341     if (entry->userinfo)
342       silc_free(entry->userinfo);
343     if (entry->id)
344       silc_free(entry->id);
345
346     memset(entry, 'F', sizeof(*entry));
347     silc_free(entry);
348
349     return TRUE;
350   }
351
352   return FALSE;
353 }
354
355 /* Returns all clients matching requested nickname. Number of clients is
356    returned to `clients_count'. Caller must free the returned table. */
357
358 int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
359                                         char *server, 
360                                         SilcClientEntry **clients,
361                                         uint32 *clients_count)
362 {
363   SilcIDCacheList list = NULL;
364   SilcIDCacheEntry id_cache = NULL;
365   int i;
366
367   SILC_LOG_DEBUG(("Start"));
368
369   if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
370     return FALSE;
371
372   *clients = silc_realloc(*clients, 
373                           (silc_idcache_list_count(list) + *clients_count) * 
374                           sizeof(**clients));
375
376   i = 0;
377   silc_idcache_list_first(list, &id_cache);
378   (*clients)[i++] = (SilcClientEntry)id_cache->context;
379
380   while (silc_idcache_list_next(list, &id_cache))
381     (*clients)[i++] = (SilcClientEntry)id_cache->context;
382   
383   silc_idcache_list_free(list);
384   
385   *clients_count += i;
386
387   SILC_LOG_DEBUG(("Found %d clients", *clients_count));
388
389   return TRUE;
390 }
391
392 /* Returns all clients matching requested nickname hash. Number of clients
393    is returned to `clients_count'. Caller must free the returned table. */
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   SilcClientID client_id;
405
406   SILC_LOG_DEBUG(("Start"));
407
408   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
409
410   /* As the Client ID is hashed in the ID cache by hashing only the hash
411      from the Client ID, we can do a lookup with only the hash not the
412      other parts of the ID and get all the clients with that hash, ie.
413      with that nickname, as the hash is from the nickname. */
414   memset(&client_id, 0, sizeof(client_id));
415   memcpy(&client_id.hash, hash, sizeof(client_id.hash));
416   if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
417     return FALSE;
418
419   *clients = silc_realloc(*clients, 
420                           (silc_idcache_list_count(list) + *clients_count) * 
421                           sizeof(**clients));
422
423   i = 0;
424   silc_idcache_list_first(list, &id_cache);
425   (*clients)[i++] = (SilcClientEntry)id_cache->context;
426
427   while (silc_idcache_list_next(list, &id_cache))
428     (*clients)[i++] = (SilcClientEntry)id_cache->context;
429   
430   silc_idcache_list_free(list);
431   
432   *clients_count += i;
433
434   SILC_LOG_DEBUG(("Found %d clients", *clients_count));
435
436   return TRUE;
437 }
438
439 /* Finds client by Client ID */
440
441 SilcClientEntry
442 silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
443                               SilcIDCacheEntry *ret_entry)
444 {
445   SilcIDCacheEntry id_cache = NULL;
446   SilcClientEntry client;
447
448   if (!id)
449     return NULL;
450
451   SILC_LOG_DEBUG(("Client ID (%s)", 
452                   silc_id_render(id, SILC_ID_CLIENT)));
453
454   /* Do extended search since the normal ID comparison function for
455      Client ID's compares only the hash from the Client ID and not the
456      entire ID. The silc_hash_client_id_compare compares the entire
457      Client ID as we want to find one specific Client ID. */
458   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
459                                        NULL, NULL, 
460                                        silc_hash_client_id_compare, NULL,
461                                        &id_cache))
462     return NULL;
463
464   client = (SilcClientEntry)id_cache->context;
465
466   if (ret_entry)
467     *ret_entry = id_cache;
468
469   SILC_LOG_DEBUG(("Found"));
470
471   return client;
472 }
473
474 /* Replaces old Client ID with new one */
475
476 SilcClientEntry
477 silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
478                               SilcClientID *new_id)
479 {
480   SilcIDCacheEntry id_cache = NULL;
481   SilcClientEntry client;
482
483   if (!old_id || !new_id)
484     return NULL;
485
486   SILC_LOG_DEBUG(("Replacing Client ID"));
487
488   /* Do extended search since the normal ID comparison function for
489      Client ID's compares only the hash from the Client ID and not the
490      entire ID. The silc_hash_client_id_compare compares the entire
491      Client ID as we want to find one specific Client ID. */
492   if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
493                                        NULL, NULL, 
494                                        silc_hash_client_id_compare, NULL,
495                                        &id_cache))
496     return NULL;
497
498   client = (SilcClientEntry)id_cache->context;
499   silc_free(client->id);
500   client->id = new_id;
501
502   /* Remove the old entry and add a new one */
503   silc_idcache_del_by_context(id_list->clients, client);
504   silc_idcache_add(id_list->clients, client->nickname, client->id, 
505                    client, FALSE);
506
507   SILC_LOG_DEBUG(("Replaced"));
508
509   return client;
510 }
511
512 /* Client cache entry destructor that is called when the cache is purged. */
513
514 void silc_idlist_client_destructor(SilcIDCache cache,
515                                    SilcIDCacheEntry entry)
516 {
517   SilcClientEntry client;
518
519   SILC_LOG_DEBUG(("Start"));
520
521   client = (SilcClientEntry)entry->context;
522   if (client) {
523     if (client->nickname)
524       silc_free(client->nickname);
525     if (client->username)
526       silc_free(client->username);
527     if (client->userinfo)
528       silc_free(client->userinfo);
529     if (client->id)
530       silc_free(client->id);
531
532     memset(client, 'F', sizeof(*client));
533     silc_free(client);
534   }
535 }
536
537 /******************************************************************************
538
539                           Channel entry functions
540
541 ******************************************************************************/
542
543 /* Add new channel entry. This add the new channel entry to the ID cache
544    system and returns the allocated entry or NULL on error. */
545
546 SilcChannelEntry
547 silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
548                         SilcChannelID *id, SilcServerEntry router,
549                         SilcCipher channel_key, SilcHmac hmac)
550 {
551   SilcChannelEntry channel;
552
553   channel = silc_calloc(1, sizeof(*channel));
554   channel->channel_name = channel_name;
555   channel->mode = mode;
556   channel->id = id;
557   channel->router = router;
558   channel->channel_key = channel_key;
559   channel->hmac = hmac;
560   if (!channel->hmac)
561     if (!silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac)) {
562       silc_free(channel);
563       return NULL;
564     }
565
566   channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
567                                              NULL, NULL, NULL, TRUE);
568
569   if (!silc_idcache_add(id_list->channels, channel->channel_name, 
570                         (void *)channel->id, (void *)channel, FALSE)) {
571     silc_hmac_free(channel->hmac);
572     silc_hash_table_free(channel->user_list);
573     silc_free(channel);
574     return NULL;
575   }
576
577   return channel;
578 }
579
580 /* Foreach callbcak to free all users from the channel when deleting a
581    channel entry. */
582
583 static void silc_idlist_del_channel_foreach(void *key, void *context,
584                                             void *user_context)
585 {
586   SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
587
588   /* Remove the context from the client's channel hash table as that
589      table and channel's user_list hash table share this same context. */
590   silc_hash_table_del_by_context(chl->client->channels, chl->channel, chl);
591   silc_free(chl);
592 }
593
594 /* Free channel entry.  This free's everything. */
595
596 int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
597 {
598   SILC_LOG_DEBUG(("Start"));
599
600   if (entry) {
601     /* Remove from cache */
602     if (entry->id)
603       if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
604         return FALSE;
605
606     /* Free data */
607     if (entry->channel_name)
608       silc_free(entry->channel_name);
609     if (entry->id)
610       silc_free(entry->id);
611     if (entry->topic)
612       silc_free(entry->topic);
613     if (entry->channel_key)
614       silc_cipher_free(entry->channel_key);
615     if (entry->key) {
616       memset(entry->key, 0, entry->key_len / 8);
617       silc_free(entry->key);
618     }
619     if (entry->cipher)
620       silc_free(entry->cipher);
621     if (entry->hmac_name)
622       silc_free(entry->hmac_name);
623     if (entry->rekey)
624       silc_free(entry->rekey);
625
626     /* Free all client entrys from the users list. The silc_hash_table_free
627        will free all the entries so they are not freed at the foreach 
628        callback. */
629     silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
630                             NULL);
631     silc_hash_table_free(entry->user_list);
632
633     memset(entry, 'F', sizeof(*entry));
634     silc_free(entry);
635     return TRUE;
636   }
637
638   return FALSE;
639 }
640
641 /* Finds channel by channel name. Channel names are unique and they
642    are not case-sensitive. */
643
644 SilcChannelEntry
645 silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
646                                  SilcIDCacheEntry *ret_entry)
647 {
648   SilcIDCacheEntry id_cache = NULL;
649
650   SILC_LOG_DEBUG(("Channel by name"));
651
652   if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
653     return NULL;
654
655   if (ret_entry)
656     *ret_entry = id_cache;
657
658   SILC_LOG_DEBUG(("Found"));
659
660   return id_cache->context;
661 }
662
663 /* Finds channel by Channel ID. */
664
665 SilcChannelEntry
666 silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
667                                SilcIDCacheEntry *ret_entry)
668 {
669   SilcIDCacheEntry id_cache = NULL;
670   SilcChannelEntry channel;
671
672   if (!id)
673     return NULL;
674
675   SILC_LOG_DEBUG(("Channel ID (%s)",
676                   silc_id_render(id, SILC_ID_CHANNEL)));
677
678   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
679     return NULL;
680
681   channel = (SilcChannelEntry)id_cache->context;
682
683   if (ret_entry)
684     *ret_entry = id_cache;
685
686   SILC_LOG_DEBUG(("Found"));
687
688   return channel;
689 }
690
691 /* Replaces old Channel ID with new one. This is done when router forces
692    normal server to change Channel ID. */
693
694 SilcChannelEntry
695 silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
696                                SilcChannelID *new_id)
697 {
698   SilcIDCacheEntry id_cache = NULL;
699   SilcChannelEntry channel;
700
701   if (!old_id || !new_id)
702     return NULL;
703
704   SILC_LOG_DEBUG(("Replacing Channel ID"));
705
706   if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
707                                    &id_cache))
708     return NULL;
709
710   channel = (SilcChannelEntry)id_cache->context;
711   silc_free(channel->id);
712   channel->id = new_id;
713
714   /* Remove the old entry and add a new one */
715   silc_idcache_del_by_context(id_list->channels, channel);
716   silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
717                    channel, FALSE);
718
719   SILC_LOG_DEBUG(("Replaced"));
720
721   return channel;
722 }
723
724 /* Returns channels from the ID list. If the `channel_id' is NULL then
725    all channels are returned. */
726
727 SilcChannelEntry *
728 silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
729                          uint32 *channels_count)
730 {
731   SilcIDCacheList list = NULL;
732   SilcIDCacheEntry id_cache = NULL;
733   SilcChannelEntry *channels = NULL;
734   int i = 0;
735
736   SILC_LOG_DEBUG(("Start"));
737
738   if (!channel_id) {
739     if (!silc_idcache_get_all(id_list->channels, &list))
740       return NULL;
741
742     channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
743     
744     i = 0;
745     silc_idcache_list_first(list, &id_cache);
746     channels[i++] = (SilcChannelEntry)id_cache->context;
747     
748     while (silc_idcache_list_next(list, &id_cache))
749       channels[i++] = (SilcChannelEntry)id_cache->context;
750     
751     silc_idcache_list_free(list);
752   } else {
753     if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
754       return NULL;
755
756     i = 1;
757     channels = silc_calloc(1, sizeof(*channels));
758     channels[0] = (SilcChannelEntry)id_cache->context;
759   }
760
761   if (channels_count)
762     *channels_count = i;
763
764   return channels;
765 }