2 protocol.c -- handle the meta-protocol
3 Copyright (C) 1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>,
4 2000 Guus Sliepen <guus@sliepen.warande.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 $Id: protocol.c,v 1.28.4.67 2000/11/25 13:33:33 guus Exp $
25 #include <sys/types.h>
30 #include <sys/socket.h>
38 #include <netinet/in.h>
40 #ifdef HAVE_OPENSSL_SHA_H
41 # include <openssl/sha.h>
46 #ifdef HAVE_OPENSSL_RAND_H
47 # include <openssl/rand.h>
52 #ifdef HAVE_OPENSSL_EVP_H
53 # include <openssl/evp.h>
64 #include "connection.h"
68 int check_id(char *id)
72 for (i = 0; i < strlen(id); i++)
73 if(!isalnum(id[i]) && id[i] != '_')
79 /* Generic request routines - takes care of logging and error detection as well */
81 int send_request(connection_t *cl, const char *format, ...)
84 char buffer[MAXBUFSIZE];
88 /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
89 and there is a limit on the input buffer anyway */
91 va_start(args, format);
92 len = vsnprintf(buffer, MAXBUFSIZE, format, args);
93 request = va_arg(args, int);
96 if(len < 0 || len > MAXBUFSIZE-1)
98 syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
104 if(debug_lvl >= DEBUG_PROTOCOL)
105 syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
108 return send_meta(cl, buffer, len);
111 int receive_request(connection_t *cl)
115 if(sscanf(cl->buffer, "%d", &request) == 1)
117 if((request < 0) || (request > 255) || (request_handlers[request] == NULL))
119 syslog(LOG_ERR, _("Unknown request from %s (%s)"),
120 cl->name, cl->hostname);
125 if(debug_lvl >= DEBUG_PROTOCOL)
126 syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
127 request_name[request], cl->name, cl->hostname);
130 if((cl->allow_request != ALL) && (cl->allow_request != request))
132 syslog(LOG_ERR, _("Unauthorized request from %s (%s)"), cl->name, cl->hostname);
136 if(request_handlers[request](cl))
137 /* Something went wrong. Probably scriptkiddies. Terminate. */
139 syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
140 request_name[request], cl->name, cl->hostname);
146 syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
147 cl->name, cl->hostname);
154 /* Connection protocol:
163 ---------------------------------------
166 ---------------------------------------
169 ---------------------------------------
175 (E) Encrypted with symmetric cipher.
177 Part of the challenge is directly used to set the symmetric cipher key and the initial vector.
178 Since a man-in-the-middle cannot decrypt the RSA challenges, this means that he cannot get or
179 forge the key for the symmetric cipher.
182 int send_id(connection_t *cl)
185 cl->allow_request = CHALLENGE;
187 return send_request(cl, "%d %s %d %lx %hd", ID, myself->name, myself->protocol_version, myself->options, myself->port);
190 int id_h(connection_t *cl)
194 char name[MAX_STRING_SIZE];
196 if(sscanf(cl->buffer, "%*d "MAX_STRING" %d %lx %hd", name, &cl->protocol_version, &cl->options, &cl->port) != 4)
198 syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
202 /* Check if version matches */
204 if(cl->protocol_version != myself->protocol_version)
206 syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
207 cl->name, cl->hostname, cl->protocol_version);
211 /* Check if identity is a valid name */
215 syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
219 /* Copy string to cl */
221 cl->name = xstrdup(name);
223 /* Load information about peer */
225 if(read_host_config(cl))
227 syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
231 /* First check if the host we connected to is already in our
232 connection list. If so, we are probably making a loop, which
236 if(cl->status.outgoing)
238 if((old = lookup_id(cl->name)))
240 if(debug_lvl >= DEBUG_CONNECTIONS)
241 syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
242 cl->status.outgoing = 0;
243 old->status.outgoing = 1;
244 terminate_connection(cl);
249 /* Now we can add the name to the id tree */
253 /* Read in the public key, so that we can send a challenge */
255 if((cfg = get_config_val(cl->config, config_publickey)))
257 cl->rsa_key = RSA_new();
258 BN_hex2bn(&cl->rsa_key->n, cfg->data.ptr);
259 BN_hex2bn(&cl->rsa_key->e, "FFFF");
263 syslog(LOG_ERR, _("No public key known for %s (%s)"), cl->name, cl->hostname);
267 return send_challenge(cl);
270 int send_challenge(connection_t *cl)
275 len = RSA_size(cl->rsa_key);
277 /* Allocate buffers for the challenge */
279 buffer = xmalloc(len*2+1);
282 free(cl->hischallenge);
284 cl->hischallenge = xmalloc(len);
286 /* Copy random data to the buffer */
288 RAND_bytes(cl->hischallenge, len);
290 cl->hischallenge[0] &= 0x7F; /* Somehow if the first byte is more than 0xD0 or something like that, decryption fails... */
292 if(debug_lvl >= DEBUG_SCARY_THINGS)
294 bin2hex(cl->hischallenge, buffer, len);
295 buffer[len*2] = '\0';
296 syslog(LOG_DEBUG, _("Generated random challenge (unencrypted): %s"), buffer);
299 /* Encrypt the random data */
301 if(RSA_public_encrypt(len, cl->hischallenge, buffer, cl->rsa_key, RSA_NO_PADDING) != len) /* NO_PADDING because the message size equals the RSA key size and it is totally random */
303 syslog(LOG_ERR, _("Error during encryption of challenge for %s (%s)"), cl->name, cl->hostname);
308 /* Convert the encrypted random data to a hexadecimal formatted string */
310 bin2hex(buffer, buffer, len);
311 buffer[len*2] = '\0';
313 /* Send the challenge */
315 cl->allow_request = CHAL_REPLY;
316 x = send_request(cl, "%d %s", CHALLENGE, buffer);
322 int challenge_h(connection_t *cl)
324 char buffer[MAX_STRING_SIZE];
327 if(sscanf(cl->buffer, "%*d "MAX_STRING, buffer) != 1)
329 syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
333 len = RSA_size(myself->rsa_key);
335 /* Check if the length of the challenge is all right */
337 if(strlen(buffer) != len*2)
339 syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
343 /* Allocate buffers for the challenge */
346 cl->mychallenge = xmalloc(len);
348 /* Convert the challenge from hexadecimal back to binary */
350 hex2bin(buffer,buffer,len);
352 /* Decrypt the challenge */
354 if(RSA_private_decrypt(len, buffer, cl->mychallenge, myself->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
356 syslog(LOG_ERR, _("Error during encryption of challenge for %s (%s)"), cl->name, cl->hostname);
360 if(debug_lvl >= DEBUG_SCARY_THINGS)
362 bin2hex(cl->mychallenge, buffer, len);
363 buffer[len*2] = '\0';
364 syslog(LOG_DEBUG, _("Received random challenge (unencrypted): %s"), buffer);
367 /* Rest is done by send_chal_reply() */
369 return send_chal_reply(cl);
372 int send_chal_reply(connection_t *cl)
374 char hash[SHA_DIGEST_LENGTH*2+1];
378 syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
382 /* Calculate the hash from the challenge we received */
384 SHA1(cl->mychallenge, RSA_size(myself->rsa_key), hash);
386 /* Convert the hash to a hexadecimal formatted string */
388 bin2hex(hash,hash,SHA_DIGEST_LENGTH);
389 hash[SHA_DIGEST_LENGTH*2] = '\0';
393 if(cl->status.outgoing)
394 cl->allow_request = ID;
396 cl->allow_request = METAKEY;
399 return send_request(cl, "%d %s", CHAL_REPLY, hash);
402 int chal_reply_h(connection_t *cl)
404 char hishash[MAX_STRING_SIZE];
405 char myhash[SHA_DIGEST_LENGTH];
407 if(sscanf(cl->buffer, "%*d "MAX_STRING, hishash) != 1)
409 syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
413 /* Check if the length of the hash is all right */
415 if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
417 syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
421 /* Convert the hash to binary format */
423 hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
425 /* Calculate the hash from the challenge we sent */
427 SHA1(cl->hischallenge, RSA_size(cl->rsa_key), myhash);
429 /* Verify the incoming hash with the calculated hash */
431 if(memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
433 syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
434 if(debug_lvl >= DEBUG_SCARY_THINGS)
436 bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
437 hishash[SHA_DIGEST_LENGTH*2] = '\0';
438 syslog(LOG_DEBUG, _("Expected challenge reply: %s"), hishash);
444 /* Identity has now been positively verified.
445 If we are accepting this new connection, then send our identity,
446 if we are making this connecting, acknowledge.
449 if(cl->status.outgoing)
450 return send_metakey(cl);
455 int send_metakey(connection_t *cl)
460 len = RSA_size(cl->rsa_key);
462 /* Allocate buffers for the meta key */
464 buffer = xmalloc(len*2+1);
466 if(!cl->cipher_outkey)
467 cl->cipher_outkey = xmalloc(len);
469 if(!cl->cipher_outctx)
470 cl->cipher_outctx = xmalloc(sizeof(*cl->cipher_outctx));
472 /* Copy random data to the buffer */
474 RAND_bytes(cl->cipher_outkey, len);
476 cl->cipher_outkey[0] &= 0x7F; /* FIXME: Somehow if the first byte is more than 0xD0 or something like that, decryption fails... */
478 if(debug_lvl >= DEBUG_SCARY_THINGS)
480 bin2hex(cl->cipher_outkey, buffer, len);
481 buffer[len*2] = '\0';
482 syslog(LOG_DEBUG, _("Generated random meta key (unencrypted): %s"), buffer);
485 /* Encrypt the random data */
487 if(RSA_public_encrypt(len, cl->cipher_outkey, buffer, cl->rsa_key, RSA_NO_PADDING) != len) /* NO_PADDING because the message size equals the RSA key size and it is totally random */
489 syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), cl->name, cl->hostname);
494 /* Convert the encrypted random data to a hexadecimal formatted string */
496 bin2hex(buffer, buffer, len);
497 buffer[len*2] = '\0';
499 /* Send the meta key */
501 if(cl->status.outgoing)
502 cl->allow_request = METAKEY;
504 cl->allow_request = ACK;
506 x = send_request(cl, "%d %s", METAKEY, buffer);
509 EVP_EncryptInit(cl->cipher_outctx, EVP_bf_cfb(), cl->cipher_outkey, cl->cipher_outkey + EVP_bf_cfb()->key_len);
514 int metakey_h(connection_t *cl)
516 char buffer[MAX_STRING_SIZE];
519 if(sscanf(cl->buffer, "%*d "MAX_STRING, buffer) != 1)
521 syslog(LOG_ERR, _("Got bad METAKEY from %s (%s)"), cl->name, cl->hostname);
525 len = RSA_size(myself->rsa_key);
527 /* Check if the length of the meta key is all right */
529 if(strlen(buffer) != len*2)
531 syslog(LOG_ERR, _("Intruder: wrong meta key length from %s (%s)"), cl->name, cl->hostname);
535 /* Allocate buffers for the meta key */
537 if(!cl->cipher_inkey)
538 cl->cipher_inkey = xmalloc(len);
540 if(!cl->cipher_inctx)
541 cl->cipher_inctx = xmalloc(sizeof(*cl->cipher_inctx));
543 /* Convert the challenge from hexadecimal back to binary */
545 hex2bin(buffer,buffer,len);
547 /* Decrypt the meta key */
549 if(RSA_private_decrypt(len, buffer, cl->cipher_inkey, myself->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
551 syslog(LOG_ERR, _("Error during encryption of meta key for %s (%s)"), cl->name, cl->hostname);
555 if(debug_lvl >= DEBUG_SCARY_THINGS)
557 bin2hex(cl->cipher_inkey, buffer, len);
558 buffer[len*2] = '\0';
559 syslog(LOG_DEBUG, _("Received random meta key (unencrypted): %s"), buffer);
562 EVP_DecryptInit(cl->cipher_inctx, EVP_bf_cfb(), cl->cipher_inkey, cl->cipher_inkey + EVP_bf_cfb()->key_len);
565 if(cl->status.outgoing)
568 return send_metakey(cl);
571 int send_ack(connection_t *cl)
575 if(cl->status.outgoing)
576 cl->allow_request = ACK;
578 x = send_request(cl, "%d", ACK);
579 cl->status.encryptout = 1;
584 int ack_h(connection_t *cl)
586 connection_t *old, *p;
590 /* Okay, before we active the connection, we check if there is another entry
591 in the connection list with the same name. If so, it presumably is an
592 old connection that has timed out but we don't know it yet.
595 while((old = lookup_id(cl->name)))
597 if(debug_lvl >= DEBUG_CONNECTIONS)
598 syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
599 cl->name, old->hostname, cl->hostname);
601 terminate_connection(old);
604 /* Activate this connection */
606 cl->allow_request = ALL;
607 cl->status.active = 1;
608 cl->status.decryptin = 1;
610 cl->cipher_pkttype = EVP_bf_cfb();
611 cl->cipher_pktkeylength = cl->cipher_pkttype->key_len + cl->cipher_pkttype->iv_len;
613 if(debug_lvl >= DEBUG_CONNECTIONS)
614 syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
617 if(!cl->status.outgoing)
620 /* Send him our subnets */
622 RBL_FOREACH(myself->subnet_tree, rbl)
624 subnet = (subnet_t *)rbl->data;
625 send_add_subnet(cl, subnet);
627 /* And send him all the hosts and their subnets we know... */
629 RBL_FOREACH(connection_tree, rbl)
631 p = (connection_t *)rbl->data;
633 if(p != cl && p->status.active)
635 /* Notify others of this connection */
638 send_add_host(p, cl);
640 /* Notify new connection of everything we know */
642 send_add_host(cl, p);
644 RBL_FOREACH(p->subnet_tree, rbl2)
646 subnet = (subnet_t *)rbl2->data;
647 send_add_subnet(cl, subnet);
655 /* Address and subnet information exchange */
657 int send_add_subnet(connection_t *cl, subnet_t *subnet)
662 x = send_request(cl, "%d %s %s", ADD_SUBNET,
663 subnet->owner->name, netstr = net2str(subnet));
669 int add_subnet_h(connection_t *cl)
671 char subnetstr[MAX_STRING_SIZE];
672 char name[MAX_STRING_SIZE];
673 connection_t *owner, *p;
677 if(sscanf(cl->buffer, "%*d "MAX_STRING" "MAX_STRING, name, subnetstr) != 2)
679 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
683 /* Check if owner name is a valid */
687 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
691 /* Check if subnet string is valid */
693 if(!(subnet = str2net(subnetstr)))
695 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
699 /* Check if somebody tries to add a subnet of ourself */
701 if(!strcmp(name, myself->name))
703 syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
704 cl->name, cl->hostname);
709 /* Check if the owner of the new subnet is in the connection list */
711 if(!(owner = lookup_id(name)))
713 syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
714 name, cl->name, cl->hostname);
718 /* If everything is correct, add the subnet to the list of the owner */
720 subnet_add(owner, subnet);
724 RBL_FOREACH(connection_tree, rbl)
726 p = (connection_t *)rbl->data;
727 if(p->status.meta && p->status.active && p!= cl)
728 send_add_subnet(p, subnet);
734 int send_del_subnet(connection_t *cl, subnet_t *subnet)
739 netstr = net2str(subnet);
740 x = send_request(cl, "%d %s %s", DEL_SUBNET, subnet->owner->name, netstr);
746 int del_subnet_h(connection_t *cl)
748 char subnetstr[MAX_STRING_SIZE];
749 char name[MAX_STRING_SIZE];
750 connection_t *owner, *p;
754 if(sscanf(cl->buffer, "%*d "MAX_STRING" "MAX_STRING, name, subnetstr) != 3)
756 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
760 /* Check if owner name is a valid */
764 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
768 /* Check if subnet string is valid */
770 if(!(subnet = str2net(subnetstr)))
772 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
778 /* Check if somebody tries to add a subnet of ourself */
780 if(!strcmp(name, myself->name))
782 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
783 cl->name, cl->hostname);
788 /* Check if the owner of the new subnet is in the connection list */
790 if(!(owner = lookup_id(name)))
792 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
793 name, cl->name, cl->hostname);
797 /* If everything is correct, delete the subnet from the list of the owner */
803 RBL_FOREACH(connection_tree, rbl)
805 p = (connection_t *)rbl->data;
806 if(p->status.meta && p->status.active && p!= cl)
807 send_del_subnet(p, subnet);
813 /* New and closed connections notification */
815 int send_add_host(connection_t *cl, connection_t *other)
818 return send_request(cl, "%d %s %lx:%d %lx", ADD_HOST,
819 other->name, other->address, other->port, other->options);
822 int add_host_h(connection_t *cl)
824 connection_t *old, *new, *p;
825 char name[MAX_STRING_SIZE];
828 new = new_connection();
830 if(sscanf(cl->buffer, "%*d "MAX_STRING" %lx:%d %lx", name, &new->address, &new->port, &new->options) != 4)
832 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
836 /* Check if identity is a valid name */
840 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
841 free_connection(new);
845 /* Check if somebody tries to add ourself */
847 if(!strcmp(name, myself->name))
849 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
851 free_connection(new);
855 /* Fill in more of the new connection structure */
857 new->hostname = hostlookup(htonl(new->address));
859 /* Check if the new host already exists in the connnection list */
861 if((old = lookup_id(name)))
863 if((new->address == old->address) && (new->port == old->port))
865 if(debug_lvl >= DEBUG_CONNECTIONS)
866 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
867 old->name, old->hostname, name, new->hostname);
868 free_connection(new);
873 if(debug_lvl >= DEBUG_CONNECTIONS)
874 syslog(LOG_NOTICE, _("Removing old entry for %s (%s) in favour of new connection"),
875 old->name, old->hostname);
877 terminate_connection(old);
881 /* Hook it up into the connection */
883 new->name = xstrdup(name);
887 /* Tell the rest about the new host */
889 RBL_FOREACH(connection_tree, rbl)
891 p = (connection_t *)rbl->data;
892 if(p->status.meta && p->status.active && p!=cl)
893 send_add_host(p, new);
896 /* Fill in rest of connection structure */
899 new->status.active = 1;
900 new->cipher_pkttype = EVP_bf_cfb();
901 new->cipher_pktkeylength = cl->cipher_pkttype->key_len + cl->cipher_pkttype->iv_len;
906 int send_del_host(connection_t *cl, connection_t *other)
909 return send_request(cl, "%d %s %lx:%d %lx", DEL_HOST,
910 other->name, other->address, other->port, other->options);
913 int del_host_h(connection_t *cl)
915 char name[MAX_STRING_SIZE];
919 connection_t *old, *p;
922 if(sscanf(cl->buffer, "%*d "MAX_STRING" %lx:%d %lx", name, &address, &port, &options) != 4)
924 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
925 cl->name, cl->hostname);
929 /* Check if identity is a valid name */
933 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
937 /* Check if somebody tries to delete ourself */
939 if(!strcmp(name, myself->name))
941 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
942 cl->name, cl->hostname);
947 /* Check if the new host already exists in the connnection list */
949 if(!(old = lookup_id(name)))
951 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
952 name, cl->name, cl->hostname);
956 /* Check if the rest matches */
958 if(address!=old->address || port!=old->port || options!=old->options || cl!=old->nexthop)
960 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
964 /* Ok, since EVERYTHING seems to check out all right, delete it */
966 old->status.active = 0;
967 terminate_connection(old);
969 /* Tell the rest about the new host */
971 RBL_FOREACH(connection_tree, rbl)
973 p = (connection_t *)rbl->data;
974 if(p->status.meta && p->status.active && p!=cl)
975 send_del_host(p, old);
981 /* Status and error notification routines */
983 int send_status(connection_t *cl, int statusno, char *statusstring)
987 statusstring = status_text[statusno];
989 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
992 int status_h(connection_t *cl)
995 char statusstring[MAX_STRING_SIZE];
997 if(sscanf(cl->buffer, "%*d %d "MAX_STRING, &statusno, statusstring) != 2)
999 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
1000 cl->name, cl->hostname);
1004 if(debug_lvl >= DEBUG_STATUS)
1006 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
1007 cl->name, cl->hostname, status_text[statusno], statusstring);
1014 int send_error(connection_t *cl, int errno, char *errstring)
1018 errstring = strerror(errno);
1019 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
1022 int error_h(connection_t *cl)
1025 char errorstring[MAX_STRING_SIZE];
1027 if(sscanf(cl->buffer, "%*d %d "MAX_STRING, &errno, errorstring) != 2)
1029 syslog(LOG_ERR, _("Got bad ERROR from %s (%s)"),
1030 cl->name, cl->hostname);
1034 if(debug_lvl >= DEBUG_ERROR)
1036 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
1037 cl->name, cl->hostname, strerror(errno), errorstring);
1040 terminate_connection(cl);
1045 int send_termreq(connection_t *cl)
1048 return send_request(cl, "%d", TERMREQ);
1051 int termreq_h(connection_t *cl)
1054 terminate_connection(cl);
1059 /* Keepalive routines - FIXME: needs a closer look */
1061 int send_ping(connection_t *cl)
1064 cl->status.pinged = 1;
1065 cl->last_ping_time = time(NULL);
1067 return send_request(cl, "%d", PING);
1070 int ping_h(connection_t *cl)
1073 return send_pong(cl);
1076 int send_pong(connection_t *cl)
1079 return send_request(cl, "%d", PONG);
1082 int pong_h(connection_t *cl)
1085 cl->status.pinged = 0;
1092 int send_key_changed(connection_t *from, connection_t *cl)
1097 RBL_FOREACH(connection_tree, rbl)
1099 p = (connection_t *)rbl->data;
1100 if(p != cl && p->status.meta && p->status.active)
1101 send_request(p, "%d %s", KEY_CHANGED, from->name);
1107 int key_changed_h(connection_t *cl)
1109 char from_id[MAX_STRING_SIZE];
1112 if(sscanf(cl->buffer, "%*d "MAX_STRING, from_id) != 1)
1114 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
1115 cl->name, cl->hostname);
1119 if(!(from = lookup_id(from_id)))
1121 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
1122 cl->name, cl->hostname, from_id);
1126 from->status.validkey = 0;
1127 from->status.waitingforkey = 0;
1129 send_key_changed(from, cl);
1134 int send_req_key(connection_t *from, connection_t *to)
1137 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
1138 from->name, to->name);
1141 int req_key_h(connection_t *cl)
1143 char from_id[MAX_STRING_SIZE];
1144 char to_id[MAX_STRING_SIZE];
1145 connection_t *from, *to;
1148 if(sscanf(cl->buffer, "%*d "MAX_STRING" "MAX_STRING, from_id, to_id) != 2)
1150 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
1151 cl->name, cl->hostname);
1155 if(!(from = lookup_id(from_id)))
1157 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
1158 cl->name, cl->hostname, from_id);
1162 /* Check if this key request is for us */
1164 if(!strcmp(to_id, myself->name))
1166 bin2hex(myself->cipher_pktkey, pktkey, myself->cipher_pktkeylength);
1167 pktkey[myself->cipher_pktkeylength*2] = '\0';
1168 send_ans_key(myself, from, pktkey);
1172 if(!(to = lookup_id(to_id)))
1174 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
1175 cl->name, cl->hostname, to_id);
1179 if(to->status.validkey) /* Proxy keys */
1181 bin2hex(to->cipher_pktkey, pktkey, to->cipher_pktkeylength);
1182 pktkey[to->cipher_pktkeylength*2] = '\0';
1183 send_ans_key(to, from, pktkey);
1186 send_req_key(from, to);
1193 int send_ans_key(connection_t *from, connection_t *to, char *pktkey)
1196 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
1197 from->name, to->name, pktkey);
1200 int ans_key_h(connection_t *cl)
1202 char from_id[MAX_STRING_SIZE];
1203 char to_id[MAX_STRING_SIZE];
1204 char pktkey[MAX_STRING_SIZE];
1206 connection_t *from, *to;
1208 if(sscanf(cl->buffer, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING, from_id, to_id, pktkey) != 3)
1210 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
1211 cl->name, cl->hostname);
1215 if(!(from = lookup_id(from_id)))
1217 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
1218 cl->name, cl->hostname, from_id);
1222 /* Check correctness of packet key */
1224 keylength = strlen(pktkey);
1226 if(keylength != from->cipher_pktkeylength*2)
1228 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key length"),
1229 cl->name, cl->hostname, from->name);
1233 /* Forward it if necessary */
1235 if(strcmp(to_id, myself->name))
1237 if(!(to = lookup_id(to_id)))
1239 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1240 cl->name, cl->hostname, to_id);
1243 send_ans_key(from, to, pktkey);
1246 /* Update our copy of the origin's packet key */
1248 if(from->cipher_pktkey)
1249 free(from->cipher_pktkey);
1251 from->cipher_pktkey = xstrdup(pktkey);
1253 hex2bin(from->cipher_pktkey, from->cipher_pktkey, keylength);
1254 from->cipher_pktkey[keylength] = '\0';
1256 from->status.validkey = 1;
1257 from->status.waitingforkey = 0;
1262 /* Jumptable for the request handlers */
1264 int (*request_handlers[])(connection_t*) = {
1265 id_h, challenge_h, chal_reply_h, metakey_h, ack_h,
1266 status_h, error_h, termreq_h,
1268 add_host_h, del_host_h,
1269 add_subnet_h, del_subnet_h,
1270 key_changed_h, req_key_h, ans_key_h,
1275 char (*request_name[]) = {
1276 "ID", "CHALLENGE", "CHAL_REPLY", "METAKEY", "ACK",
1277 "STATUS", "ERROR", "TERMREQ",
1279 "ADD_HOST", "DEL_HOST",
1280 "ADD_SUBNET", "DEL_SUBNET",
1281 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",
1284 /* Status strings */
1286 char (*status_text[]) = {
1292 char (*error_text[]) = {