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