Splitted SILC core library. Core library includes now only
[silc.git] / apps / silc / clientconfig.c
1 /*
2
3   serverconfig.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 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 /*
21  * $Id$
22  * $Log$
23  * Revision 1.3  2000/07/05 06:12:05  priikone
24  *      Global cosmetic changes.
25  *
26  * Revision 1.2  2000/07/03 05:49:11  priikone
27  *      Remove ':' from the end of line when in commands section.  Command
28  *      execution should work now from config file ok.
29  *
30  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
31  *      Imported from internal CVS/Added Log headers.
32  *
33  *
34  */
35
36 #include "clientincludes.h"
37 #include "clientconfig.h"
38
39 /* 
40    All possible configuration sections for SILC client.
41 */
42 SilcClientConfigSection silc_client_config_sections[] = {
43   { "[cipher]", 
44     SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 },
45   { "[pkcs]", 
46     SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 2 },
47   { "[hash]", 
48     SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
49   { "[connection]", 
50     SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
51   { "[commands]", 
52     SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND, 0 },
53   
54   { NULL, SILC_CLIENT_CONFIG_SECTION_TYPE_NONE, 0 }
55 };
56
57 /* Allocates a new configuration object, opens configuration file and
58    parses the file. The parsed data is returned to the newly allocated
59    configuration object. */
60
61 SilcClientConfig silc_client_config_alloc(char *filename)
62 {
63   SilcClientConfig new;
64   SilcBuffer buffer;
65   SilcClientConfigParse config_parse;
66
67   SILC_LOG_DEBUG(("Allocating new configuration object"));
68
69   new = silc_calloc(1, sizeof(*new));
70   new->filename = filename;
71
72   /* Open configuration file and parse it */
73   config_parse = NULL;
74   buffer = NULL;
75   silc_config_open(filename, &buffer);
76   if (!buffer)
77     goto fail;
78   if ((silc_client_config_parse(new, buffer, &config_parse)) == FALSE)
79     goto fail;
80   if ((silc_client_config_parse_lines(new, config_parse)) == FALSE)
81     goto fail;
82
83   silc_free(buffer);
84
85   return new;
86
87  fail:
88   silc_free(new);
89   return NULL;
90 }
91
92 /* Free's a configuration object. */
93
94 void silc_client_config_free(SilcClientConfig config)
95 {
96   if (config) {
97
98     silc_free(config);
99   }
100 }
101
102 /* Parses the the buffer and returns the parsed lines into return_config
103    argument. The return_config argument doesn't have to be initialized 
104    before calling this. It will be initialized during the parsing. The
105    buffer sent as argument can be safely free'd after this function has
106    succesfully returned. */
107
108 int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer, 
109                              SilcClientConfigParse *return_config)
110 {
111   int i, begin;
112   unsigned int linenum;
113   char line[1024], *cp;
114   SilcClientConfigSection *cptr = NULL;
115   SilcClientConfigParse parse = *return_config, first = NULL;
116
117   SILC_LOG_DEBUG(("Parsing configuration file"));
118
119   begin = 0;
120   linenum = 0;
121   while((begin = silc_gets(line, sizeof(line), 
122                            buffer->data, buffer->len, begin)) != EOF) {
123     cp = line;
124     linenum++;
125
126     /* Check for bad line */
127     if (silc_check_line(cp))
128       continue;
129
130     /* Remove tabs and whitespaces from the line */
131     if (strchr(cp, '\t')) {
132       i = 0;
133       while(strchr(cp + i, '\t')) {
134         *strchr(cp + i, '\t') = ' ';
135         i++;
136       }
137     }
138     for (i = 0; i < strlen(cp); i++) {
139       if (cp[i] != ' ') {
140         if (i)
141           cp++;
142         break;
143       }
144       cp++;
145     }
146
147     /* Parse line */
148     switch(cp[0]) {
149     case '[':
150       /*
151        * Start of a section
152        */
153
154       /* Remove new line sign */
155       if (strchr(cp, '\n'))
156         *strchr(cp, '\n') = '\0';
157       
158       /* Check for matching sections */
159       for (cptr = silc_client_config_sections; cptr->section; cptr++)
160         if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
161           break;
162
163       if (!cptr->section) {
164         fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
165                         config->filename, linenum, cp);
166         return FALSE;
167       }
168
169       break;
170     default:
171       /*
172        * Start of a configuration line
173        */
174
175       /* Handle config section */
176       if (cptr->type != SILC_CLIENT_CONFIG_SECTION_TYPE_NONE) {
177         
178         if (strchr(cp, '\n'))
179             *strchr(cp, '\n') = ':';
180
181         if (parse == NULL) {
182           parse = silc_calloc(1, sizeof(*parse));
183           parse->line = NULL;
184           parse->section = NULL;
185           parse->next = NULL;
186           parse->prev = NULL;
187         } else {
188           if (parse->next == NULL) {
189             parse->next = silc_calloc(1, sizeof(*parse->next));
190             parse->next->line = NULL;
191             parse->next->section = NULL;
192             parse->next->next = NULL;
193             parse->next->prev = parse;
194             parse = parse->next;
195           }
196         }
197         
198         if (first == NULL)
199           first = parse;
200
201         /* Add the line to parsing structure for further parsing. */
202         if (parse) {
203           parse->section = cptr;
204           parse->line = silc_buffer_alloc(strlen(cp) + 1);
205           parse->linenum = linenum;
206           silc_buffer_pull_tail(parse->line, strlen(cp));
207           silc_buffer_put(parse->line, cp, strlen(cp));
208         }
209       }
210       break;
211     }
212   }
213   
214   /* Set the return_config argument to its first value so that further
215      parsing can be started from the first line. */
216   *return_config = first;
217
218   return TRUE;
219 }
220
221 /* Parses the lines earlier read from configuration file. The config object
222    must not be initialized, it will be initialized in this function. The
223    parse_config argument is uninitialized automatically during this
224    function. */
225
226 int silc_client_config_parse_lines(SilcClientConfig config, 
227                                    SilcClientConfigParse parse_config)
228 {
229   int ret, check = FALSE;
230   char *tmp;
231   SilcClientConfigParse pc = parse_config;
232   SilcBuffer line;
233
234   SILC_LOG_DEBUG(("Parsing configuration lines"));
235   
236   if (!config)
237     return FALSE;
238   
239   while(pc) {
240     check = FALSE;
241     line = pc->line;
242
243     /* Get number of tokens in line (command section is handeled
244        specially and has no tokens at all). */
245     ret = silc_config_check_num_token(line);
246     if (ret != pc->section->maxfields && 
247         pc->section->type != SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND) {
248       /* Bad line */
249       fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
250               config->filename, pc->linenum, ret, 
251               pc->section->maxfields);
252       break;
253     }
254
255     /* Parse the line */
256     switch(pc->section->type) {
257     case SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER:
258
259       if (!config->cipher) {
260         config->cipher = silc_calloc(1, sizeof(*config->cipher));
261         config->cipher->next = NULL;
262         config->cipher->prev = NULL;
263       } else {
264         if (!config->cipher->next) {
265           config->cipher->next = 
266             silc_calloc(1, sizeof(*config->cipher->next));
267           config->cipher->next->next = NULL;
268           config->cipher->next->prev = config->cipher;
269           config->cipher = config->cipher->next;
270         }
271       }
272
273       /* Get cipher name */
274       ret = silc_config_get_token(line, &config->cipher->alg_name);
275       if (ret < 0)
276         break;
277       if (ret == 0) {
278         fprintf(stderr, "%s:%d: Cipher name not defined\n",
279                 config->filename, pc->linenum);
280         break;
281       }
282
283       /* Get module name */
284       config->cipher->sim_name = NULL;
285       ret = silc_config_get_token(line, &config->cipher->sim_name);
286       if (ret < 0)
287         break;
288
289       /* Get block length */
290       ret = silc_config_get_token(line, &tmp);
291       if (ret < 0)
292         break;
293       if (ret == 0) {
294         fprintf(stderr, "%s:%d: Cipher block length not defined\n",
295                 config->filename, pc->linenum);
296         break;
297       }
298       config->cipher->block_len = atoi(tmp);
299       silc_free(tmp);
300
301       /* Get key length */
302       ret = silc_config_get_token(line, &tmp);
303       if (ret < 0)
304         break;
305       if (ret == 0) {
306         fprintf(stderr, "%s:%d: Cipher key length not defined\n",
307                 config->filename, pc->linenum);
308         break;
309       }
310       config->cipher->key_len = atoi(tmp);
311       silc_free(tmp);
312
313       check = TRUE;
314       break;
315
316     case SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS:
317
318       if (!config->pkcs) {
319         config->pkcs = silc_calloc(1, sizeof(*config->pkcs));
320         config->pkcs->next = NULL;
321         config->pkcs->prev = NULL;
322       } else {
323         if (!config->pkcs->next) {
324           config->pkcs->next = 
325             silc_calloc(1, sizeof(*config->pkcs->next));
326           config->pkcs->next->next = NULL;
327           config->pkcs->next->prev = config->pkcs;
328           config->pkcs = config->pkcs->next;
329         }
330       }
331
332       /* Get PKCS name */
333       ret = silc_config_get_token(line, &config->pkcs->alg_name);
334       if (ret < 0)
335         break;
336       if (ret == 0) {
337         fprintf(stderr, "%s:%d: PKCS name not defined\n",
338                 config->filename, pc->linenum);
339         break;
340       }
341
342       /* Get key length */
343       ret = silc_config_get_token(line, &tmp);
344       if (ret < 0)
345         break;
346       if (ret == 0) {
347         fprintf(stderr, "%s:%d: PKCS key length not defined\n",
348                 config->filename, pc->linenum);
349         break;
350       }
351       config->pkcs->key_len = atoi(tmp);
352       silc_free(tmp);
353
354       check = TRUE;
355       break;
356
357     case SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION:
358
359       if (!config->hash_func) {
360         config->hash_func = silc_calloc(1, sizeof(*config->hash_func));
361         config->hash_func->next = NULL;
362         config->hash_func->prev = NULL;
363       } else {
364         if (!config->hash_func->next) {
365           config->hash_func->next = 
366             silc_calloc(1, sizeof(*config->hash_func->next));
367           config->hash_func->next->next = NULL;
368           config->hash_func->next->prev = config->hash_func;
369           config->hash_func = config->hash_func->next;
370         }
371       }
372
373       /* Get Hash function name */
374       ret = silc_config_get_token(line, &config->hash_func->alg_name);
375       if (ret < 0)
376         break;
377       if (ret == 0) {
378         fprintf(stderr, "%s:%d: Hash function name not defined\n",
379                 config->filename, pc->linenum);
380         break;
381       }
382       
383       /* Get Hash function module name */
384       config->hash_func->sim_name = NULL;
385       ret = silc_config_get_token(line, &config->hash_func->sim_name);
386       if (ret < 0)
387         break;
388
389       /* Get block length */
390       ret = silc_config_get_token(line, &tmp);
391       if (ret < 0)
392         break;
393       if (ret == 0) {
394         fprintf(stderr, "%s:%d: Hash function block length not defined\n",
395                 config->filename, pc->linenum);
396         break;
397       }
398       config->hash_func->block_len = atoi(tmp);
399       silc_free(tmp);
400
401       /* Get hash length */
402       ret = silc_config_get_token(line, &tmp);
403       if (ret < 0)
404         break;
405       if (ret == 0) {
406         fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
407                 config->filename, pc->linenum);
408         break;
409       }
410       config->hash_func->key_len = atoi(tmp);
411       silc_free(tmp);
412
413       check = TRUE;
414       break;
415
416     case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION:
417
418       if (!config->conns) {
419         config->conns = silc_calloc(1, sizeof(*config->conns));
420         config->conns->next = NULL;
421         config->conns->prev = NULL;
422       } else {
423         if (!config->conns->next) {
424           config->conns->next = silc_calloc(1, sizeof(*config->conns));
425           config->conns->next->next = NULL;
426           config->conns->next->prev = config->conns;
427           config->conns = config->conns->next;
428         }
429       }
430       
431       /* Get host */
432       ret = silc_config_get_token(line, &config->conns->host);
433       if (ret < 0)
434         break;
435       if (ret == 0)
436         /* Any host */
437         config->conns->host = strdup("*");
438
439       /* Get authentication method */
440       ret = silc_config_get_token(line, &tmp);
441       if (ret < 0)
442         break;
443       if (ret) {
444         if (strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD) &&
445             strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) {
446           fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
447                   config->filename, pc->linenum, tmp);
448           break;
449         }
450
451         if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD))
452           config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
453
454         if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
455           config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
456
457         silc_free(tmp);
458       }
459
460       /* Get authentication data */
461       ret = silc_config_get_token(line, &config->conns->auth_data);
462       if (ret < 0)
463         break;
464
465       /* Get port */
466       ret = silc_config_get_token(line, &tmp);
467       if (ret < 0)
468         break;
469       if (ret) {
470         config->conns->port = atoi(tmp);
471         silc_free(tmp);
472       }
473
474       check = TRUE;
475       break;
476
477     case SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND:
478
479       if (!config->commands) {
480         config->commands = silc_calloc(1, sizeof(*config->commands));
481         config->commands->next = NULL;
482         config->commands->prev = NULL;
483       } else {
484         if (!config->commands->next) {
485           config->commands->next = silc_calloc(1, sizeof(*config->commands));
486           config->commands->next->next = NULL;
487           config->commands->next->prev = config->commands;
488           config->commands = config->commands->next;
489         }
490       }
491       
492       /* Get command line (this may include parameters as well. They
493          will be parsed later with standard command parser when
494          executing particular command.) */
495       config->commands->command = silc_calloc(strlen(line->data), 
496                                               sizeof(char));
497       memcpy(config->commands->command, line->data, strlen(line->data) - 1);
498       if (ret < 0)
499         break;
500
501       check = TRUE;
502       break;
503
504     case SILC_CLIENT_CONFIG_SECTION_TYPE_NONE:
505     default:
506       break;
507     }
508
509     /* Check for error */
510     if (check == FALSE) {
511       /* Line could not be parsed */
512       fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
513       break;
514     }
515
516     pc = pc->next;
517   }
518
519   if (check == FALSE)
520     return FALSE;;
521
522   /* Before returning all the lists in the config object must be set
523      to their first values (the last value is first here). */
524   while (config->cipher && config->cipher->prev)
525     config->cipher = config->cipher->prev;
526   while (config->pkcs && config->pkcs->prev)
527     config->pkcs = config->pkcs->prev;
528   while (config->hash_func && config->hash_func->prev)
529     config->hash_func = config->hash_func->prev;
530   while (config->conns && config->conns->prev)
531     config->conns = config->conns->prev;
532   while (config->commands && config->commands->prev)
533     config->commands = config->commands->prev;
534   
535   SILC_LOG_DEBUG(("Done"));
536   
537   return TRUE;
538 }
539
540 /* Registers configured ciphers. These can then be allocated by the
541    client when needed. */
542
543 void silc_client_config_register_ciphers(SilcClientConfig config)
544 {
545   SilcClientConfigSectionAlg *alg;
546   SilcClient client = (SilcClient)config->client;
547
548   SILC_LOG_DEBUG(("Registering configured ciphers"));
549
550   alg = config->cipher;
551   while(alg) {
552
553     if (!alg->sim_name) {
554       /* Crypto module is supposed to be built in. Nothing to be done
555          here except to test that the cipher really is built in. */
556       SilcCipher tmp = NULL;
557
558       if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
559         SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name));
560         silc_client_stop(client);
561         exit(1);
562       }
563       silc_cipher_free(tmp);
564
565 #ifdef SILC_SIM
566     } else {
567       /* Load (try at least) the crypto SIM module */
568       SilcCipherObject cipher;
569       SilcSimContext *sim;
570
571       memset(&cipher, 0, sizeof(cipher));
572       cipher.name = alg->alg_name;
573       cipher.block_len = alg->block_len;
574       cipher.key_len = alg->key_len * 8;
575
576       sim = silc_sim_alloc();
577       sim->type = SILC_SIM_CIPHER;
578       sim->libname = alg->sim_name;
579
580       if ((silc_sim_load(sim))) {
581         cipher.set_key = 
582           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
583                                                 SILC_CIPHER_SIM_SET_KEY));
584         SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
585         cipher.set_key_with_string = 
586           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
587                                                 SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
588         SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
589         cipher.encrypt = 
590           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
591                                                 SILC_CIPHER_SIM_ENCRYPT_CBC));
592         SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
593         cipher.decrypt = 
594           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
595                                                 SILC_CIPHER_SIM_DECRYPT_CBC));
596         SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
597         cipher.context_len = 
598           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
599                                                 SILC_CIPHER_SIM_CONTEXT_LEN));
600         SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
601
602         /* Put the SIM to the table of all SIM's in client */
603         client->sim = silc_realloc(client->sim,
604                                    sizeof(*client->sim) * 
605                                    (client->sim_count + 1));
606         client->sim[client->sim_count] = sim;
607         client->sim_count++;
608       } else {
609         SILC_LOG_ERROR(("Error configuring ciphers"));
610         silc_client_stop(client);
611         exit(1);
612       }
613
614       /* Register the cipher */
615       silc_cipher_register(&cipher);
616 #endif
617     }
618
619     alg = alg->next;
620   }
621 }
622
623 /* Registers configured PKCS's. */
624 /* XXX: This really doesn't do anything now since we have statically
625    registered our PKCS's. This should be implemented when PKCS works
626    as SIM's. This checks now only that the PKCS user requested is 
627    really out there. */
628
629 void silc_client_config_register_pkcs(SilcClientConfig config)
630 {
631   SilcClientConfigSectionAlg *alg = config->pkcs;
632   SilcClient client = (SilcClient)config->client;
633   SilcPKCS tmp = NULL;
634
635   SILC_LOG_DEBUG(("Registering configured PKCS"));
636
637   while(alg) {
638
639     if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) {
640       SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name));
641       silc_client_stop(client);
642       exit(1);
643     }
644     silc_free(tmp);
645
646     alg = alg->next;
647   }
648 }
649
650 /* Registers configured hash functions. These can then be allocated by the
651    client when needed. */
652
653 void silc_client_config_register_hashfuncs(SilcClientConfig config)
654 {
655   SilcClientConfigSectionAlg *alg;
656   SilcClient client = (SilcClient)config->client;
657
658   SILC_LOG_DEBUG(("Registering configured hash functions"));
659
660   alg = config->hash_func;
661   while(alg) {
662
663     if (!alg->sim_name) {
664       /* Hash module is supposed to be built in. Nothing to be done
665          here except to test that the hash function really is built in. */
666       SilcHash tmp = NULL;
667
668       if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
669         SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name));
670         silc_client_stop(client);
671         exit(1);
672       }
673       silc_free(tmp);
674
675 #ifdef SILC_SIM
676     } else {
677       /* Load (try at least) the hash SIM module */
678       SilcHashObject hash;
679       SilcSimContext *sim;
680
681       memset(&hash, 0, sizeof(hash));
682       hash.name = alg->alg_name;
683       hash.block_len = alg->block_len;
684       hash.hash_len = alg->key_len;
685
686       sim = silc_sim_alloc();
687       sim->type = SILC_SIM_HASH;
688       sim->libname = alg->sim_name;
689
690       if ((silc_sim_load(sim))) {
691         hash.init = 
692           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
693                                                 SILC_HASH_SIM_INIT));
694         SILC_LOG_DEBUG(("init=%p", hash.init));
695         hash.update = 
696           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
697                                                 SILC_HASH_SIM_UPDATE));
698         SILC_LOG_DEBUG(("update=%p", hash.update));
699         hash.final = 
700           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
701                                                 SILC_HASH_SIM_FINAL));
702         SILC_LOG_DEBUG(("final=%p", hash.final));
703         hash.context_len = 
704           silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
705                                                 SILC_HASH_SIM_CONTEXT_LEN));
706         SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
707
708         /* Put the SIM to the table of all SIM's in client */
709         client->sim = silc_realloc(client->sim,
710                                    sizeof(*client->sim) * 
711                                    (client->sim_count + 1));
712         client->sim[client->sim_count] = sim;
713         client->sim_count++;
714       } else {
715         SILC_LOG_ERROR(("Error configuring hash functions"));
716         silc_client_stop(client);
717         exit(1);
718       }
719
720       /* Register the cipher */
721       silc_hash_register(&hash);
722 #endif
723     }
724
725     alg = alg->next;
726   }
727 }
728
729
730 SilcClientConfigSectionConnection *
731 silc_client_config_find_connection(SilcClientConfig config, 
732                                    char *host, int port)
733 {
734   int i;
735   SilcClientConfigSectionConnection *conn = NULL;
736
737   SILC_LOG_DEBUG(("Finding connection"));
738
739   if (!host)
740     return NULL;
741
742   if (!config->conns)
743     return NULL;
744
745   conn = config->conns;
746   for (i = 0; conn; i++) {
747     if (silc_string_compare(conn->host, host))
748       break;
749     conn = conn->next;
750   }
751
752   if (!conn)
753     return NULL;
754
755   SILC_LOG_DEBUG(("Found match"));
756
757   return conn;
758 }