Added preliminary Symbian support.
[silc.git] / lib / silcskr / silcskr.c
1 /*
2
3   silcskr.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2006 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21 #include "silcskr.h"
22
23 /* XXX Locking, when removing keys */
24
25 /************************** Types and definitions ***************************/
26
27 /* Search constraints */
28 typedef enum {
29   SILC_SKR_FIND_PKCS_TYPE,
30   SILC_SKR_FIND_USERNAME,
31   SILC_SKR_FIND_HOST,
32   SILC_SKR_FIND_REALNAME,
33   SILC_SKR_FIND_EMAIL,
34   SILC_SKR_FIND_ORG,
35   SILC_SKR_FIND_COUNTRY,
36   SILC_SKR_FIND_PUBLIC_KEY,
37   SILC_SKR_FIND_CONTEXT,
38   SILC_SKR_FIND_USAGE,          /* Never added as key specific */
39 } SilcSKRFindType;
40
41 /* Hash table key context */
42 typedef struct {
43   SilcSKRFindType type;         /* Type of key */
44   void *data;                   /* Hash table key */
45 } *SilcSKREntry, SilcSKREntryStruct;
46
47 /* Foreach user context when finding entries from hash table */
48 typedef struct {
49   SilcDList list;
50   void *key_context;
51   SilcSKRKeyUsage usage;
52 } SilcSKRFindForeach;
53
54 #if defined(SILC_DEBUG)
55 static const char *find_name[] = {
56   "PKCS TYPE",
57   "USERNAME",
58   "HOST",
59   "REALNAME",
60   "EMAIL",
61   "ORG",
62   "COUNTRY",
63   "PUBLIC KEY",
64   "CONTEXT",
65   "USAGE",
66   NULL
67 };
68 #endif /* SILC_DEBUG */
69
70 /************************ Static utility functions **************************/
71
72 #if defined(SILC_DEBUG)
73
74 /* Returns search constraint string */
75
76 static void silc_skr_type_string(SilcSKRFindType type, void *data,
77                                  char *retbuf, SilcUInt32 retbuf_size)
78 {
79   switch (type) {
80   case SILC_SKR_FIND_PKCS_TYPE:
81   case SILC_SKR_FIND_USAGE:
82     silc_snprintf(retbuf, retbuf_size, "[%s] [%d]", find_name[type],
83              (int)SILC_PTR_TO_32(data));
84     break;
85
86   case SILC_SKR_FIND_PUBLIC_KEY:
87     silc_snprintf(retbuf, retbuf_size, "[%s] [%p]", find_name[type], data);
88     break;
89
90   default:
91     silc_snprintf(retbuf, retbuf_size, "[%s] [%s]", find_name[type],
92              (char *)data);
93   }
94 }
95
96 #endif /* SILC_DEBUG */
97
98 /* Hash table destructor for search constraints */
99
100 static void silc_skr_find_destructor(void *key, void *context,
101                                      void *user_context)
102 {
103   SilcSKRFindType type = SILC_PTR_TO_32(key);
104
105   switch (type) {
106   case SILC_SKR_FIND_PKCS_TYPE:
107   case SILC_SKR_FIND_USAGE:
108   case SILC_SKR_FIND_CONTEXT:
109     break;
110
111   case SILC_SKR_FIND_PUBLIC_KEY:
112     silc_pkcs_public_key_free(context);
113     break;
114
115   default:
116     silc_free(context);
117   }
118 }
119
120 /* Hash table destructor for key entries */
121
122 static void silc_skr_destructor(void *key, void *context, void *user_context)
123 {
124   SilcSKREntry type = key;
125   SilcSKRKeyInternal entry = context;
126
127   /* Destroy search data, except for SILC_SKR_FIND_PUBLIC_KEY because it
128      shares same context with the key entry. */
129   if (SILC_PTR_TO_32(type->type) != SILC_SKR_FIND_PUBLIC_KEY)
130     silc_skr_find_destructor(SILC_32_TO_PTR(type->type), type->data, NULL);
131   silc_free(type);
132
133   /* Destroy key */
134   entry->refcnt--;
135   if (entry->refcnt > 0)
136     return;
137
138   silc_pkcs_public_key_free(entry->key.key);
139   silc_free(entry);
140 }
141
142 /* Hash table hash function for key entries */
143
144 static SilcUInt32 silc_skr_hash(void *key, void *user_context)
145 {
146   SilcSKREntry type = key;
147
148   switch (type->type) {
149   case SILC_SKR_FIND_PKCS_TYPE:
150   case SILC_SKR_FIND_CONTEXT:
151     return type->type + (type->type ^ SILC_PTR_TO_32(type->data));
152     break;
153
154   case SILC_SKR_FIND_PUBLIC_KEY:
155     return type->type + silc_hash_public_key(type->data, user_context);
156     break;
157
158   default:
159     break;
160   }
161
162   return type->type + silc_hash_string(type->data, user_context);
163 }
164
165 /* Hash table comparison function for key entries */
166
167 static SilcBool silc_skr_compare(void *key1, void *key2, void *user_context)
168 {
169   SilcSKREntry type1 = key1;
170   SilcSKREntry type2 = key2;
171
172   if (type1->type != type2->type)
173     return FALSE;
174
175   switch (type1->type) {
176   case SILC_SKR_FIND_PKCS_TYPE:
177   case SILC_SKR_FIND_CONTEXT:
178     return type1->data == type2->data;
179     break;
180
181   case SILC_SKR_FIND_PUBLIC_KEY:
182     return silc_hash_public_key_compare(type1->data, type2->data,
183                                         user_context);
184     break;
185
186   default:
187     break;
188   }
189
190   return silc_utf8_strcasecmp((const char *)type1->data,
191                               (const char *)type2->data);
192 }
193
194 /* Foreach function for finding entries in the repository */
195
196 static void silc_skr_find_foreach(void *key, void *context,
197                                   void *user_context)
198 {
199   SilcSKRFindForeach *f = user_context;
200   SilcSKRKeyInternal k = context;
201
202   if (k) {
203     /* If key context is present, it must match the context in the key.
204        This is used only internally when adding keys, to check if the key
205        is added with same context. */
206     if (f->key_context && f->key_context != k->key.key_context)
207       return;
208
209     /* Check for usage bits.  At least one usage bit must be set. */
210     if (f->usage && k->key.usage && (f->usage & k->key.usage) == 0)
211       return;
212
213     silc_dlist_add(f->list, k);
214   }
215 }
216
217 /* Finds entry from repository by search constraint type and data */
218
219 static SilcBool silc_skr_find_entry(SilcSKR skr,
220                                     SilcSKRStatus *status,
221                                     SilcSKRFindType type,
222                                     void *type_data,
223                                     SilcDList *results,
224                                     void *key_context,
225                                     SilcSKRKeyUsage usage)
226 {
227   SilcSKREntryStruct find;
228   SilcSKRFindForeach f;
229
230   f.list = silc_dlist_init();
231   if (!f.list) {
232     *status |= SILC_SKR_NO_MEMORY;
233     return FALSE;
234   }
235   f.key_context = key_context;
236   f.usage = usage;
237
238   find.type = type;
239   find.data = type_data;
240
241   silc_hash_table_find_foreach(skr->keys, (void *)&find,
242                                silc_skr_find_foreach, &f);
243
244   if (!silc_dlist_count(f.list)) {
245     *status |= SILC_SKR_NOT_FOUND;
246     silc_dlist_uninit(f.list);
247     return FALSE;
248   }
249
250   if (results)
251     *results = f.list;
252   else
253     silc_dlist_uninit(f.list);
254
255   return TRUE;
256 }
257
258 /* Add a key by search constraint type to repository */
259
260 static SilcBool silc_skr_add_entry(SilcSKR skr, SilcSKRFindType type,
261                                    void *type_data, SilcSKRKeyInternal key)
262 {
263   SilcSKREntry entry;
264
265   entry = silc_calloc(1, sizeof(*entry));
266   if (!entry)
267     return FALSE;
268
269   entry->type = type;
270   entry->data = type_data;
271
272   return silc_hash_table_add(skr->keys, entry, key);
273 }
274
275 /* Add SILC style public key to repository */
276
277 static SilcSKRStatus silc_skr_add_silc(SilcSKR skr,
278                                        SilcPublicKey public_key,
279                                        SilcSKRKeyUsage usage,
280                                        void *key_context)
281 {
282   SilcSKRKeyInternal key;
283   SilcSKRStatus status = SILC_SKR_ERROR;
284   SilcPublicKeyIdentifier ident;
285   SilcSILCPublicKey silc_pubkey;
286
287   /* Get the SILC public key */
288   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
289   ident = &silc_pubkey->identifier;
290
291   SILC_LOG_DEBUG(("Adding SILC public key [%s]", ident->username));
292
293   silc_mutex_lock(skr->lock);
294
295   /* Check that this key hasn't been added already */
296   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
297                           public_key, NULL, key_context, 0)) {
298     silc_mutex_unlock(skr->lock);
299     SILC_LOG_DEBUG(("Key already added"));
300     return status | SILC_SKR_ALREADY_EXIST;
301   }
302
303   /* Allocate key entry */
304   key = silc_calloc(1, sizeof(*key));
305   if (!key) {
306     silc_mutex_unlock(skr->lock);
307     return status | SILC_SKR_NO_MEMORY;
308   }
309
310   key->key.usage = usage;
311   key->key.key = public_key;
312   key->key.key_context = key_context;
313
314   /* Add key specifics */
315
316   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
317                           public_key, key))
318     goto err;
319   key->refcnt++;
320
321   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PKCS_TYPE,
322                           SILC_32_TO_PTR(SILC_PKCS_SILC), key))
323     goto err;
324   key->refcnt++;
325
326   if (ident->username) {
327     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_USERNAME,
328                             ident->username, key))
329       goto err;
330     key->refcnt++;
331   }
332
333   if (ident->host) {
334     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_HOST,
335                             ident->host, key))
336       goto err;
337     key->refcnt++;
338   }
339
340   if (ident->realname) {
341     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_REALNAME,
342                             ident->realname, key))
343       goto err;
344     key->refcnt++;
345   }
346
347   if (ident->email) {
348     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_EMAIL,
349                             ident->email, key))
350       goto err;
351     key->refcnt++;
352   }
353
354   if (ident->org) {
355     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_ORG,
356                             ident->org, key))
357       goto err;
358     key->refcnt++;
359   }
360
361   if (ident->country) {
362     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_COUNTRY,
363                             ident->country, key))
364       goto err;
365     key->refcnt++;
366   }
367
368   if (key_context) {
369     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
370                             key_context, key))
371       goto err;
372     key->refcnt++;
373   }
374
375   silc_mutex_unlock(skr->lock);
376
377   return SILC_SKR_OK;
378
379  err:
380   silc_mutex_unlock(skr->lock);
381   return status;
382 }
383
384 /* Add SILC style public key to repository, and only the public key, not
385    other details from the key. */
386
387 static SilcSKRStatus silc_skr_add_silc_simple(SilcSKR skr,
388                                               SilcPublicKey public_key,
389                                               SilcSKRKeyUsage usage,
390                                               void *key_context)
391 {
392   SilcSKRKeyInternal key;
393   SilcSKRStatus status = SILC_SKR_ERROR;
394
395   SILC_LOG_DEBUG(("Adding SILC public key"));
396
397   silc_mutex_lock(skr->lock);
398
399   /* Check that this key hasn't been added already */
400   if (silc_skr_find_entry(skr, &status, SILC_SKR_FIND_PUBLIC_KEY,
401                           public_key, NULL, key_context, 0)) {
402     silc_mutex_unlock(skr->lock);
403     SILC_LOG_DEBUG(("Key already added"));
404     return status | SILC_SKR_ALREADY_EXIST;
405   }
406
407   /* Allocate key entry */
408   key = silc_calloc(1, sizeof(*key));
409   if (!key) {
410     silc_mutex_unlock(skr->lock);
411     return status | SILC_SKR_NO_MEMORY;
412   }
413
414   key->key.usage = usage;
415   key->key.key = public_key;
416   key->key.key_context = key_context;
417
418   /* Add key specifics */
419
420   if (!silc_skr_add_entry(skr, SILC_SKR_FIND_PUBLIC_KEY,
421                           public_key, key))
422     goto err;
423   key->refcnt++;
424
425   if (key_context) {
426     if (!silc_skr_add_entry(skr, SILC_SKR_FIND_CONTEXT,
427                             key_context, key))
428       goto err;
429     key->refcnt++;
430   }
431
432   silc_mutex_unlock(skr->lock);
433
434   return SILC_SKR_OK;
435
436  err:
437   silc_mutex_unlock(skr->lock);
438   return status;
439 }
440
441 /* This performs AND operation.  Any entry already in `results' that is not
442    in `list' will be removed from `results'. */
443
444 static SilcBool silc_skr_results_and(SilcDList list, SilcSKRStatus *status,
445                                      SilcDList *results)
446 {
447   SilcSKRKeyInternal entry, r;
448
449   if (*results == NULL) {
450     *results = silc_dlist_init();
451     if (*results == NULL) {
452       *status |= SILC_SKR_NO_MEMORY;
453       return FALSE;
454     }
455   }
456
457   /* If results is empty, just add all entries from list to results */
458   if (!silc_dlist_count(*results)) {
459     silc_dlist_start(list);
460     while ((entry = silc_dlist_get(list)) != SILC_LIST_END)
461       silc_dlist_add(*results, entry);
462
463     return TRUE;
464   }
465
466   silc_dlist_start(*results);
467   while ((entry = silc_dlist_get(*results)) != SILC_LIST_END) {
468
469     /* Check if this entry is in list  */
470     silc_dlist_start(list);
471     while ((r = silc_dlist_get(list)) != SILC_LIST_END) {
472       if (r == entry)
473         break;
474     }
475     if (r != SILC_LIST_END)
476       continue;
477
478     /* Remove from results */
479     silc_dlist_del(*results, entry);
480   }
481
482   /* If results became empty, we did not find any key */
483   if (!silc_dlist_count(*results)) {
484     SILC_LOG_DEBUG(("Not all search constraints found"));
485     *status |= SILC_SKR_NOT_FOUND;
486     return FALSE;
487   }
488
489   return TRUE;
490 }
491
492
493 /**************************** Key Repository API ****************************/
494
495 /* Allocate key repository */
496
497 SilcSKR silc_skr_alloc(SilcSchedule scheduler)
498 {
499   SilcSKR skr;
500
501   skr = silc_calloc(1, sizeof(*skr));
502   if (!skr)
503     return NULL;
504
505   if (!silc_skr_init(skr, scheduler)) {
506     silc_skr_free(skr);
507     return NULL;
508   }
509
510   return skr;
511 }
512
513 /* Free key repository */
514
515 void silc_skr_free(SilcSKR skr)
516 {
517   silc_skr_uninit(skr);
518   silc_free(skr);
519 }
520
521 /* Initializes key repository */
522
523 SilcBool silc_skr_init(SilcSKR skr, SilcSchedule scheduler)
524 {
525   if (!scheduler)
526     return FALSE;
527
528   skr->scheduler = scheduler;
529
530   if (!silc_mutex_alloc(&skr->lock))
531     return FALSE;
532
533   skr->keys = silc_hash_table_alloc(0, silc_skr_hash, NULL,
534                                     silc_skr_compare, NULL,
535                                     silc_skr_destructor, NULL, TRUE);
536   if (!skr->keys)
537     return FALSE;
538
539   return TRUE;
540 }
541
542 /* Uninitializes key repository */
543
544 void silc_skr_uninit(SilcSKR skr)
545 {
546   if (skr->keys)
547     silc_hash_table_free(skr->keys);
548   silc_mutex_free(skr->lock);
549 }
550
551 /* Adds public key to key repository */
552
553 SilcSKRStatus silc_skr_add_public_key(SilcSKR skr,
554                                       SilcPublicKey public_key,
555                                       SilcSKRKeyUsage usage,
556                                       void *key_context)
557 {
558   SilcPKCSType type;
559
560   if (!public_key)
561     return SILC_SKR_ERROR;
562
563   type = silc_pkcs_get_type(public_key);
564
565   SILC_LOG_DEBUG(("Adding public key to repository"));
566
567   switch (type) {
568
569   case SILC_PKCS_SILC:
570     return silc_skr_add_silc(skr, public_key, usage, key_context);
571     break;
572
573   default:
574     break;
575   }
576
577   return SILC_SKR_ERROR;
578 }
579
580 /* Adds public key to repository. */
581
582 SilcSKRStatus silc_skr_add_public_key_simple(SilcSKR skr,
583                                              SilcPublicKey public_key,
584                                              SilcSKRKeyUsage usage,
585                                              void *key_context)
586 {
587   SilcPKCSType type;
588
589   if (!public_key)
590     return SILC_SKR_ERROR;
591
592   type = silc_pkcs_get_type(public_key);
593
594   SILC_LOG_DEBUG(("Adding public key to repository"));
595
596   switch (type) {
597
598   case SILC_PKCS_SILC:
599     return silc_skr_add_silc_simple(skr, public_key, usage, key_context);
600     break;
601
602   default:
603     break;
604   }
605
606   return SILC_SKR_ERROR;
607 }
608
609
610 /************************** Search Constraints API **************************/
611
612 /* Allocate search constraints */
613
614 SilcSKRFind silc_skr_find_alloc(void)
615 {
616   SilcSKRFind find;
617
618   find = silc_calloc(1, sizeof(*find));
619   if (!find)
620     return NULL;
621
622   find->constr = silc_hash_table_alloc(0, silc_hash_uint, NULL, NULL, NULL,
623                                        silc_skr_find_destructor, NULL, TRUE);
624   if (!find->constr) {
625     silc_skr_find_free(find);
626     return NULL;
627   }
628
629   return find;
630 }
631
632 /* Free search constraints */
633
634 void silc_skr_find_free(SilcSKRFind find)
635 {
636   if (find->constr)
637     silc_hash_table_free(find->constr);
638   silc_free(find);
639 }
640
641 SilcBool silc_skr_find_set_pkcs_type(SilcSKRFind find, SilcPKCSType type)
642 {
643   return silc_hash_table_add(find->constr,
644                              SILC_32_TO_PTR(SILC_SKR_FIND_PKCS_TYPE),
645                              SILC_32_TO_PTR(type));
646 }
647
648 SilcBool silc_skr_find_set_username(SilcSKRFind find, const char *username)
649 {
650   void *c = silc_memdup(username, strlen(username));
651   if (!c)
652     return FALSE;
653   return silc_hash_table_add(find->constr,
654                              SILC_32_TO_PTR(SILC_SKR_FIND_USERNAME), c);
655 }
656
657 SilcBool silc_skr_find_set_host(SilcSKRFind find, const char *host)
658 {
659   void *c = silc_memdup(host, strlen(host));
660   if (!c)
661     return FALSE;
662   return silc_hash_table_add(find->constr,
663                              SILC_32_TO_PTR(SILC_SKR_FIND_HOST), c);
664 }
665
666 SilcBool silc_skr_find_set_realname(SilcSKRFind find, const char *realname)
667 {
668   void *c = silc_memdup(realname, strlen(realname));
669   if (!c)
670     return FALSE;
671   return silc_hash_table_add(find->constr,
672                              SILC_32_TO_PTR(SILC_SKR_FIND_REALNAME), c);
673 }
674
675 SilcBool silc_skr_find_set_email(SilcSKRFind find, const char *email)
676 {
677   void *c = silc_memdup(email, strlen(email));
678   if (!c)
679     return FALSE;
680   return silc_hash_table_add(find->constr,
681                              SILC_32_TO_PTR(SILC_SKR_FIND_EMAIL), c);
682 }
683
684 SilcBool silc_skr_find_set_org(SilcSKRFind find, const char *org)
685 {
686   void *c = silc_memdup(org, strlen(org));
687   if (!c)
688     return FALSE;
689   return silc_hash_table_add(find->constr,
690                              SILC_32_TO_PTR(SILC_SKR_FIND_ORG), c);
691 }
692
693 SilcBool silc_skr_find_set_country(SilcSKRFind find, const char *country)
694 {
695   void *c = silc_memdup(country, strlen(country));
696   if (!c)
697     return FALSE;
698   return silc_hash_table_add(find->constr,
699                              SILC_32_TO_PTR(SILC_SKR_FIND_COUNTRY), c);
700 }
701
702 SilcBool silc_skr_find_set_public_key(SilcSKRFind find,
703                                       SilcPublicKey public_key)
704 {
705   SilcPublicKey pk = silc_pkcs_public_key_copy(public_key);
706   if (!pk)
707     return FALSE;
708   return silc_hash_table_add(find->constr,
709                              SILC_32_TO_PTR(SILC_SKR_FIND_PUBLIC_KEY), pk);
710 }
711
712 SilcBool silc_skr_find_set_context(SilcSKRFind find, void *context)
713 {
714   return silc_hash_table_add(find->constr,
715                              SILC_32_TO_PTR(SILC_SKR_FIND_CONTEXT), context);
716 }
717
718 SilcBool silc_skr_find_set_usage(SilcSKRFind find, SilcSKRKeyUsage usage)
719 {
720   if (!usage)
721     return TRUE;
722   return silc_hash_table_add(find->constr,
723                              SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
724                              SILC_32_TO_PTR(usage));
725 }
726
727 /******************************** Search API ********************************/
728
729 /* Finds key(s) by the set search constraints.  The callback will be called
730    once keys has been found. */
731 /* This is now synchronous function but may later change async */
732
733 SilcAsyncOperation silc_skr_find(SilcSKR skr, SilcSKRFind find,
734                                  SilcSKRFindCallback callback,
735                                  void *callback_context)
736 {
737   SilcSKRStatus status = SILC_SKR_ERROR;
738   SilcHashTableList htl;
739   SilcDList list, results = NULL;
740   void *type, *ctx, *usage = NULL;
741
742   SILC_LOG_DEBUG(("Finding key from repository"));
743
744   if (!find || !callback)
745     return NULL;
746
747   silc_mutex_lock(skr->lock);
748
749   /* Get usage bits, if searching by them */
750   silc_hash_table_find(find->constr, SILC_32_TO_PTR(SILC_SKR_FIND_USAGE),
751                        NULL, &usage);
752
753   silc_hash_table_list(find->constr, &htl);
754   while (silc_hash_table_get(&htl, &type, &ctx)) {
755
756 #if defined(SILC_DEBUG)
757     char tmp[256];
758     memset(tmp, 0, sizeof(tmp));
759     silc_skr_type_string((SilcSKRFindType)SILC_32_TO_PTR(type),
760                          ctx, tmp, sizeof(tmp) - 1);
761     SILC_LOG_DEBUG(("Finding key by %s", tmp));
762 #endif /* SILC_DEBUG */
763
764     /* SILC_SKR_FIND_USAGE is handled separately while searching the keys. */
765     if ((SilcSKRFindType)SILC_32_TO_PTR(type) == SILC_SKR_FIND_USAGE)
766       continue;
767
768     /* Find entries by this search constraint */
769     if (!silc_skr_find_entry(skr, &status,
770                              (SilcSKRFindType)SILC_32_TO_PTR(type),
771                              ctx, &list, NULL, SILC_PTR_TO_32(usage))) {
772       SILC_LOG_DEBUG(("Not found"));
773       if (results) {
774         silc_dlist_uninit(results);
775         results = NULL;
776       }
777       break;
778     }
779
780     /* For now, our logic rule is AND.  All constraints must be found
781        to find the key.  Later OR might be added also. */
782     if (!silc_skr_results_and(list, &status, &results)) {
783       SILC_LOG_DEBUG(("Not found"));
784       if (results) {
785         silc_dlist_uninit(results);
786         results = NULL;
787       }
788       silc_dlist_uninit(list);
789       break;
790     }
791
792     silc_dlist_uninit(list);
793   }
794   silc_hash_table_list_reset(&htl);
795
796   silc_mutex_unlock(skr->lock);
797
798   /* Return results */
799   if (!results) {
800     callback(skr, find, status, NULL, callback_context);
801   } else {
802     silc_dlist_start(results);
803     callback(skr, find, SILC_SKR_OK, results, callback_context);
804   }
805
806   return NULL;
807 }