6ecdd14352aebcd65b9a831c8d7183356eb49525
[silc.git] / lib / silcapputil / silcapputil.c
1 /*
2
3   silcapputil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2007 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 /* $Id$ */
20
21 #include "silc.h"
22
23 static char *silc_create_pk_identifier(void)
24 {
25   char *username = NULL, *realname = NULL;
26   char *hostname, email[256];
27   char *ident;
28
29   /* Get realname */
30   realname = silc_get_real_name();
31
32   /* Get hostname */
33   hostname = silc_net_localhost();
34   if (!hostname)
35     return NULL;
36
37   /* Get username (mandatory) */
38   username = silc_get_username();
39   if (!username)
40     return NULL;
41
42   /* Create default email address, whether it is right or not */
43   silc_snprintf(email, sizeof(email), "%s@%s", username, hostname);
44
45   ident = silc_pkcs_silc_encode_identifier(username, hostname, realname,
46                                            email, NULL, NULL, NULL);
47   if (realname)
48     silc_free(realname);
49   silc_free(hostname);
50   silc_free(username);
51
52   return ident;
53 }
54
55 /* Generate key pair */
56
57 SilcBool silc_create_key_pair(const char *pkcs_name,
58                               SilcUInt32 key_len_bits,
59                               const char *pub_filename,
60                               const char *prv_filename,
61                               const char *pub_identifier,
62                               const char *passphrase,
63                               SilcPublicKey *return_public_key,
64                               SilcPrivateKey *return_private_key,
65                               SilcBool interactive)
66 {
67   SilcRng rng;
68   char line[256];
69   char *pkfile = pub_filename ? strdup(pub_filename) : NULL;
70   char *prvfile = prv_filename ? strdup(prv_filename) : NULL;
71   char *alg = pkcs_name ? strdup(pkcs_name) : NULL;
72   char *identifier = pub_identifier ? strdup(pub_identifier) : NULL;
73   char *pass = passphrase ? strdup(passphrase) : NULL;
74   SilcPublicKey public_key;
75   SilcPrivateKey private_key;
76
77   if (interactive && (!alg || !pub_filename || !prv_filename))
78     printf("\
79 New pair of keys will be created.  Please, answer to following questions.\n\
80 ");
81
82   if (!alg) {
83     if (interactive) {
84       while (!alg) {
85         alg = silc_get_input("PKCS name (l to list names) [rsa]: ", FALSE);
86         if (!alg)
87           alg = strdup("rsa");
88
89         if (*alg == 'l' || *alg == 'L') {
90           char *list = silc_pkcs_get_supported();
91           printf("%s\n", list);
92           silc_free(list);
93           silc_free(alg);
94           alg = NULL;
95         }
96       }
97     } else {
98       alg = strdup("rsa");
99     }
100   }
101
102   if (!silc_pkcs_find_algorithm(alg, NULL)) {
103     fprintf(stderr, "Unknown PKCS algorithm `%s' or crypto library"
104             "is not initialized", alg);
105     return FALSE;
106   }
107
108   if (!key_len_bits) {
109     if (interactive) {
110       char *length = NULL;
111       length = silc_get_input("Key length in key_len_bits [2048]: ", FALSE);
112       if (length)
113         key_len_bits = atoi(length);
114       silc_free(length);
115     }
116     if (!key_len_bits)
117       key_len_bits = 2048;
118   }
119
120   if (!identifier) {
121     char *def = silc_create_pk_identifier();
122
123     if (interactive) {
124       memset(line, 0, sizeof(line));
125       if (def)
126         silc_snprintf(line, sizeof(line), "Identifier [%s]: ", def);
127       else
128         silc_snprintf(line, sizeof(line),
129                "Identifier (eg. UN=jon, HN=jon.dummy.com, "
130                "RN=Jon Johnson, E=jon@dummy.com): ");
131
132       while (!identifier) {
133         identifier = silc_get_input(line, FALSE);
134         if (!identifier && def)
135           identifier = strdup(def);
136       }
137     } else {
138       if (!def) {
139         fprintf(stderr, "Could not create public key identifier: %s\n",
140                 strerror(errno));
141         return FALSE;
142       }
143       identifier = strdup(def);
144     }
145
146     silc_free(def);
147   }
148
149   rng = silc_rng_alloc();
150   silc_rng_init(rng);
151   silc_rng_global_init(rng);
152
153   if (!pkfile) {
154     if (interactive) {
155       memset(line, 0, sizeof(line));
156       silc_snprintf(line, sizeof(line), "Public key filename [public_key.pub]: ");
157       pkfile = silc_get_input(line, FALSE);
158     }
159     if (!pkfile)
160       pkfile = strdup("public_key.pub");
161   }
162
163   if (!prvfile) {
164     if (interactive) {
165       memset(line, 0, sizeof(line));
166       silc_snprintf(line, sizeof(line), "Private key filename [private_key.prv]: ");
167       prvfile = silc_get_input(line, FALSE);
168     }
169     if (!prvfile)
170       prvfile = strdup("private_key.prv");
171   }
172
173   if (!pass) {
174     while (TRUE) {
175       char *pass2 = NULL;
176       pass = silc_get_input("Private key passphrase: ", TRUE);
177       if (!pass) {
178         pass = strdup("");
179         break;
180       } else {
181         SilcBool match;
182         printf("\n");
183         pass2 = silc_get_input("Retype private key passphrase: ", TRUE);
184         if (!pass2)
185           pass2 = strdup("");
186         match = !strcmp(pass, pass2);
187         silc_free(pass2);
188         if (match)
189           break;
190         fprintf(stderr, "\nPassphrases do not match\n\n");
191       }
192     }
193   }
194
195   if (interactive)
196     printf("\nGenerating the key pair...\n");
197
198   /* Generate keys */
199   if (!silc_pkcs_silc_generate_key(alg, key_len_bits,
200                                    identifier, rng, &public_key,
201                                    &private_key))
202     return FALSE;
203
204   /* Save public key into file */
205   silc_pkcs_save_public_key(pkfile, public_key, SILC_PKCS_FILE_BASE64);
206
207   /* Save private key into file */
208   silc_pkcs_save_private_key(prvfile, private_key,
209                              (const unsigned char *)pass, strlen(pass),
210                              SILC_PKCS_FILE_BIN, rng);
211
212   if (return_public_key)
213     *return_public_key = public_key;
214   else
215     silc_pkcs_public_key_free(public_key);
216
217   if (return_private_key)
218     *return_private_key = private_key;
219   else
220     silc_pkcs_private_key_free(private_key);
221
222   printf("Public key has been saved into `%s'.\n", pkfile);
223   printf("Private key has been saved into `%s'.\n", prvfile);
224   if (interactive) {
225     printf("Press <Enter> to continue...\n");
226     getchar();
227   }
228
229   silc_rng_free(rng);
230   silc_free(alg);
231   silc_free(pkfile);
232   silc_free(prvfile);
233   silc_free(identifier);
234   memset(pass, 0, strlen(pass));
235   silc_free(pass);
236
237   return TRUE;
238 }
239
240 /* Load key pair */
241
242 SilcBool silc_load_key_pair(const char *pub_filename,
243                             const char *prv_filename,
244                             const char *passphrase,
245                             SilcPublicKey *return_public_key,
246                             SilcPrivateKey *return_private_key)
247 {
248   char *pass = passphrase ? strdup(passphrase) : NULL;
249
250   SILC_LOG_DEBUG(("Loading public and private keys"));
251
252   if (!silc_pkcs_load_public_key(pub_filename, return_public_key)) {
253     if (pass)
254       memset(pass, 0, strlen(pass));
255     silc_free(pass);
256     return FALSE;
257   }
258
259   if (!pass) {
260     pass = silc_get_input("Private key passphrase: ", TRUE);
261     if (!pass)
262       pass = strdup("");
263   }
264
265   if (!silc_pkcs_load_private_key(prv_filename,
266                                   (const unsigned char *)pass, strlen(pass),
267                                   return_private_key)) {
268     silc_pkcs_public_key_free(*return_public_key);
269     *return_public_key = NULL;
270     memset(pass, 0, strlen(pass));
271     silc_free(pass);
272     return FALSE;
273   }
274
275   memset(pass, 0, strlen(pass));
276   silc_free(pass);
277   return TRUE;
278 }
279
280 /* Dump public key into stdout */
281
282 SilcBool silc_show_public_key(SilcPublicKey public_key)
283 {
284   SilcSILCPublicKey silc_pubkey;
285   SilcPublicKeyIdentifier ident;
286   char *fingerprint, *babbleprint;
287   unsigned char *pk;
288   SilcUInt32 pk_len;
289   SilcUInt32 key_len = 0;
290
291   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
292   if (!silc_pubkey)
293     return FALSE;
294
295   ident = &silc_pubkey->identifier;
296   key_len = silc_pkcs_public_key_get_len(public_key);
297   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
298   if (!pk)
299     return FALSE;
300   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
301   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
302
303   printf("Algorithm          : %s\n", silc_pkcs_get_name(public_key));
304   if (key_len)
305     printf("Key length (bits)  : %d\n", (unsigned int)key_len);
306   if (ident->version)
307     printf("Version            : %s\n", ident->version);
308   if (ident->realname)
309     printf("Real name          : %s\n", ident->realname);
310   if (ident->username)
311     printf("Username           : %s\n", ident->username);
312   if (ident->host)
313     printf("Hostname           : %s\n", ident->host);
314   if (ident->email)
315     printf("Email              : %s\n", ident->email);
316   if (ident->org)
317     printf("Organization       : %s\n", ident->org);
318   if (ident->country)
319     printf("Country            : %s\n", ident->country);
320   printf("Fingerprint (SHA1) : %s\n", fingerprint);
321   printf("Babbleprint (SHA1) : %s\n", babbleprint);
322
323   fflush(stdout);
324
325   silc_free(fingerprint);
326   silc_free(babbleprint);
327   silc_free(pk);
328
329   return TRUE;
330 }
331
332 /* Dump public key into stdout */
333
334 SilcBool silc_show_public_key_file(const char *pub_filename)
335 {
336   SilcPublicKey public_key;
337   SilcBool ret;
338
339   if (!silc_pkcs_load_public_key((char *)pub_filename, &public_key)) {
340     fprintf(stderr, "Could not load public key file `%s'\n", pub_filename);
341     return FALSE;
342   }
343
344   printf("Public key file    : %s\n", pub_filename);
345   ret = silc_show_public_key(public_key);
346   silc_pkcs_public_key_free(public_key);
347
348   return ret;
349 }
350
351 /* Change private key passphrase */
352
353 SilcBool silc_change_private_key_passphrase(const char *prv_filename,
354                                             const char *old_passphrase,
355                                             const char *new_passphrase)
356 {
357   SilcPrivateKey private_key;
358   char *pass;
359   SilcRng rng;
360
361   pass = old_passphrase ? strdup(old_passphrase) : NULL;
362   if (!pass) {
363     pass = silc_get_input("Old passphrase: ", TRUE);
364     if (!pass)
365       pass = strdup("");
366   }
367
368   if (!silc_pkcs_load_private_key(prv_filename,
369                                   (const unsigned char *)pass, strlen(pass),
370                                   &private_key)) {
371     memset(pass, 0, strlen(pass));
372     silc_free(pass);
373     fprintf(stderr, "Could not load private key `%s' file\n", prv_filename);
374     return FALSE;
375   }
376
377   memset(pass, 0, strlen(pass));
378   silc_free(pass);
379
380   pass = new_passphrase ? strdup(new_passphrase) : NULL;
381   if (!pass) {
382     char *pass2 = NULL;
383     fprintf(stdout, "\n");
384     pass = silc_get_input("New passphrase: ", TRUE);
385     if (!pass) {
386       pass = strdup("");
387     } else {
388       while (TRUE) {
389         printf("\n");
390         pass2 = silc_get_input("Retype new passphrase: ", TRUE);
391         if (!pass2)
392           pass2 = strdup("");
393         if (!strcmp(pass, pass2))
394           break;
395         fprintf(stderr, "\nPassphrases do not match");
396       }
397       silc_free(pass2);
398     }
399   }
400
401   rng = silc_rng_alloc();
402   silc_rng_init(rng);
403
404   silc_pkcs_save_private_key((char *)prv_filename, private_key,
405                              (unsigned char *)pass, strlen(pass),
406                              SILC_PKCS_FILE_BIN, rng);
407
408   fprintf(stdout, "\nPassphrase changed\n");
409
410   memset(pass, 0, strlen(pass));
411   silc_free(pass);
412
413   silc_pkcs_private_key_free(private_key);
414   silc_rng_free(rng);
415
416   return TRUE;
417 }
418
419 /* Checks that the 'identifier' string is valid identifier string
420    and does not contain any unassigned or prohibited character.  This
421    function is used to check for valid nicknames, channel names,
422    server names, usernames, hostnames, service names, algorithm names,
423    other security property names, and SILC Public Key name. */
424
425 unsigned char *silc_identifier_check(const unsigned char *identifier,
426                                      SilcUInt32 identifier_len,
427                                      SilcStringEncoding identifier_encoding,
428                                      SilcUInt32 max_allowed_length,
429                                      SilcUInt32 *out_len)
430 {
431   unsigned char *utf8s;
432   SilcUInt32 utf8s_len;
433   SilcStringprepStatus status;
434
435   if (!identifier || !identifier_len)
436     return NULL;
437
438   if (max_allowed_length && identifier_len > max_allowed_length)
439     return NULL;
440
441   status = silc_stringprep(identifier, identifier_len,
442                            identifier_encoding, SILC_IDENTIFIER_PREP, 0,
443                            &utf8s, &utf8s_len, SILC_STRING_UTF8);
444   if (status != SILC_STRINGPREP_OK) {
445     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
446     return NULL;
447   }
448
449   if (out_len)
450     *out_len = utf8s_len;
451
452   return utf8s;
453 }
454
455 /* Same as above but does not allocate memory, just checks the
456    validity of the string. */
457
458 SilcBool silc_identifier_verify(const unsigned char *identifier,
459                                 SilcUInt32 identifier_len,
460                                 SilcStringEncoding identifier_encoding,
461                                 SilcUInt32 max_allowed_length)
462 {
463   SilcStringprepStatus status;
464
465   if (!identifier || !identifier_len)
466     return FALSE;
467
468   if (max_allowed_length && identifier_len > max_allowed_length)
469     return FALSE;
470
471   status = silc_stringprep(identifier, identifier_len,
472                            identifier_encoding, SILC_IDENTIFIER_PREP, 0,
473                            NULL, NULL, SILC_STRING_UTF8);
474   if (status != SILC_STRINGPREP_OK) {
475     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
476     return FALSE;
477   }
478
479   return TRUE;
480 }
481
482 unsigned char *silc_channel_name_check(const unsigned char *identifier,
483                                        SilcUInt32 identifier_len,
484                                        SilcStringEncoding identifier_encoding,
485                                        SilcUInt32 max_allowed_length,
486                                        SilcUInt32 *out_len)
487 {
488   unsigned char *utf8s;
489   SilcUInt32 utf8s_len;
490   SilcStringprepStatus status;
491
492   if (!identifier || !identifier_len)
493     return NULL;
494
495   if (max_allowed_length && identifier_len > max_allowed_length)
496     return NULL;
497
498   status = silc_stringprep(identifier, identifier_len,
499                            identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
500                            &utf8s, &utf8s_len, SILC_STRING_UTF8);
501   if (status != SILC_STRINGPREP_OK) {
502     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
503     return NULL;
504   }
505
506   if (out_len)
507     *out_len = utf8s_len;
508
509   return utf8s;
510 }
511
512 /* Same as above but does not allocate memory, just checks the
513    validity of the string. */
514
515 SilcBool silc_channel_name_verify(const unsigned char *identifier,
516                                   SilcUInt32 identifier_len,
517                                   SilcStringEncoding identifier_encoding,
518                                   SilcUInt32 max_allowed_length)
519 {
520   SilcStringprepStatus status;
521
522   if (!identifier || !identifier_len)
523     return FALSE;
524
525   if (max_allowed_length && identifier_len > max_allowed_length)
526     return FALSE;
527
528   status = silc_stringprep(identifier, identifier_len,
529                            identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
530                            NULL, NULL, SILC_STRING_UTF8);
531   if (status != SILC_STRINGPREP_OK) {
532     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
533     return FALSE;
534   }
535
536   return TRUE;
537 }
538
539 /* Return mode list */
540
541 SilcBool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
542                             SilcUInt32 **list)
543 {
544   int i;
545
546   if (silc_buffer_len(mode_list) / 4 != mode_list_count)
547     return FALSE;
548
549   *list = silc_calloc(mode_list_count, sizeof(**list));
550
551   for (i = 0; i < mode_list_count; i++) {
552     SILC_GET32_MSB((*list)[i], mode_list->data);
553     silc_buffer_pull(mode_list, 4);
554   }
555
556   silc_buffer_push(mode_list, mode_list->data - mode_list->head);
557
558   return TRUE;
559 }
560
561 /* Status message structure. Messages are defined below. */
562 typedef struct {
563   SilcStatus status;
564   const char *message;
565 } SilcStatusMessage;
566
567 #define STAT(x) SILC_STATUS_ERR_##x
568 static const SilcStatusMessage silc_status_messages[] = {
569
570   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
571   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
572   { STAT(NO_SUCH_SERVER),    "There was no such server" },
573   { STAT(INCOMPLETE_INFORMATION),  "Incomplete registration information" },
574   { STAT(NO_RECIPIENT),      "No recipient given" },
575   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
576   { STAT(WILDCARDS),         "Wilcrads not allowed" },
577   { STAT(NO_CLIENT_ID),      "No Client ID given" },
578   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
579   { STAT(NO_SERVER_ID),      "No Server ID given" },
580   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
581   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
582   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
583   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
584   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
585   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
586   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
587   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
588   { STAT(NOT_REGISTERED),    "You have not registered" },
589   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
590   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
591   { STAT(PERM_DENIED),       "Permission denied" },
592   { STAT(BANNED_FROM_SERVER),"You are not allowed to connect" },
593   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
594   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
595   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
596   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
597   { STAT(UNKNOWN_MODE),    "Unknown mode" },
598   { STAT(NOT_YOU),         "Cannot change mode for other users" },
599   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
600   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
601   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
602   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
603   { STAT(BAD_NICKNAME),    "Bad nickname" },
604   { STAT(BAD_CHANNEL),     "Bad channel name" },
605   { STAT(AUTH_FAILED),     "Authentication failed" },
606   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
607   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
608   { STAT(RESOURCE_LIMIT), "No more free resources" },
609   { STAT(NO_SUCH_SERVICE), "Service doesn't exist" },
610   { STAT(NOT_AUTHENTICATED), "You have not been authenticated" },
611   { STAT(BAD_SERVER_ID), "Server ID is not valid" },
612   { STAT(KEY_EXCHANGE_FAILED), "Key exchange failed" },
613   { STAT(BAD_VERSION), "Bad version" },
614   { STAT(TIMEDOUT), "Service timed out" },
615   { STAT(UNSUPPORTED_PUBLIC_KEY), "Unsupported public key type" },
616   { STAT(OPERATION_ALLOWED), "Operation is not allowed" },
617   { STAT(BAD_SERVER), "Bad server name" },
618   { STAT(BAD_USERNAME), "Bad user name" },
619   { STAT(NO_SUCH_PUBLIC_KEY), "Unknown public key" },
620
621   { 0, NULL }
622 };
623
624 /* Returns status message string */
625
626 const char *silc_get_status_message(unsigned char status)
627 {
628   int i;
629
630   for (i = 0; silc_status_messages[i].message; i++) {
631     if (silc_status_messages[i].status == status)
632       break;
633   }
634
635   if (silc_status_messages[i].message == NULL)
636     return "";
637
638   return silc_status_messages[i].message;
639 }
640
641 static const char *packet_name[] = {
642   "NONE",
643   "DISCONNECT",
644   "SUCCESS",
645   "FAILURE",
646   "REJECT",
647   "NOTIFY",
648   "ERROR",
649   "CHANNEL MESSAGE",
650   "CHANNEL KEY",
651   "PRIVATE MESSAGE",
652   "PRIVATE MESSAGE KEY",
653   "COMMAND",
654   "COMMAND REPLY",
655   "KEY EXCHANGE",
656   "KEY EXCHANGE 1",
657   "KEY EXCHANGE 2",
658   "CONNECTION AUTH REQUEST",
659   "CONNECTION AUTH",
660   "NEW ID",
661   "NEW CLIENT",
662   "NEW SERVER",
663   "NEW CHANNEL",
664   "REKEY",
665   "REKEY_DONE",
666   "HEARTBEAT",
667   "KEY AGREEMENT",
668   "RESUME ROUTER",
669   "FTP",
670   "RESUME CLIENT",
671 };
672
673 /* Returns packet type name */
674
675 const char *silc_get_packet_name(unsigned char type)
676 {
677   if (type >= SILC_PACKET_MAX)
678     return "RESERVED";
679   if (type >= SILC_PACKET_PRIVATE)
680     return "PRIVATE RANGE";
681   if (type > (sizeof(packet_name) / sizeof(*packet_name)))
682     return "UNKNOWN";
683   return packet_name[type];
684 }
685
686 static const char *command_name[] = {
687   "NONE",
688   "WHOIS",
689   "WHOWAS",
690   "IDENTIFY",
691   "NICK",
692   "LIST",
693   "TOPIC",
694   "INVITE",
695   "QUIT",
696   "KILL",
697   "INFO",
698   "STATS",
699   "PING",
700   "OPER",
701   "JOIN",
702   "MOTD",
703   "UMODE",
704   "CMODE",
705   "CUMODE",
706   "KICK",
707   "BAN",
708   "DETACH",
709   "WATCH",
710   "SILCOPER",
711   "LEAVE",
712   "USERS",
713   "GETKEY",
714   "SERVICE",
715 };
716
717 /* Returns command name */
718
719 const char *silc_get_command_name(unsigned char command)
720 {
721   if (command >= SILC_COMMAND_RESERVED)
722     return "RESERVED";
723   if (command >= SILC_COMMAND_PRIVATE)
724     return "PRIVATE RANGE";
725   if (command > (sizeof(command_name) / sizeof(*command_name)))
726     return "UNKNOWN";
727   return command_name[command];
728 }
729
730 /* Parses SILC protocol style version string. */
731
732 SilcBool silc_parse_version_string(const char *version,
733                                    SilcUInt32 *protocol_version,
734                                    char **protocol_version_string,
735                                    SilcUInt32 *software_version,
736                                    char **software_version_string,
737                                    char **vendor_version)
738 {
739   char *cp, buf[32];
740   int maj = 0, min = 0;
741
742   if (!strstr(version, "SILC-"))
743     return FALSE;
744
745   cp = (char *)version + 5;
746   if (!cp || !(*cp))
747     return FALSE;
748
749   /* Take protocol version */
750
751   maj = atoi(cp);
752   if (!strchr(cp, '.'))
753     return FALSE;
754   cp = strchr(cp, '.') + 1;
755   if (!cp || !(*cp))
756     return FALSE;
757   min = atoi(cp);
758
759   memset(buf, 0, sizeof(buf));
760   silc_snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
761   if (protocol_version)
762     *protocol_version = atoi(buf);
763   memset(buf, 0, sizeof(buf));
764   silc_snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
765   if (protocol_version_string)
766     *protocol_version_string = strdup(buf);
767
768   /* Take software version */
769
770   maj = 0;
771   min = 0;
772   if (!strchr(cp, '-'))
773     return FALSE;
774   cp = strchr(cp, '-') + 1;
775   if (!cp || !(*cp))
776     return FALSE;
777
778   maj = atoi(cp);
779   if (strchr(cp, '.')) {
780     cp = strchr(cp, '.') + 1;
781     if (cp && *cp)
782       min = atoi(cp);
783   }
784
785   memset(buf, 0, sizeof(buf));
786   silc_snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
787   if (software_version)
788     *software_version = atoi(buf);
789   memset(buf, 0, sizeof(buf));
790   silc_snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
791   if (software_version_string)
792     *software_version_string = strdup(buf);
793
794   /* Take vendor string */
795
796   if (strchr(cp, '.')) {
797     cp = strchr(cp, '.') + 1;
798     if (cp && *cp && vendor_version)
799       *vendor_version = strdup(cp);
800   } else if (strchr(cp, ' ')) {
801     cp = strchr(cp, ' ') + 1;
802     if (cp && *cp && vendor_version)
803       *vendor_version = strdup(cp);
804   }
805
806   return TRUE;
807 }
808
809 /* Converts version string x.x into number representation. */
810
811 SilcUInt32 silc_version_to_num(const char *version)
812 {
813   int maj = 0, min = 0;
814   char *cp, buf[32];
815
816   if (!version)
817     return 0;
818
819   cp = (char *)version;
820   maj = atoi(cp);
821   cp = strchr(cp, '.');
822   if (cp)
823     min = atoi(cp + 1);
824
825   memset(buf, 0, sizeof(buf));
826   silc_snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
827   return (SilcUInt32)atoi(buf);
828 }
829
830 /* Parses mode mask and returns the mode as string. */
831
832 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
833 {
834   char string[100];
835
836   if (!mode)
837     return NULL;
838
839   memset(string, 0, sizeof(string));
840
841   if (mode & SILC_CHANNEL_MODE_PRIVATE)
842     strncat(string, "p", 1);
843
844   if (mode & SILC_CHANNEL_MODE_SECRET)
845     strncat(string, "s", 1);
846
847   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
848     strncat(string, "k", 1);
849
850   if (mode & SILC_CHANNEL_MODE_INVITE)
851     strncat(string, "i", 1);
852
853   if (mode & SILC_CHANNEL_MODE_TOPIC)
854     strncat(string, "t", 1);
855
856   if (mode & SILC_CHANNEL_MODE_ULIMIT)
857     strncat(string, "l", 1);
858
859   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
860     strncat(string, "a", 1);
861
862   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
863     strncat(string, "f", 1);
864
865   if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
866     strncat(string, "C", 1);
867
868   if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
869     strncat(string, "m", 1);
870
871   if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
872     strncat(string, "M", 1);
873
874   if (mode & SILC_CHANNEL_MODE_CIPHER)
875     strncat(string, "c", 1);
876
877   if (mode & SILC_CHANNEL_MODE_HMAC)
878     strncat(string, "h", 1);
879
880   if (mode & SILC_CHANNEL_MODE_CIPHER) {
881     if (strlen(cipher) + strlen(string) + 1< sizeof(string)) {
882       strncat(string, " ", 1);
883       strncat(string, cipher, strlen(cipher));
884     }
885   }
886
887   if (mode & SILC_CHANNEL_MODE_HMAC) {
888     if (strlen(hmac) + strlen(string) + 1< sizeof(string)) {
889       strncat(string, " ", 1);
890       strncat(string, hmac, strlen(hmac));
891     }
892   }
893
894   /* Rest of mode is ignored */
895
896   return strdup(string);
897 }
898
899 /* Parses channel user mode mask and returns te mode as string */
900
901 char *silc_client_chumode(SilcUInt32 mode)
902 {
903   char string[64];
904
905   if (!mode)
906     return NULL;
907
908   memset(string, 0, sizeof(string));
909
910   if (mode & SILC_CHANNEL_UMODE_CHANFO)
911     strncat(string, "f", 1);
912
913   if (mode & SILC_CHANNEL_UMODE_CHANOP)
914     strncat(string, "o", 1);
915
916   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
917     strncat(string, "b", 1);
918
919   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
920     strncat(string, "u", 1);
921
922   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
923     strncat(string, "r", 1);
924
925   if (mode & SILC_CHANNEL_UMODE_QUIET)
926     strncat(string, "q", 1);
927
928   return strdup(string);
929 }
930
931 /* Parses channel user mode and returns it as special mode character. */
932
933 char *silc_client_chumode_char(SilcUInt32 mode)
934 {
935   char string[64];
936
937   if (!mode)
938     return NULL;
939
940   memset(string, 0, sizeof(string));
941
942   if (mode & SILC_CHANNEL_UMODE_CHANFO)
943     strncat(string, "*", 1);
944
945   if (mode & SILC_CHANNEL_UMODE_CHANOP)
946     strncat(string, "@", 1);
947
948   if (mode & SILC_CHANNEL_UMODE_QUIET)
949     strncat(string, "&", 1);
950
951   return strdup(string);
952 }
953
954 /* Renders ID to suitable to print for example to log file. */
955
956 static char rid[256];
957 #define _PUT_STRING(__d__, __s__)                                       \
958 do {                                                                    \
959   int __sp = sizeof(__d__) - 1 - strlen(__d__);                         \
960   if (__sp < strlen(__s__)) {                                           \
961     if (__sp)                                                           \
962       strncat(__d__, __s__, (sizeof(__d__) - 1) - strlen(__d__));       \
963   } else {                                                              \
964     strncat(__d__, __s__, strlen(__s__));                               \
965   }                                                                     \
966 } while(0)
967
968 char *silc_id_render(void *id, SilcIdType id_type)
969 {
970   char tmp[100];
971   unsigned char tmps[2];
972   char *cp;
973
974   memset(rid, 0, sizeof(rid));
975   switch(id_type) {
976   case SILC_ID_SERVER:
977     {
978       SilcServerID *server_id = (SilcServerID *)id;
979       if (server_id->ip.data_len > 4) {
980 #ifdef HAVE_IPV6
981         struct sockaddr_in6 ipv6;
982         memset(&ipv6, 0, sizeof(ipv6));
983         ipv6.sin6_family = AF_INET6;
984         memmove(&ipv6.sin6_addr, server_id->ip.data, sizeof(ipv6.sin6_addr));
985         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
986                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
987           _PUT_STRING(rid, tmp);
988 #endif
989       } else {
990         struct in_addr ipv4;
991         memmove(&ipv4.s_addr, server_id->ip.data, 4);
992         cp = inet_ntoa(ipv4);
993         if (cp)
994           _PUT_STRING(rid, cp);
995       }
996
997       memset(tmp, 0, sizeof(tmp));
998       silc_snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(server_id->port));
999       _PUT_STRING(rid, tmp);
1000       SILC_PUT16_MSB(server_id->rnd, tmps);
1001       memset(tmp, 0, sizeof(tmp));
1002       silc_snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
1003       _PUT_STRING(rid, tmp);
1004     }
1005     break;
1006   case SILC_ID_CLIENT:
1007     {
1008       SilcClientID *client_id = (SilcClientID *)id;
1009       if (client_id->ip.data_len > 4) {
1010 #ifdef HAVE_IPV6
1011         struct sockaddr_in6 ipv6;
1012         memset(&ipv6, 0, sizeof(ipv6));
1013         ipv6.sin6_family = AF_INET6;
1014         memmove(&ipv6.sin6_addr, client_id->ip.data, sizeof(ipv6.sin6_addr));
1015         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
1016                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
1017           _PUT_STRING(rid, tmp);
1018 #endif
1019       } else {
1020         struct in_addr ipv4;
1021         memmove(&ipv4.s_addr, client_id->ip.data, 4);
1022         cp = inet_ntoa(ipv4);
1023         if (cp)
1024           _PUT_STRING(rid, cp);
1025       }
1026
1027       memset(tmp, 0, sizeof(tmp));
1028       silc_snprintf(tmp, sizeof(tmp) - 1, ",%02x,", client_id->rnd);
1029       _PUT_STRING(rid, tmp);
1030       memset(tmp, 0, sizeof(tmp));
1031       silc_snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x %02x %02x...]",
1032                client_id->hash[0], client_id->hash[1],
1033                client_id->hash[2], client_id->hash[3]);
1034       _PUT_STRING(rid, tmp);
1035     }
1036     break;
1037   case SILC_ID_CHANNEL:
1038     {
1039       SilcChannelID *channel_id = (SilcChannelID *)id;
1040       if (channel_id->ip.data_len > 4) {
1041 #ifdef HAVE_IPV6
1042         struct sockaddr_in6 ipv6;
1043         memset(&ipv6, 0, sizeof(ipv6));
1044         ipv6.sin6_family = AF_INET6;
1045         memmove(&ipv6.sin6_addr, channel_id->ip.data, sizeof(ipv6.sin6_addr));
1046         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
1047                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
1048           _PUT_STRING(rid, tmp);
1049 #endif
1050       } else {
1051         struct in_addr ipv4;
1052         memmove(&ipv4.s_addr, channel_id->ip.data, 4);
1053         cp = inet_ntoa(ipv4);
1054         if (cp)
1055           _PUT_STRING(rid, cp);
1056       }
1057
1058       memset(tmp, 0, sizeof(tmp));
1059       silc_snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(channel_id->port));
1060       _PUT_STRING(rid, tmp);
1061       SILC_PUT16_MSB(channel_id->rnd, tmps);
1062       memset(tmp, 0, sizeof(tmp));
1063       silc_snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
1064       _PUT_STRING(rid, tmp);
1065     }
1066     break;
1067   }
1068
1069   return rid;
1070 }
1071 #undef _PUT_STRING