3c596398e91013716a4f963e46cceebabc33ad38
[tinc] / src / protocol.c
1 /*
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>
5
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.
10
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.
15
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.
19
20     $Id: protocol.c,v 1.28.4.43 2000/10/20 15:34:37 guus Exp $
21 */
22
23 #include "config.h"
24
25 #include <sys/types.h>
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <sys/socket.h>
31 #include <unistd.h>
32 #include <stdio.h>
33
34 #include <utils.h>
35 #include <xalloc.h>
36
37 #include <netinet/in.h>
38
39 #include <openssl/sha.h>
40
41 #include "conf.h"
42 #include "encr.h"
43 #include "net.h"
44 #include "netutl.h"
45 #include "protocol.h"
46 #include "meta.h"
47
48 #include "system.h"
49
50 int check_id(char *id)
51 {
52   int i;
53
54   for (i = 0; i < strlen(id); i++)
55     if(!isalnum(id[i]) && id[i] != '_')
56       return -1;
57           
58   return 0;
59 }
60
61 /* Generic request routines - takes care of logging and error detection as well */
62
63 int send_request(conn_list_t *cl, const char *format, ...)
64 {
65   va_list args;
66   char buffer[MAXBUFSIZE];
67   int len, request;
68
69 cp
70   /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
71      and there is a limit on the input buffer anyway */
72
73   va_start(args, format);
74   len = vsnprintf(buffer, MAXBUFSIZE, format, args);
75   request = va_arg(args, int);
76   va_end(args);
77
78   if(len < 0 || len > MAXBUFSIZE-1)
79     {
80       syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
81       return -1;
82     }
83
84   len++;
85
86   if(debug_lvl >= DEBUG_PROTOCOL)
87     syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
88
89 cp
90   return send_meta(cl, buffer, len);
91 }
92
93 int receive_request(conn_list_t *cl)
94 {
95   int request;
96 cp  
97   if(sscanf(cl->buffer, "%d", &request) == 1)
98     {
99       if((request < 0) || (request > 255) || (request_handlers[request] == NULL))
100         {
101           syslog(LOG_ERR, _("Unknown request from %s (%s)"),
102                  cl->name, cl->hostname);
103           return -1;
104         }
105       else
106         {
107           if(debug_lvl > DEBUG_PROTOCOL)
108             syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
109                    request_name[request], cl->name, cl->hostname);
110         }
111       if(request_handlers[request](cl))
112         /* Something went wrong. Probably scriptkiddies. Terminate. */
113         {
114           syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
115                  request_name[request], cl->name, cl->hostname);
116           return -1;
117         }
118     }
119   else
120     {
121       syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
122              cl->name, cl->hostname);
123       return -1;
124     }
125 }
126
127 /* Connection protocol:
128
129    Client               Server
130    send_id(u)
131                         send_challenge(R)
132    send_chal_reply(H)
133                         send_id(u)
134    send_challenge(R)
135                         send_chal_reply(H)
136    ---------------------------------------
137    Any negotations about the meta protocol
138    encryption go here(u).
139    ---------------------------------------
140    send_ack(u)
141                         send_ack(u)
142    ---------------------------------------
143    Other requests(E)...
144
145    (u) Unencrypted,
146    (R) RSA,
147    (H) SHA1,
148    (E) Encrypted with symmetric cipher.
149
150    Part of the challenge is directly used to set the symmetric cipher key and the initial vector.
151    Since a man-in-the-middle cannot decrypt the RSA challenges, this means that he cannot get or
152    forge the key for the symmetric cipher.
153 */
154
155 int send_id(conn_list_t *cl)
156 {
157 cp
158   cl->allow_request = CHALLENGE;
159 cp
160   return send_request(cl, "%d %s %d %lx %hd", ID, myself->name, myself->protocol_version, myself->options, myself->port);
161 }
162
163 int id_h(conn_list_t *cl)
164 {
165   conn_list_t *old;
166   config_t *cfg;
167 cp
168   if(sscanf(cl->buffer, "%*d %as %d %lx %hd", &cl->name, &cl->protocol_version, &cl->options, &cl->port) != 4)
169     {
170        syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
171        return -1;
172     }
173
174   /* Check if version matches */
175
176   if(cl->protocol_version != myself->protocol_version)
177     {
178       syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
179              cl->name, cl->hostname, cl->protocol_version);
180       return -1;
181     }
182
183   /* Check if identity is a valid name */
184
185   if(check_id(cl->name))
186     {
187       syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
188       return -1;
189     }
190
191   /* Load information about peer */
192 cp
193   if(read_host_config(cl))
194     {
195       syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
196       return -1;
197     }
198
199   /* First check if the host we connected to is already in our
200      connection list. If so, we are probably making a loop, which
201      is not desirable.
202    */
203 cp
204   if(cl->status.outgoing)
205     {
206       if((old = lookup_id(cl->name)))
207         {
208           if(debug_lvl > DEBUG_CONNECTIONS)
209             syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
210           cl->status.outgoing = 0;
211           old->status.outgoing = 1;
212           terminate_connection(cl);
213           return 0;
214         }
215     }
216 cp    
217   if(!(cfg = get_config_val(cl->config, publickey)))
218     {
219       syslog(LOG_ERR, _("No public key known for %s (%s)"), cl->name, cl->hostname);
220       return -1;
221     }
222   else
223     {
224 cp
225       cl->rsa_key = RSA_new();
226       BN_hex2bn(&cl->rsa_key->n, cfg->data.ptr);
227       BN_hex2bn(&cl->rsa_key->e, "FFFF");
228     }
229
230 cp
231   return send_challenge(cl);
232 }
233
234 int send_challenge(conn_list_t *cl)
235 {
236   char *buffer;
237   int len, x;
238 cp
239   len = RSA_size(cl->rsa_key);
240
241   /* Allocate buffers for the challenge */
242
243   buffer = xmalloc(len*2+1);
244
245   if(cl->hischallenge)
246     free(cl->hischallenge);
247     
248   cl->hischallenge = xmalloc(len);
249 cp
250   /* Copy random data to the buffer */
251
252   RAND_bytes(cl->hischallenge, len);
253
254   /* Encrypt the random data */
255   
256   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 */
257     {
258       syslog(LOG_ERR, _("Error during encryption of challenge for %s (%s)"), cl->name, cl->hostname);
259       free(buffer);
260       return -1;
261     }
262 cp
263   /* Convert the encrypted random data to a hexadecimal formatted string */
264
265   bin2hex(buffer, buffer, len);
266   buffer[len*2] = '\0';
267
268   /* Send the challenge */
269
270   cl->allow_request = CHAL_REPLY;
271   x = send_request(cl, "%d %s", CHALLENGE, buffer);
272   free(buffer);
273 cp
274   return x;
275 }
276
277 int challenge_h(conn_list_t *cl)
278 {
279   char *buffer;
280   int len;
281 cp
282   if(sscanf(cl->buffer, "%*d %as", &buffer) != 1)
283     {
284        syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
285        return -1;
286     }
287
288   len = RSA_size(myself->rsa_key);
289
290   /* Check if the length of the challenge is all right */
291
292   if(strlen(buffer) != len*2)
293     {
294       syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
295       free(buffer);
296       return -1;
297     }
298
299   /* Allocate buffers for the challenge */
300
301   if(!cl->mychallenge)
302     cl->mychallenge = xmalloc(len);
303
304   /* Convert the challenge from hexadecimal back to binary */
305
306   hex2bin(buffer,buffer,len);
307
308   /* Decrypt the challenge */
309   
310   if(RSA_private_decrypt(len, buffer, cl->mychallenge, myself->rsa_key, RSA_NO_PADDING) != len) /* See challenge() */
311     {
312       syslog(LOG_ERR, _("Error during encryption of challenge for %s (%s)"), cl->name, cl->hostname);
313       free(buffer);
314       return -1;
315     }
316
317   free(buffer);
318     
319   /* Rest is done by send_chal_reply() */
320 cp
321   return send_chal_reply(cl);
322 }
323
324 int send_chal_reply(conn_list_t *cl)
325 {
326   char hash[SHA_DIGEST_LENGTH*2+1];
327 cp
328   if(!cl->mychallenge)
329     {
330       syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
331       return -1;
332     }
333      
334   /* Calculate the hash from the challenge we received */
335
336   SHA1(cl->mychallenge, RSA_size(myself->rsa_key), hash);
337
338   /* Convert the hash to a hexadecimal formatted string */
339
340   bin2hex(hash,hash,SHA_DIGEST_LENGTH);
341   hash[SHA_DIGEST_LENGTH*2] = '\0';
342
343   /* Send the reply */
344
345   if(cl->status.outgoing)
346     cl->allow_request = ID;
347   else
348     cl->allow_request = ACK;
349
350 cp
351   return send_request(cl, "%d %s", CHAL_REPLY, hash);
352 }
353
354 int chal_reply_h(conn_list_t *cl)
355 {
356   char *hishash;
357   char myhash[SHA_DIGEST_LENGTH];
358 cp
359   if(sscanf(cl->buffer, "%*d %as", &hishash) != 1)
360     {
361        syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
362        free(hishash);
363        return -1;
364     }
365
366   /* Check if the length of the hash is all right */
367
368   if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
369     {
370       syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
371       free(hishash);
372       return -1;
373     }
374
375   /* Convert the hash to binary format */
376
377   hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
378
379   /* Calculate the hash from the challenge we sent */
380
381   SHA1(cl->hischallenge, RSA_size(cl->rsa_key), myhash);
382
383   /* Verify the incoming hash with the calculated hash */
384
385   if(memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
386     {
387       syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
388       free(hishash);
389       return -1;
390     }
391
392   free(hishash);
393
394   /* Identity has now been positively verified.
395      If we are accepting this new connection, then send our identity,
396      if we are making this connecting, acknowledge.
397    */
398 cp
399   if(cl->status.outgoing)
400       return send_ack(cl);
401   else
402       return send_id(cl);
403 }
404
405 int send_ack(conn_list_t *cl)
406 {
407 cp
408   cl->allow_request = ACK;
409 cp
410   return send_request(cl, "%d", ACK);
411 }
412
413 int ack_h(conn_list_t *cl)
414 {
415   conn_list_t *old;
416 cp
417   /* Okay, before we active the connection, we check if there is another entry
418      in the connection list with the same name. If so, it presumably is an
419      old connection that has timed out but we don't know it yet.
420    */
421
422   while((old = lookup_id(cl->name)))
423     {
424       if(debug_lvl > DEBUG_CONNECTIONS)
425         syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
426         cl->name, old->hostname, cl->hostname);
427       old->status.active = 0;
428       terminate_connection(old);
429     }
430
431   /* Activate this connection */
432
433   cl->allow_request = ALL;
434   cl->status.active = 1;
435
436   if(debug_lvl > DEBUG_CONNECTIONS)
437     syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
438
439   /* Exchange information about other tinc daemons */
440
441 /* FIXME: reprogram this.
442   notify_others(cl, NULL, send_add_host);
443   notify_one(cl);
444 */
445   upstreamindex = 0;
446
447 cp
448   if(cl->status.outgoing)
449     return 0;
450   else
451     return send_ack(cl);
452 }
453
454 /* Address and subnet information exchange */
455
456 int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
457 {
458   int x;
459   char *netstr;
460 cp
461   x = send_request(cl, "%d %s %s", ADD_SUBNET,
462                       other->name, netstr = net2str(subnet));
463   free(netstr);
464 cp
465   return x;
466 }
467
468 int add_subnet_h(conn_list_t *cl)
469 {
470   char *subnetstr;
471   char *name;
472   conn_list_t *owner;
473   subnet_t *subnet, *old;
474 cp
475   if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
476     {
477       syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
478       free(name); free(subnetstr);
479       return -1;
480     }
481
482   /* Check if owner name is a valid */
483
484   if(check_id(name))
485     {
486       syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
487       free(name); free(subnetstr);
488       return -1;
489     }
490
491   /* Check if subnet string is valid */
492
493   if(!(subnet = str2net(subnetstr)))
494     {
495       syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
496       free(name); free(subnetstr);
497       return -1;
498     }
499
500   free(subnetstr);
501   
502   /* Check if somebody tries to add a subnet of ourself */
503
504   if(!strcmp(name, myself->name))
505     {
506       syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
507              cl->name, cl->hostname);
508       free(name);
509       sighup = 1;
510       return 0;
511     }
512
513   /* Check if the owner of the new subnet is in the connection list */
514
515   if(!(owner = lookup_id(name)))
516     {
517       syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
518              name, cl->name, cl->hostname);
519       free(name);
520       return -1;
521     }
522
523   /* If everything is correct, add the subnet to the list of the owner */
524
525   subnet_add(owner, subnet);
526 cp
527   return 0;
528 }
529
530 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
531 {
532 cp
533   return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
534 }
535
536 int del_subnet_h(conn_list_t *cl)
537 {
538   char *subnetstr;
539   char *name;
540   conn_list_t *owner;
541   subnet_t *subnet, *old;
542 cp
543   if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
544     {
545       syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
546       free(name); free(subnetstr);
547       return -1;
548     }
549
550   /* Check if owner name is a valid */
551
552   if(check_id(name))
553     {
554       syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
555       free(name); free(subnetstr);
556       return -1;
557     }
558
559   /* Check if subnet string is valid */
560
561   if(!(subnet = str2net(subnetstr)))
562     {
563       syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
564       free(name); free(subnetstr);
565       return -1;
566     }
567
568   free(subnetstr);
569   
570   /* Check if somebody tries to add a subnet of ourself */
571
572   if(!strcmp(name, myself->name))
573     {
574       syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
575              cl->name, cl->hostname);
576       free(name);
577       sighup = 1;
578       return 0;
579     }
580
581   /* Check if the owner of the new subnet is in the connection list */
582
583   if(!(owner = lookup_id(name)))
584     {
585       syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
586              name, cl->name, cl->hostname);
587       free(name);
588       return -1;
589     }
590
591   /* If everything is correct, delete the subnet from the list of the owner */
592
593   subnet_del(subnet);
594 cp
595   return 0;
596 }
597
598 /* New and closed connections notification */
599
600 int send_add_host(conn_list_t *cl, conn_list_t *other)
601 {
602 cp
603   return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
604                       myself->name, other->name, other->address, other->port, other->options);
605 }
606
607 int add_host_h(conn_list_t *cl)
608 {
609   char *sender;
610   conn_list_t *old, *new, *hisuplink;
611 cp
612   new = new_conn_list();
613
614   if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
615     {
616        syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
617        return -1;
618     }
619
620   /* Check if identity is a valid name */
621
622   if(check_id(new->name) || check_id(sender))
623     {
624       syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
625       free(sender);
626       return -1;
627     }
628
629   /* Check if somebody tries to add ourself */
630
631   if(!strcmp(new->name, myself->name))
632     {
633       syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
634       sighup = 1;
635       free(sender);
636       return 0;
637     }
638
639   /* We got an ADD_HOST from ourself!? */
640
641   if(!strcmp(sender, myself->name))
642     {
643       syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
644       sighup = 1;
645       free(sender);
646       return 0;
647     }
648
649   /* Lookup his uplink */
650
651   if(!(new->hisuplink = lookup_id(sender)))
652     {
653       syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
654              sender, cl->name, cl->hostname);
655       free(sender);
656       return -1;
657     }
658     
659   free(sender);
660
661   /* Fill in more of the new conn_list structure */
662
663   new->hostname = hostlookup(htonl(new->address));
664
665   /* Check if the new host already exists in the connnection list */
666
667   if((old = lookup_id(new->name)))
668     {
669       if((new->address == old->address) && (new->port == old->port))
670         {
671           if(debug_lvl > DEBUG_CONNECTIONS)
672             syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
673                    old->name, old->hostname, new->name, new->hostname);
674           return 0;
675         }
676       else
677         {
678           if(debug_lvl > DEBUG_CONNECTIONS)
679             syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
680                    old->name, old->hostname);
681           old->status.active = 0;
682           terminate_connection(old);
683         }
684     }
685
686   /* Fill in rest of conn_list structure */
687
688   new->myuplink = cl;
689   new->status.active = 1;
690
691   /* Hook it up into the conn_list */
692
693   conn_list_add(conn_list, new);
694
695   /* Tell the rest about the new host */
696 /* FIXME: reprogram this.
697   notify_others(new, cl, send_add_host);
698 */
699 cp
700   return 0;
701 }
702
703 int send_del_host(conn_list_t *cl, conn_list_t *other)
704 {
705 cp
706   return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
707                       myself->name, other->name, other->address, other->port, other->options);
708 }
709
710 int del_host_h(conn_list_t *cl)
711 {
712   char *name;
713   char *sender;
714   ip_t address;
715   port_t port;
716   int options;
717   conn_list_t *old, *hisuplink;
718
719 cp
720   if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
721     {
722       syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
723              cl->name, cl->hostname);
724       return -1;
725     }
726
727   /* Check if identity is a valid name */
728
729   if(check_id(name) || check_id(sender))
730     {
731       syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
732       free(name); free(sender);
733       return -1;
734     }
735
736   /* Check if somebody tries to delete ourself */
737
738   if(!strcmp(name, myself->name))
739     {
740       syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
741              cl->name, cl->hostname);
742       free(name); free(sender);
743       sighup = 1;
744       return 0;
745     }
746
747   /* We got an ADD_HOST from ourself!? */
748
749   if(!strcmp(sender, myself->name))
750     {
751       syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
752       sighup = 1;
753       free(name); free(sender);
754       return 0;
755     }
756
757   /* Lookup his uplink */
758
759   if(!(hisuplink = lookup_id(sender)))
760     {
761       syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
762              cl->name, cl->hostname, sender);
763       free(name); free(sender);
764       return -1;
765     }
766     
767   free(sender);
768
769   /* Check if the new host already exists in the connnection list */
770
771   if(!(old = lookup_id(name)))
772     {
773       syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
774              name, cl->name, cl->hostname);
775       free(name);
776       return -1;
777     }
778   
779   /* Check if the rest matches */
780   
781   if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
782     {
783       syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
784       return 0;
785     }
786
787   /* Ok, since EVERYTHING seems to check out all right, delete it */
788
789   old->status.termreq = 1;
790   old->status.active = 0;
791
792   terminate_connection(old);
793 cp
794   return 0;
795 }
796
797 /* Status and error notification routines */
798
799 int send_status(conn_list_t *cl, int statusno, char *statusstring)
800 {
801 cp
802   if(!statusstring)
803     statusstring = status_text[statusno];
804 cp
805   return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
806 }
807
808 int status_h(conn_list_t *cl)
809 {
810   int statusno;
811   char *statusstring;
812 cp
813   if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
814     {
815        syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
816               cl->name, cl->hostname);
817        return -1;
818     }
819
820   if(debug_lvl > DEBUG_STATUS)
821     {
822       syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
823              cl->name, cl->hostname, status_text[statusno], statusstring);
824     }
825
826 cp
827   free(statusstring);
828   return 0;
829 }
830
831 int send_error(conn_list_t *cl, int errno, char *errstring)
832 {
833 cp
834   if(!errstring)
835     errstring = strerror(errno);
836   return send_request(cl, "%d %d %s", ERROR, errno, errstring);
837 }
838
839 int error_h(conn_list_t *cl)
840 {
841   int errno;
842   char *errorstring;
843 cp
844   if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
845     {
846        syslog(LOG_ERR, _("Got bad ERROR from %s (%s)"),
847               cl->name, cl->hostname);
848        return -1;
849     }
850
851   if(debug_lvl > DEBUG_ERROR)
852     {
853       syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
854              cl->name, cl->hostname, strerror(errno), errorstring);
855     }
856
857   free(errorstring);
858   cl->status.termreq = 1;
859   terminate_connection(cl);
860 cp
861   return 0;
862 }
863
864 int send_termreq(conn_list_t *cl)
865 {
866 cp
867   return send_request(cl, "%d", TERMREQ);
868 }
869
870 int termreq_h(conn_list_t *cl)
871 {
872 cp
873   cl->status.termreq = 1;
874   terminate_connection(cl);
875 cp
876   return 0;
877 }
878
879 /* Keepalive routines - FIXME: needs a closer look */
880
881 int send_ping(conn_list_t *cl)
882 {
883   cl->status.pinged = 1;
884 cp
885   return send_request(cl, "%d", PING);
886 }
887
888 int ping_h(conn_list_t *cl)
889 {
890 cp
891   return send_pong(cl);
892 }
893
894 int send_pong(conn_list_t *cl)
895 {
896 cp
897   return send_request(cl, "%d", PONG);
898 }
899
900 int pong_h(conn_list_t *cl)
901 {
902 cp
903   cl->status.got_pong = 1;
904 cp
905   return 0;
906 }
907
908 /* Key exchange */
909
910 int send_key_changed(conn_list_t *from, conn_list_t *cl)
911 {
912   conn_list_t *p;
913 cp
914   for(p = conn_list; p != NULL; p = p->next)
915     {
916       if(p!=cl && p->status.meta && p->status.active)
917         send_request(p, "%d %s", KEY_CHANGED,
918                      from->name);
919     }
920 cp
921   return 0;
922 }
923
924 int key_changed_h(conn_list_t *cl)
925 {
926   char *from_id;
927   conn_list_t *from;
928 cp
929   if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
930     {
931       syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
932              cl->name, cl->hostname);
933       return -1;
934     }
935
936   if(!(from = lookup_id(from_id)))
937     {
938       syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
939              cl->name, cl->hostname, from_id);
940       free(from_id);
941       return -1;
942     }
943
944   free(from_id);
945
946   from->status.validkey = 0;
947   from->status.waitingforkey = 0;
948
949   send_key_changed(from, cl);
950 cp
951   return 0;
952 }
953
954 int send_req_key(conn_list_t *from, conn_list_t *to)
955 {
956 cp
957   return send_request(to->nexthop, "%d %s %s", REQ_KEY,
958                       from->name, to->name);
959 }
960
961 int req_key_h(conn_list_t *cl)
962 {
963   char *from_id, *to_id;
964   conn_list_t *from, *to;
965 cp
966   if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
967     {
968        syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
969               cl->name, cl->hostname);
970        return -1;
971     }
972
973   if(!(from = lookup_id(from_id)))
974     {
975       syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
976              cl->name, cl->hostname, from_id);
977       free(from_id); free(to_id);
978       return -1;
979     }
980
981   /* Check if this key request is for us */
982
983   if(!strcmp(to_id, myself->name))
984     {
985       send_ans_key(myself, from, myself->cipher_pktkey);
986     }
987   else
988     {
989       if(!(to = lookup_id(to_id)))
990         {
991           syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
992                  cl->name, cl->hostname, to_id);
993           free(from_id); free(to_id);
994           return -1;
995         }
996       send_req_key(from, to);
997     }
998
999   free(from_id); free(to_id);
1000 cp
1001   return 0;
1002 }
1003
1004 int send_ans_key(conn_list_t *from, conn_list_t *to, char *pktkey)
1005 {
1006 cp
1007   return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
1008                       from->name, to->name, pktkey);
1009 }
1010
1011 int ans_key_h(conn_list_t *cl)
1012 {
1013   char *from_id, *to_id, *pktkey;
1014   int keylength;
1015   conn_list_t *from, *to;
1016 cp
1017   if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &pktkey) != 3)
1018     {
1019        syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
1020               cl->name, cl->hostname);
1021        return -1;
1022     }
1023
1024   if(!(from = lookup_id(from_id)))
1025     {
1026       syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
1027              cl->name, cl->hostname, from_id);
1028       free(from_id); free(to_id); free(pktkey);
1029       return -1;
1030     }
1031
1032   /* Check if this key request is for us */
1033
1034   if(!strcmp(to_id, myself->name))
1035     {
1036       /* It is for us, convert it to binary and set the key with it. */
1037
1038       keylength = strlen(pktkey);
1039
1040       if((keylength%2) || (keylength <= 0))
1041         {
1042           syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
1043                  cl->name, cl->hostname, from->name);
1044           free(from_id); free(to_id); free(pktkey);
1045           return -1;
1046         }
1047       keylength /= 2;
1048       hex2bin(pktkey, pktkey, keylength);
1049       BF_set_key(cl->cipher_pktkey, keylength, pktkey);
1050     }
1051   else
1052     {
1053       if(!(to = lookup_id(to_id)))
1054         {
1055           syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1056                  cl->name, cl->hostname, to_id);
1057           free(from_id); free(to_id); free(pktkey);
1058           return -1;
1059         }
1060       send_ans_key(from, to, pktkey);
1061     }
1062
1063   free(from_id); free(to_id); free(pktkey);
1064 cp
1065   return 0;
1066 }
1067
1068 /* Jumptable for the request handlers */
1069
1070 int (*request_handlers[])(conn_list_t*) = {
1071   id_h, challenge_h, chal_reply_h, ack_h,
1072   status_h, error_h, termreq_h,
1073   ping_h, pong_h,
1074   add_host_h, del_host_h,
1075   add_subnet_h, del_subnet_h,
1076   key_changed_h, req_key_h, ans_key_h,
1077 };
1078
1079 /* Request names */
1080
1081 char (*request_name[]) = {
1082   "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1083   "STATUS", "ERROR", "TERMREQ",
1084   "PING", "PONG",
1085   "ADD_HOST", "DEL_HOST",
1086   "ADD_SUBNET", "DEL_SUBNET",
1087   "KEY_CHANGED", "REQ_KEY", "ANS_KEY",
1088 };
1089
1090 /* Status strings */
1091
1092 char (*status_text[]) = {
1093   "Warning",
1094 };
1095
1096 /* Error strings */
1097
1098 char (*error_text[]) = {
1099   "Error",
1100 };