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.35 2000/09/22 16:20:07 guus Exp $
25 #include <sys/types.h>
30 #include <sys/socket.h>
37 #include <netinet/in.h>
39 #include <openssl/sha.h>
49 int check_id(char *id)
53 for (i = 0; i < strlen(id); i++)
55 if(!isalpha(id[i]) && id[i] != '_')
64 /* Generic outgoing request routine - takes care of logging and error detection as well */
66 int send_request(conn_list_t *cl, const char *format, int request, /*args*/ ...)
69 char buffer[MAXBUFSIZE+1];
73 /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
74 and there is a limit on the input buffer anyway */
76 va_start(args, request);
77 len = vsnprintf(buffer, MAXBUFSIZE+1, format, args);
80 if(len < 0 || len > MAXBUFSIZE)
82 syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
86 if(debug_lvl >= DEBUG_PROTOCOL)
87 syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
89 return send_meta(cl, buffer, length);
93 int send_meta(conn_list_t *cl, const char *buffer, int length)
96 if(debug_lvl >= DEBUG_META)
97 syslog(LOG_DEBUG, _("Sending %d bytes of metadata to %s (%s): %s"), int length,
98 cl->name, cl->hostname, buffer);
100 if(cl->status.encryptin)
102 /* FIXME: Do encryption */
105 if(write(cl->meta_socket, buffer, length) < 0)
107 syslog(LOG_ERR, _("Sending meta data to %s (%s) failed: %m"), cl->name, cl->hostname);
114 int broadcast_meta(conn_list_t *cl, const char *buffer, int length)
118 for(p = conn_list; p != NULL; p = p->next)
119 if(p != cl && p->status.meta && p->status.active)
120 send_meta(p, buffer, length);
125 /* Connection protocol:
134 ---------------------------------------
135 Any negotations about the meta protocol
136 encryption go here(u).
137 ---------------------------------------
140 ---------------------------------------
146 (E) Encrypted with symmetric cipher.
148 Part of the challenge is directly used to set the symmetric cipher key and the initial vector.
149 Since a man-in-the-middle cannot decrypt the RSA challenges, this means that he cannot get or
150 forge the key for the symmetric cipher.
153 int send_id(conn_list_t *cl)
156 return send_request(cl, "%d %s %d %lx", ID, myself->name, myself->protocol_version, myself->options);
159 int id_h(conn_list_t *cl)
163 if(sscanf(cl->buffer, "%*d %as %d %lx", &cl->name, &cl->protocol_version, &cl->options) != 3)
165 syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
169 /* Check if version matches */
171 if(cl->protocol_version != myself->protocol_version)
173 syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
174 cl->name, cl->hostname, cl->protocol_version);
178 /* Check if identity is a valid name */
180 if(!check_id(cl->name))
182 syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
186 /* Load information about peer */
190 syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
195 /* First check if the host we connected to is already in our
196 connection list. If so, we are probably making a loop, which
200 if(cl->status.outgoing)
202 if((old = lookup_id(cl->name)))
204 if(debug_lvl > DEBUG_CONNECTIONS)
205 syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
206 cl->status.outgoing = 0;
207 old->status.outgoing = 1;
208 terminate_connection(cl);
213 /* Send a challenge to verify the identity */
215 cl->allow_request = CHAL_REPLY;
217 return send_challenge(cl);
220 int send_challenge(conn_list_t *cl)
222 char buffer[CHAL_LENGTH*2+1];
224 /* Allocate buffers for the challenge */
226 if(!cl->hischallenge)
227 cl->hischallenge = xmalloc(CHAL_LENGTH);
229 /* Copy random data to the buffer */
231 RAND_bytes(cl->hischallenge, CHAL_LENGTH);
233 /* Convert the random data to a hexadecimal formatted string */
235 bin2hex(cl->hischallenge,buffer,CHAL_LENGTH);
236 buffer[keylength*2] = '\0';
238 /* Send the challenge */
240 cl->allow_request = CHAL_REPLY;
242 return send_request(cl, "%d %s", CHALLENGE, buffer);
245 int challenge_h(conn_list_t *cl)
249 if(sscanf(cl->buffer, "%*d %as", &buffer) != 1)
251 syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
255 /* Check if the length of the challenge is all right */
257 if(strlen(buffer) != CHAL_LENGTH*2)
259 syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
264 /* Allocate buffers for the challenge */
267 cl->mychallenge = xmalloc(CHAL_LENGTH);
269 /* Convert the challenge from hexadecimal back to binary */
271 hex2bin(buffer,cl->mychallenge,CHAL_LENGTH);
274 /* Rest is done by send_chal_reply() */
276 return send_chal_reply(cl);
279 int send_chal_reply(conn_list_t *cl)
281 char hash[SHA_DIGEST_LENGTH*2+1];
285 syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
289 /* Calculate the hash from the challenge we received */
291 SHA1(cl->mychallenge, CHAL_LENGTH, hash);
293 /* Convert the hash to a hexadecimal formatted string */
295 bin2hex(hash,hash,SHA_DIGEST_LENGTH);
296 hash[SHA_DIGEST_LENGTH*2] = '\0';
300 if(cl->status.outgoing)
301 cl->allow_request = ID;
303 cl->allow_request = ACK;
306 return send_request(cl, "%d %s", CHAL_REPLY, hash);
309 int chal_reply_h(conn_list_t *cl)
312 char myhash[SHA_DIGEST_LENGTH];
314 if(sscanf(cl->buffer, "%*d %as", &hishash) != 2)
316 syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
321 /* Check if the length of the hash is all right */
323 if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
325 syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
330 /* Convert the hash to binary format */
332 hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
334 /* Calculate the hash from the challenge we sent */
336 SHA1(cl->hischallenge, CHAL_LENGTH, myhash);
338 /* Verify the incoming hash with the calculated hash */
340 if(!memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
342 syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
349 /* Identity has now been positively verified.
350 If we are accepting this new connection, then send our identity,
351 if we are making this connecting, acknowledge.
354 if(cl->status.outgoing)
356 cl->allow_request = ACK;
361 cl->allow_request = CHALLENGE;
366 int send_ack(conn_list_t *cl)
369 return send_request(cl, "%d", ACK);
372 int ack_h(conn_list_t *cl)
376 /* Okay, before we active the connection, we check if there is another entry
377 in the connection list with the same name. If so, it presumably is an
378 old connection that has timed out but we don't know it yet.
381 while((old = lookup_id(cl->name)))
383 if(debug_lvl > DEBUG_CONNECTIONS)
384 syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
385 cl->name, old->hostname, cl->hostname);
386 old->status.active = 0;
387 terminate_connection(old);
390 /* Activate this connection */
392 cl->allow_request = ALL;
393 cl->status.active = 1;
395 if(debug_lvl > DEBUG_CONNECTIONS)
396 syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
398 /* Exchange information about other tinc daemons */
400 notify_others(cl, NULL, send_add_host);
406 if(cl->status.outgoing)
412 /* Address and subnet information exchange */
414 int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
419 x = send_request(cl, "%d %s %s", ADD_SUBNET,
420 other->name, netstr = net2str(subnet));
426 int add_subnet_h(conn_list_t *cl)
431 subnet_t *subnet, *old;
433 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
435 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
436 free(name); free(subnetstr);
440 /* Check if owner name is a valid */
444 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
445 free(name); free(subnetstr);
449 /* Check if subnet string is valid */
451 if((subnet = str2net(subnetstr)) == -1)
453 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
454 free(name); free(subnetstr);
460 /* Check if somebody tries to add a subnet of ourself */
462 if(!strcmp(name, myself->name))
464 syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
465 cl->name, cl->hostname);
471 /* Check if the owner of the new subnet is in the connection list */
473 if(!(owner = lookup_id(name))
475 syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
476 name, cl->name, cl->hostname);
481 /* If everything is correct, add the subnet to the list of the owner */
483 return subnet_add(owner, subnet);
486 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
489 return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
492 int del_subnet_h(conn_list_t *cl)
497 subnet_t *subnet, *old;
499 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
501 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
502 free(name); free(subnetstr);
506 /* Check if owner name is a valid */
510 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
511 free(name); free(subnetstr);
515 /* Check if subnet string is valid */
517 if((subnet = str2net(subnetstr)) == -1)
519 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
520 free(name); free(subnetstr);
526 /* Check if somebody tries to add a subnet of ourself */
528 if(!strcmp(name, myself->name))
530 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
531 cl->name, cl->hostname);
537 /* Check if the owner of the new subnet is in the connection list */
539 if(!(owner = lookup_id(name))
541 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
542 name, cl->name, cl->hostname);
547 /* If everything is correct, add the subnet to the list of the owner */
549 return subnet_del(owner, subnet);
552 /* New and closed connections notification */
554 int send_add_host(conn_list_t *cl, conn_list_t *other)
557 return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
558 myself->name, other->name, other->real_ip, other->port, other->options);
561 int add_host_h(conn_list_t *cl)
564 conn_list_t *old, *new, *hisuplink;
566 new = new_conn_list();
568 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
570 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
574 /* Check if identity is a valid name */
576 if(!check_id(new->name) || !check_id(sender))
578 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
583 /* Check if somebody tries to add ourself */
585 if(!strcmp(new->name, myself->name))
587 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
593 /* We got an ADD_HOST from ourself!? */
595 if(!strcmp(sender, myself->name))
597 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
603 /* Lookup his uplink */
605 if(!(new->hisuplink = lookup_id(sender))
607 syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
608 sender, cl->name, cl->hostname);
615 /* Fill in more of the new conn_list structure */
617 new->hostname = hostlookup(htonl(new->real_ip));
619 /* Check if the new host already exists in the connnection list */
621 if((old = lookup_id(new->name)))
623 if((new->real_ip == old->real_ip) && (new->port == old->port))
625 if(debug_lvl > DEBUG_CONNECTIONS)
626 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
627 old->name, old->hostname, new->name, new->hostname);
632 if(debug_lvl > DEBUG_CONNECTIONS)
633 syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
634 old->name, old->hostname);
635 old->status.active = 0;
636 terminate_connection(old);
640 /* Fill in rest of conn_list structure */
643 new->status.active = 1;
645 /* Hook it up into the conn_list */
647 conn_list_add(conn_list, new);
649 /* Tell the rest about the new host */
651 notify_others(new, cl, send_add_host);
657 int send_del_host(conn_list_t *cl, conn_list_t *other)
660 return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
661 myself->name, other->name, other->real_ip, other->port, other->options);
664 int del_host_h(conn_list_t *cl)
671 conn_list_t *old, *hisuplink;
674 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
676 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
677 cl->name, cl->hostname);
681 /* Check if identity is a valid name */
683 if(!check_id(name) || !check_id(sender))
685 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
686 free(name); free(sender);
690 /* Check if somebody tries to delete ourself */
692 if(!strcmp(name, myself->name))
694 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
695 cl->name, cl->hostname);
696 free(name); free(sender);
701 /* We got an ADD_HOST from ourself!? */
703 if(!strcmp(sender, myself->name))
705 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
707 free(name); free(sender);
711 /* Lookup his uplink */
713 if(!(hisuplink = lookup_id(sender))
715 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
716 cl->name, cl->hostname, sender);
717 free(name); free(sender);
723 /* Check if the new host already exists in the connnection list */
725 if(!(old = lookup_id(name)))
727 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
728 name, cl->name, cl->hostname);
733 /* Check if the rest matches */
735 if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
737 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
741 /* Ok, since EVERYTHING seems to check out all right, delete it */
743 old->status.termreq = 1;
744 old->status.active = 0;
746 terminate_connection(old);
751 /* Status and error notification routines */
753 int send_status(conn_list_t *cl, int statusno, char *statusstring)
757 statusstring = status_text[statusno];
759 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
762 int status_h(conn_list_t *cl)
767 if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
769 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
770 cl->name, cl->hostname);
774 if(debug_lvl > DEBUG_STATUS)
776 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
777 cl->name, cl->hostname, status_text[statusno], statusstring);
785 int send_error(conn_list_t *cl, int errno, char *errstring)
789 errstring = strerror(errno);
790 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
793 int error_h(conn_list_t *cl)
798 if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
800 syslog(LOG_ERR, _("Got bad error from %s (%s)"),
801 cl->name, cl->hostname);
805 if(debug_lvl > DEBUG_error)
807 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
808 cl->name, cl->hostname, strerror(errno), errorstring);
812 cl->status.termreq = 1;
813 terminate_connection(cl);
818 int send_termreq(conn_list_t *cl)
821 return send_request(cl, "%d", TERMREQ);
824 int termreq_h(conn_list_t *cl)
827 cl->status.termreq = 1;
828 terminate_connection(cl);
833 /* Keepalive routines - FIXME: needs a closer look */
835 int send_ping(conn_list_t *cl)
837 cl->status.pinged = 1;
839 return send_request(cl, "%d", PING);
842 int ping_h(conn_list_t *cl)
845 return send_pong(cl);
848 int send_pong(conn_list_t *cl)
851 return send_request(cl, "%d", PONG);
854 int pong_h(conn_list_t *cl)
857 cl->status.got_pong = 1;
864 int send_key_changed(conn_list_t *from, conn_list_t *cl)
868 for(p = conn_list; p != NULL; p = p->next)
870 if(p!=cl && p->status.meta && p->status.active)
871 send_request(p, "%d %s", KEY_CHANGED,
878 int key_changed_h(conn_list_t *cl)
883 if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
885 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
886 cl->name, cl->hostname);
890 if(!(from = lookup_id(from_id)))
892 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
893 cl->name, cl->hostname, from_id);
900 from->status.validkey = 0;
901 from->status.waitingforkey = 0;
903 send_key_changed(from, cl);
908 int send_req_key(conn_list_t *from, conn_list_t *to)
911 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
912 from->name, to->name);
915 int req_key_h(conn_list_t *cl)
917 char *from_id, *to_id;
918 conn_list_t *from, *to;
920 if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
922 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
923 cl->name, cl->hostname);
927 if(!(from = lookup_id(from_id)))
929 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
930 cl->name, cl->hostname, from_id);
931 free(from_id); free(to_id);
935 /* Check if this key request is for us */
937 if(!strcmp(to_id, myself->name))
939 send_ans_key(myself, from, myself->datakey->key);
943 if(!(to = lookup_id(to_id)))
945 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
946 cl->name, cl->hostname, to_id);
947 free(from_id); free(to_id);
950 send_req_key(from, to);
953 free(from_id); free(to_id);
958 int send_ans_key(conn_list_t *from, conn_list_t *to, char *datakey)
961 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
962 from->name, to->name, datakey);
965 int ans_key_h(conn_list_t *cl)
967 char *from_id, *to_id, *datakey;
969 conn_list_t *from, *to;
971 if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &datakey) != 3)
973 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
974 cl->name, cl->hostname);
978 if(!(from = lookup_id(from_id)))
980 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
981 cl->name, cl->hostname, from_id);
982 free(from_id); free(to_id); free(datakey);
986 /* Check if this key request is for us */
988 if(!strcmp(to_id, myself->name))
990 /* It is for us, convert it to binary and set the key with it. */
992 keylength = strlen(datakey);
994 if((keylength%2) || (keylength <= 0))
996 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
997 cl->name, cl->hostname, from->name);
998 free(from_id); free(to_id); free(datakey);
1002 hex2bin(datakey, datakey, keylength);
1003 BF_set_key(cl->datakey, keylength, datakey);
1007 if(!(to = lookup_id(to_id)))
1009 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1010 cl->name, cl->hostname, to_id);
1011 free(from_id); free(to_id); free(datakey);
1014 send_ans_key(from, to, datakey);
1017 free(from_id); free(to_id); free(datakey);
1022 /* Jumptable for the request handlers */
1024 int (*request_handlers[])(conn_list_t*) = {
1025 id_h, challenge_h, chal_reply_h, ack_h,
1026 status_h, error_h, termreq_h,
1028 add_host_h, del_host_h,
1029 add_subnet_h, del_subnet_h,
1030 key_changed_h, req_key_h, ans_key_h,
1035 char (*request_name[]) = {
1036 "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1037 "STATUS", "ERROR", "TERMREQ",
1039 "ADD_HOST", "DEL_HOST",
1040 "ADD_SUBNET", "DEL_SUBNET",
1041 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",