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