chiark / gitweb /
e081bdf6106ffd6faf982b6b54a88b3cbdf4bc41
[tripe] / admin.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Admin interface for configuration
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "tripe.h"
32
33 /*----- Global variables --------------------------------------------------*/
34
35 #ifndef NTRACE
36
37 const trace_opt tr_opts[] = {
38   { 't',        T_TUNNEL,       "tunnel events" },
39   { 'r',        T_PEER,         "peer events" },
40   { 'a',        T_ADMIN,        "admin interface" },
41   { 'p',        T_PACKET,       "packet contents" },
42   { 'c',        T_CRYPTO,       "crypto details" },
43   { 's',        T_KEYSET,       "symmetric keyset management" },
44   { 'x',        T_KEYEXCH,      "key exchange" },
45   { 'm',        T_KEYMGMT,      "key management" },
46   { 'A',        T_ALL,          "all of the above" },
47   { 0,          0,              0 }
48 };
49
50 unsigned tr_flags = 0;
51 #endif
52
53 static const trace_opt w_opts[] = {
54   { 't',        AF_TRACE,       "trace messages" },
55   { 'n',        AF_NOTE,        "asynchronous notifications" },
56   { 'w',        AF_WARN,        "warnings" },
57   { 'A',        AF_ALLMSGS,     "all of the above" },
58   { 0,          0,              0 }
59 };
60
61 /*----- Static variables --------------------------------------------------*/
62
63 static admin *admins;
64 static sel_file sock;
65 static const char *sockname;
66 static unsigned flags = 0;
67 static admin *a_stdin = 0;
68 static sig s_term, s_int, s_hup;
69
70 #define F_DAEMON 1u
71 #define F_INIT 2u
72
73 #define T_RESOLVE SEC(30)
74 #define T_PING SEC(5)
75
76 static void a_destroy(admin */*a*/);
77 static void a_lock(admin */*a*/);
78 static void a_unlock(admin */*a*/);
79
80 /*----- Output functions --------------------------------------------------*/
81
82 /* --- @trywrite@ --- *
83  *
84  * Arguments:   @admin *a@ = pointer to an admin block
85  *              @const char *p@ = pointer to buffer to write
86  *              @size_t sz@ = size of data to write
87  *
88  * Returns:     The number of bytes written, or less than zero on error.
89  *
90  * Use:         Attempts to write data to a client.
91  */
92
93 static ssize_t trywrite(admin *a, const char *p, size_t sz)
94 {
95   ssize_t n, done = 0;
96
97 again:
98   if (!sz)
99     return (done);
100   n = write(a->w.fd, p, sz);
101   if (n > 0) {
102     done += n;
103     p += n;
104     sz -= n;
105     goto again;
106   }
107   if (n < 0) {
108     if (errno == EINTR)
109       goto again;
110     if (errno != EAGAIN && errno != EWOULDBLOCK) {
111       a_destroy(a);
112       a_warn("ADMIN client-read-error -- %s", strerror(errno));
113       return (-1);
114     }
115   }
116   return (done);
117 }
118
119 /* --- @dosend@ --- *
120  *
121  * Arguemnts:   @admin *a@ = pointer to an admin block
122  *              @const char *p@ = pointer to buffer to write
123  *              @size_t sz@ = size of data to write
124  *
125  * Returns:     ---
126  *
127  * Use:         Sends data to an admin client.
128  */
129
130 static void dosend(admin *a, const char *p, size_t sz)
131 {
132   ssize_t n;
133   obuf *o;
134
135   if (a->f & AF_DEAD)
136     return;
137
138   /* --- Try to send the data immediately --- */
139
140   if (!a->o_head) {
141     if ((n = trywrite(a, p, sz)) < 0)
142       return;
143     p += n;
144     sz -= n;
145     if (!sz)
146       return;
147   }
148         
149   /* --- Fill buffers with the data until it's all gone --- */
150
151   o = a->o_tail;
152   if (!o)
153     sel_addfile(&a->w);
154   else if (o->p_in < o->buf + OBUFSZ)
155     goto noalloc;
156
157   do {
158     o = xmalloc(sizeof(obuf));
159     o->next = 0;
160     o->p_in = o->p_out = o->buf;
161     if (a->o_tail)
162       a->o_tail->next = o;
163     else
164       a->o_head = o;
165     a->o_tail = o;
166
167   noalloc:
168     n = o->buf + OBUFSZ - o->p_in;
169     if (n > sz)
170       n = sz;
171     memcpy(o->p_in, p, n);
172     o->p_in += n;
173     p += n;
174     sz -= n;
175   } while (sz);
176 }
177
178 /* --- @a_flush@ --- *
179  *
180  * Arguments:   @int fd@ = file descriptor
181  *              @unsigned mode@ = what's happening
182  *              @void *v@ = pointer to my admin block
183  *
184  * Returns:     ---
185  *
186  * Use:         Flushes buffers when a client is ready to read again.
187  */
188
189 static void a_flush(int fd, unsigned mode, void *v)
190 {
191   admin *a = v;
192   obuf *o, *oo;
193   ssize_t n;
194
195   o = a->o_head;
196   while (o) {
197     if ((n = trywrite(a, o->p_out, o->p_in - o->p_out)) < 0)
198       return;
199     o->p_out += n;
200     if (o->p_in < o->p_out)
201       break;
202     oo = o;
203     o = o->next;
204     xfree(oo);
205   }
206   a->o_head = o;
207   if (!o) {
208     a->o_tail = 0;
209     sel_rmfile(&a->w);
210   }
211 }
212
213 /*----- Utility functions -------------------------------------------------*/
214
215 /* --- @a_write@, @a_vwrite@ --- *
216  *
217  * Arguments:   @admin *a@ = admin connection to write to
218  *              @const char *tag@ = tag prefix string, or null
219  *              @const char *fmt@ = pointer to format string
220  *              @va_list ap@ = arguments in list
221  *              @...@ = other arguments
222  *
223  * Returns:     ---
224  *
225  * Use:         Sends a message to an admin connection.
226  */
227
228 static void a_vwrite(admin *a, const char *tag, const char *fmt, va_list ap)
229 {
230   dstr d = DSTR_INIT;
231   if (tag) {
232     dstr_puts(&d, tag);
233     if (fmt)
234       dstr_putc(&d, ' ');
235   }
236   if (fmt)
237     dstr_vputf(&d, fmt, &ap);
238   dstr_putc(&d, '\n');
239   dosend(a, d.buf, d.len);
240   dstr_destroy(&d);
241 }
242
243 static void a_write(admin *a, const char *tag, const char *fmt, ...)
244 {
245   va_list ap;
246   va_start(ap, fmt);
247   a_vwrite(a, tag, fmt, ap);
248   va_end(ap);
249 }
250
251 /* --- @a_ok@, @a_info@, @a_fail@ --- *
252  *
253  * Arguments:   @admin *a@ = connection
254  *              @const char *fmt@ = format string
255  *              @...@ = other arguments
256  *
257  * Returns:     ---
258  *
259  * Use:         Convenience functions for @a_write@.
260  */
261
262 static void a_ok(admin *a) { a_write(a, "OK", 0); }
263
264 static void a_info(admin *a, const char *fmt, ...)
265 {
266   va_list ap;
267   va_start(ap, fmt);
268   a_vwrite(a, "INFO", fmt, ap);
269   va_end(ap);
270 }
271
272 static void a_fail(admin *a, const char *fmt, ...)
273 {
274   va_list ap;
275   va_start(ap, fmt);
276   a_vwrite(a, "FAIL", fmt, ap);
277   va_end(ap);
278 }
279
280 /* --- @a_alert@, @a_valert@, @a_rawalert@ --- *
281  *
282  * Arguments:   @unsigned f_and, f_eq@ = filter for connections
283  *              @const char *tag@ = tag prefix string
284  *              @const char *fmt@ = pointer to format string
285  @              @const char *p@ = pointer to raw string
286  *              @size_t sz@ = size of raw string
287  *              @va_list ap@ = arguments in list
288  *              @...@ = other arguments
289  *
290  * Returns:     ---
291  *
292  * Use:         Write a message to all admin connections matched by the given
293  *              filter.
294  */
295
296 static void a_rawalert(unsigned f_and, unsigned f_eq, const char *tag,
297                        const char *p, size_t sz)
298 {
299   admin *a, *aa;
300   dstr d = DSTR_INIT;
301   
302   if (!(flags & F_INIT))
303     return;
304   if (tag) {
305     dstr_puts(&d, tag);
306     if (p)
307       dstr_putc(&d, ' ');
308   }
309   if (p)
310     dstr_putm(&d, p, sz);
311   dstr_putc(&d, '\n');
312   p = d.buf;
313   sz = d.len;
314   for (a = admins; a; a = aa) {
315     aa = a->next;
316     if ((a->f & f_and) == f_eq)
317       dosend(a, d.buf, d.len);
318   }
319   dstr_destroy(&d);
320 }
321
322 static void a_valert(unsigned f_and, unsigned f_eq, const char *tag,
323                      const char *fmt, va_list ap)
324 {
325   dstr d = DSTR_INIT;
326
327   if (!(flags & F_INIT))
328     return;
329   if (fmt)
330     dstr_vputf(&d, fmt, &ap);
331   a_rawalert(f_and, f_eq, tag, fmt ? d.buf : 0, fmt ? d.len : 0);
332   dstr_destroy(&d);
333 }
334
335 #if 0 /*unused*/
336 static void a_alert(unsigned f_and, unsigned f_eq, const char *tag,
337                     const char *fmt, ...)
338 {
339   va_list ap;
340   va_start(ap, fmt);
341   a_valert(f_and, f_eq, tag, fmt, ap);
342   va_end(ap);
343 }
344 #endif
345
346 /* --- @a_warn@ --- *
347  *
348  * Arguments:   @const char *fmt@ = pointer to format string
349  *              @...@ = other arguments
350  *
351  * Returns:     ---
352  *
353  * Use:         Informs all admin connections of a warning.
354  */
355
356 void a_warn(const char *fmt, ...)
357 {
358   va_list ap;
359
360   va_start(ap, fmt);
361   if (flags & F_INIT)
362     a_valert(0, 0, "WARN", fmt, ap);
363   else {
364     fprintf(stderr, "%s: ", QUIS);
365     vfprintf(stderr, fmt, ap);
366     fputc('\n', stderr);
367   }
368   va_end(ap);
369 }
370
371 /* --- @a_trace@ --- *
372  *
373  * Arguments:   @const char *p@ = pointer to a buffer
374  *              @size_t sz@ = size of the buffer
375  *              @void *v@ = uninteresting pointer
376  *
377  * Returns:     ---
378  *
379  * Use:         Custom trace output handler.  Sends trace messages to
380  *              interested admin connections.
381  */
382
383 #ifndef NTRACE
384 static void a_trace(const char *p, size_t sz, void *v)
385 {
386   a_rawalert(AF_TRACE, AF_TRACE, "TRACE", p, sz);
387 }
388 #endif
389
390 /* --- @a_notify@ --- *
391  *
392  * Arguments:   @const char *fmt@ = pointer to format string
393  *              @...@ = other arguments
394  *
395  * Returns:     ---
396  *
397  * Use:         Sends a notification to interested admin connections.
398  */
399
400 void a_notify(const char *fmt, ...)
401 {
402   va_list ap;
403
404   va_start(ap, fmt);
405   a_valert(AF_NOTE, AF_NOTE, "NOTE", fmt, ap);
406   va_end(ap);
407 }
408
409 /* --- @a_quit@ --- *
410  *
411  * Arguments:   ---
412  *
413  * Returns:     ---
414  *
415  * Use:         Shuts things down nicely.
416  */
417
418 void a_quit(void)
419 {
420   peer *p;
421
422   while ((p = p_first()) != 0)
423     p_destroy(p);
424   close(sock.fd);
425   unlink(sockname);
426   exit(0);
427 }
428
429 /* --- @a_sigdie@ --- *
430  *
431  * Arguments:   @int sig@ = signal number
432  *              @void *v@ = an uninteresting argument
433  *
434  * Returns:     ---
435  *
436  * Use          Shuts down on receipt of a fatal signal.
437  */
438
439 static void a_sigdie(int sig, void *v)
440 {
441   char *p;
442   char buf[20];
443
444   switch (sig) {
445     case SIGTERM:       p = "SIGTERM"; break;
446     case SIGINT:        p = "SIGINT"; break;
447     default:
448       sprintf(buf, "%i", sig);
449       p = buf;
450       break;
451   }
452   a_warn("SERVER quit signal %s", p);
453   a_quit();
454 }
455
456 /* --- @a_sighup@ --- *
457  *
458  * Arguments:   @int sig@ = signal number
459  *              @void *v@ = an uninteresting argument
460  *
461  * Returns:     ---
462  *
463  * Use          Logs a message about SIGHUP not being useful.
464  */
465
466 static void a_sighup(int sig, void *v)
467 {
468   a_warn("SERVER ignore signal SIGHUP");
469 }
470
471 /* --- @a_parsetime@ --- *
472  *
473  * Arguments;   @const char *p@ = time string to parse
474  *
475  * Returns:     Time in seconds, or @< 0@ on error.
476  */
477
478 static long a_parsetime(const char *p)
479 {
480   char *q;
481   long t = strtol(p, &q, 0);
482
483   switch (*q) {
484     case 'd': t *= 24;
485     case 'h': t *= 60;
486     case 'm': t *= 60;
487     case 's': if (q[1] != 0)
488     default:    t = -1;
489     case 0:   break;
490   }
491   return (t);    
492 }
493
494 /*----- Adding peers ------------------------------------------------------*/
495  
496 /* --- @a_resolve@ --- *
497  *
498  * Arguments:   @struct hostent *h@ = pointer to resolved hostname
499  *              @void *v@ = pointer to admin block
500  *
501  * Returns:     ---
502  *
503  * Use:         Handles a completed name resolution.
504  */
505
506 static void a_resolve(struct hostent *h, void *v)
507 {
508   admin *a = v;
509
510   a_lock(a);
511   T( trace(T_ADMIN, "admin: %u resolved", a->seq); )
512   TIMER;
513   sel_rmtimer(&a->t);
514   if (!h)
515     a_fail(a, "resolve-error %s", a->paddr);
516   else if (p_find(a->peer.name))
517     a_fail(a, "peer-exists %s", a->peer.name);
518   else {
519     memcpy(&a->peer.sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
520     if (!p_create(&a->peer))
521       a_fail(a, "peer-create-fail %s", a->peer.name);
522     else
523       a_ok(a);
524   }
525   xfree(a->peer.name);
526   xfree(a->paddr);
527   a->peer.name = 0;
528   selbuf_enable(&a->b);
529   a_unlock(a);
530 }
531
532 /* --- @a_timer@ --- *
533  *
534  * Arguments:   @struct timeval *tv@ = timer
535  *              @void *v@ = pointer to admin block
536  *
537  * Returns:     ---
538  *
539  * Use:         Times out a resolver.
540  */
541
542 static void a_timer(struct timeval *tv, void *v)
543 {
544   admin *a = v;
545
546   a_lock(a);
547   T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
548   bres_abort(&a->r);
549   a_fail(a, "resolver-timeout %s\n", a->paddr);
550   xfree(a->peer.name);
551   xfree(a->paddr);
552   a->peer.name = 0;
553   selbuf_enable(&a->b);
554   a_unlock(a);
555 }
556
557 /* --- @acmd_add@ --- *
558  *
559  * Arguments:   @admin *a@ = connection which requested the addition
560  *              @unsigned ac@ = argument count
561  *              @char *av[]@ = pointer to the argument list
562  *
563  * Returns:     ---
564  *
565  * Use:         Adds a new peer.
566  */
567
568 static void acmd_add(admin *a, unsigned ac, char *av[])
569 {
570   unsigned long pt;
571   struct timeval tv;
572   unsigned i, j;
573   char *p;
574
575   /* --- Make sure someone's not got there already --- */
576
577   if (p_find(av[0])) {
578     a_fail(a, "peer-exists %s", av[0]);
579     return;
580   }
581
582   /* --- Set stuff up --- */
583
584   a->peer.name = av[0];
585   a->peer.t_ka = 0;
586   a->peer.tops = tun_default;
587
588   /* --- Parse options --- */
589
590   i = 1;
591   for (;;) {
592     if (!av[i])
593       goto bad_syntax;
594     if (mystrieq(av[i], "-tunnel")) {
595       if (!av[++i]) goto bad_syntax;
596       for (j = 0;; j++) {
597         if (!tunnels[j]) {
598           a_fail(a, "unknown-tunnel %s", av[i]);
599           return;
600         }
601         if (mystrieq(av[i], tunnels[j]->name)) {
602           a->peer.tops = tunnels[j];
603           break;
604         }
605       }
606     } else if (mystrieq(av[i], "-keepalive")) {
607       long t;
608       if (!av[++i]) goto bad_syntax;
609       if ((t = a_parsetime(av[i])) < 0) {
610         a_fail(a, "bad-time-spec %s", av[i]);
611         return;
612       }
613       a->peer.t_ka = t;
614     } else if (mystrieq(av[i], "--")) {
615       i++;
616       break;
617     } else
618       break;
619     i++;
620   }
621
622   /* --- Fill in the easy bits of address --- */
623
624   if (mystrieq(av[i], "inet")) i++;
625   if (ac - i != 2) {
626     a_fail(a, "bad-syntax -- add PEER [OPTIONS] [inet] ADDRESS PORT");
627     return;
628   }
629   a->peer.sa.sin.sin_family = AF_INET;
630   a->peer.sasz = sizeof(a->peer.sa.sin);
631   pt = strtoul(av[i + 1], &p, 0);
632   if (*p) {
633     struct servent *s = getservbyname(av[i + 1], "udp");
634     if (!s) {
635       a_fail(a, "unknown-service %s", av[i + 1]);
636       return;
637     }
638     pt = ntohs(s->s_port);
639   }
640   if (pt == 0 || pt >= 65536) {
641     a_fail(a, "invalid-port %lu", pt);
642     return;
643   }
644   a->peer.sa.sin.sin_port = htons(pt);
645
646   /* --- If the name is numeric, do it the easy way --- */
647   
648   if (inet_aton(av[i], &a->peer.sa.sin.sin_addr)) {
649     if (!p_create(&a->peer))
650       a_fail(a, "peer-create-fail %s", av[0]);
651     else
652       a_ok(a);
653     a->peer.name = 0;
654     return;
655   }
656
657   /* --- Store everything for later and crank up the resolver --- *
658    *
659    * We disable the line buffer until the resolver completes (or times out).
660    * This prevents other commands on the same connection (though the rest of
661    * the system continues regardless), but makes life simpler for the client.
662    */
663
664   a->peer.name = xstrdup(av[0]);
665   a->paddr = xstrdup(av[i]);
666   selbuf_disable(&a->b);
667   gettimeofday(&tv, 0);
668   tv.tv_sec += T_RESOLVE;
669   sel_addtimer(&sel, &a->t, &tv, a_timer, a);
670   bres_byname(&a->r, a->paddr, a_resolve, a);
671   T( trace(T_ADMIN, "admin: %u resolving hostname `%s'",
672            a->seq, a->paddr); )
673   return;
674
675 bad_syntax:
676   a_fail(a, "bad-syntax -- add PEER [OPTIONS] ADDR ...");
677   return;
678 }
679
680 /*----- Ping --------------------------------------------------------------*/
681
682 /* --- @a_pong@ --- *
683  *
684  * Arguments:   @int rc@ = return code
685  *              @void *av@ = admin connection which requested the ping
686  *
687  * Returns:     ---
688  *
689  * Use:         Collects what happened to a ping message.
690  */
691
692 static void a_pong(int rc, void *av)
693 {
694   admin *a = av;
695   struct timeval tv;
696   double millis;
697
698   a_lock(a);
699   switch (rc) {
700     case PING_OK:
701       gettimeofday(&tv, 0);
702       tv_sub(&tv, &tv, &a->pingtime);
703       millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
704       a_info(a, "ping-ok %.1f", millis);
705       a_ok(a);
706       break;
707     case PING_TIMEOUT:
708       a_info(a, "ping-timeout");
709       a_ok(a);
710       break;
711     case PING_PEERDIED:
712       a_info(a, "ping-peer-died");
713       a_ok(a);
714       break;
715     default:
716       abort();
717   }
718   a_unlock(a);
719 }
720
721 /* --- @acmd_ping@, @acmd_eping@ --- *
722  *
723  * Arguments:   @admin *a@ = connection which requested the ping
724  *              @unsigned ac@ = argument count
725  *              @char *av[]@ = pointer to the argument list
726  *
727  * Returns:     ---
728  *
729  * Use:         Pings a peer.
730  */
731
732 static void a_ping(admin *a, unsigned ac, char *av[],
733                    const char *cmd, unsigned msg)
734 {
735   long t = T_PING;
736   int i;
737   peer *p;
738
739   i = 0;
740   for (;;) {
741     if (!av[i])
742       goto bad_syntax;
743     if (mystrieq(av[i], "-timeout")) {
744       if (!av[++i]) goto bad_syntax;
745       if ((t = a_parsetime(av[i])) < 0) {
746         a_fail(a, "bad-time-spec %s", av[i]);
747         return;
748       }
749     } else if (mystrieq(av[i], "--")) {
750       i++;
751       break;
752     } else
753       break;
754     i++;
755   }
756
757   if (!av[i]) goto bad_syntax;
758   if ((p = p_find(av[i])) == 0) {
759     a_fail(a, "unknown-peer %s", av[i]);
760     return;
761   }
762   gettimeofday(&a->pingtime, 0);
763   if (p_pingsend(p, &a->ping, msg, t, a_pong, a))
764     a_fail(a, "ping-send-failed");
765   return;
766     
767 bad_syntax:
768   a_fail(a, "bad-syntax -- %s [OPTIONS] PEER", cmd);
769   return;
770 }
771
772 static void acmd_ping(admin *a, unsigned ac, char *av[])
773   { a_ping(a, ac, av, "ping", MISC_PING); }
774 static void acmd_eping(admin *a, unsigned ac, char *av[])
775   { a_ping(a, ac, av, "eping", MISC_EPING); }
776   
777
778 /*----- Administration commands -------------------------------------------*/
779
780 /* --- Miscellaneous commands --- */
781
782 /* --- @traceish@ --- *
783  *
784  * Arguments:   @admin *a@ = connection to complain on
785  *              @unsigned ac@ = number of arguments
786  *              @char *av[]@ = vector of arguments
787  *              @const char *what@ = what we're messing with
788  *              @const trace_opt *tt@ = options table
789  *              @unsigned *ff@ = where the flags are
790  *
791  * Returns:     Nonzero if anything changed.
792  *
793  * Use:         Guts of trace-ish commands like `trace' and `watch'.
794  */
795
796 static int traceish(admin *a, unsigned ac, char *av[],
797                     const char *what, const trace_opt *tt, unsigned *ff)
798 {
799   int ch = 0;
800
801   if (!ac || strcmp(av[0], "?") == 0) {
802     const trace_opt *t;
803     a_info(a, "Current %s status:", what);
804     for (t = tt; t->ch; t++) {
805       a_info(a, "%c %c  %s",
806              t->ch, (*ff & t->f) == t->f ? '*' : ' ', t->help);
807     }
808   } else {
809     unsigned sense = 1;
810     unsigned f = *ff;
811     const trace_opt *t;
812     char *p = av[0];
813
814     while (*p) {
815       switch (*p) {
816         case '+': sense = 1; break;
817         case '-': sense = 0; break;
818         default:
819           for (t = tt; t->ch; t++) {
820             if (t->ch == *p) {
821               if (sense) f |= t->f;
822               else f &= ~t->f;
823               goto tropt_ok;
824             }
825           }
826           a_fail(a, "bad-%s-option %c", what, *p);
827           return (0);
828         tropt_ok:;
829           break;
830       }
831       p++;
832     }
833     *ff = f;
834     ch = 1;
835   }
836   a_ok(a);
837   return (ch);
838 }
839
840 #ifndef NTRACE
841
842 static void acmd_trace(admin *a, unsigned ac, char *av[])
843 {
844   if (traceish(a, ac, av, "trace", tr_opts, &tr_flags))
845     trace_level(tr_flags);
846 }
847
848 #endif
849
850 static void acmd_watch(admin *a, unsigned ac, char *av[])
851 {
852   traceish(a, ac, av, "watch", w_opts, &a->f);
853 }
854
855 static void quotify(dstr *d, const char *p)
856 {
857   if (d->len)
858     dstr_putc(d, ' ');
859   if (*p && !p[strcspn(p, "\"' \t\n\v")])
860     dstr_puts(d, p);
861   else {
862     dstr_putc(d, '\"');
863     while (*p) {
864       if (*p == '\\' || *p == '\"')
865         dstr_putc(d, '\\');
866       dstr_putc(d, *p++);
867     }
868     dstr_putc(d, '\"');
869   }
870 }
871
872 static void alertcmd(admin *a, unsigned f_and, unsigned f_eq,
873                      const char *tag, unsigned ac, char *av[])
874 {
875   dstr d = DSTR_INIT;
876   unsigned i;
877
878   dstr_puts(&d, "USER");
879   for (i = 0; i < ac; i++)
880     quotify(&d, av[i]);
881   dstr_putz(&d);
882   a_rawalert(f_and, f_eq, tag, d.buf, d.len);
883   dstr_destroy(&d);
884   a_ok(a);
885 }
886
887 static void acmd_notify(admin *a, unsigned ac, char *av[])
888   { alertcmd(a, AF_NOTE, AF_NOTE, "NOTE", ac, av); }
889 static void acmd_warn(admin *a, unsigned ac, char *av[])
890   { alertcmd(a, AF_WARN, AF_WARN, "WARN", ac, av); }
891
892 static void acmd_port(admin *a, unsigned ac, char *av[])
893 {
894   a_info(a, "%u", p_port());
895   a_ok(a);
896 }
897
898 static void acmd_daemon(admin *a, unsigned ac, char *av[])
899 {
900   if (flags & F_DAEMON)
901     a_fail(a, "already-daemon");
902   else {
903     a_notify("DAEMON");
904     if (a_stdin)
905       a_destroy(a_stdin);
906     if (u_daemon())
907       a_fail(a, "daemon-error -- %s", strerror(errno));
908     else {
909       flags |= F_DAEMON;
910       a_ok(a);
911     }
912   }
913 }
914
915 static void acmd_list(admin *a, unsigned ac, char *av[])
916 {
917   peer *p;
918   for (p = p_first(); p; p = p_next(p))
919     a_info(a, "%s", p_name(p));
920   a_ok(a);
921 }
922
923 static void acmd_ifname(admin *a, unsigned ac, char *av[])
924 {
925   peer *p;
926
927   if ((p = p_find(av[0])) == 0)
928     a_fail(a, "unknown-peer %s", av[0]);
929   else {
930     a_info(a, "%s", p_ifname(p));
931     a_ok(a);
932   }
933 }
934
935 static void acmd_addr(admin *a, unsigned ac, char *av[])
936 {
937   peer *p;
938   const addr *ad;
939
940   if ((p = p_find(av[0])) == 0)
941     a_fail(a, "unknown-peer %s", av[0]);
942   else {
943     ad = p_addr(p);
944     assert(ad->sa.sa_family == AF_INET);
945     a_info(a, "INET %s %u",
946             inet_ntoa(ad->sin.sin_addr),
947             (unsigned)ntohs(ad->sin.sin_port));
948     a_ok(a);
949   }
950 }
951
952 static void acmd_stats(admin *a, unsigned ac, char *av[])
953 {
954   peer *p;
955   stats *st;
956
957   if ((p = p_find(av[0])) == 0)
958     a_fail(a, "unknown-peer %s", av[0]);
959   else {
960     st = p_stats(p);
961     a_info(a, "start-time=%s", timestr(st->t_start));
962     a_info(a, "last-packet-time=%s", timestr(st->t_last));
963     a_info(a, "last-keyexch-time=%s", timestr(st->t_kx));
964     a_info(a, "packets-in=%lu bytes-in=%lu", st->n_in, st->sz_in);
965     a_info(a, "packets-out=%lu bytes-out=%lu",
966             st->n_out, st->sz_out);
967     a_info(a, "keyexch-packets-in=%lu keyexch-bytes-in=%lu",
968             st->n_kxin, st->sz_kxin);
969     a_info(a, "keyexch-packets-out=%lu keyexch-bytes-out=%lu",
970             st->n_kxout, st->sz_kxout);
971     a_info(a, "ip-packets-in=%lu ip-bytes-in=%lu",
972             st->n_ipin, st->sz_ipin);
973     a_info(a, "ip-packets-out=%lu ip-bytes-out=%lu",
974             st->n_ipout, st->sz_ipout);
975     a_info(a, "rejected-packets=%lu", st->n_reject);
976     a_ok(a);
977   }
978 }
979
980 static void acmd_kill(admin *a, unsigned ac, char *av[])
981 {
982   peer *p;
983   if ((p = p_find(av[0])) == 0)
984     a_fail(a, "unknown-peer %s", av[0]);
985   else {
986     p_destroy(p);
987     a_ok(a);
988   }
989 }
990
991 static void acmd_forcekx(admin *a, unsigned ac, char *av[])
992 {
993   peer *p;
994   if ((p = p_find(av[0])) == 0)
995     a_fail(a, "unknown-peer %s", av[0]);
996   else {
997     kx_start(&p->kx);
998     a_ok(a);
999   }
1000 }
1001
1002 static void acmd_quit(admin *a, unsigned ac, char *av[])
1003 {
1004   a_warn("SERVER quit admin-request");
1005   a_ok(a);
1006   a_quit();
1007 }
1008
1009 static void acmd_version(admin *a, unsigned ac, char *av[])
1010 {
1011   a_info(a, "%s %s", PACKAGE, VERSION);
1012   a_ok(a);
1013 }
1014
1015 static void acmd_tunnels(admin *a, unsigned ac, char *av[])
1016 {
1017   int i;
1018   for (i = 0; tunnels[i]; i++)
1019     a_info(a, "%s", tunnels[i]->name);
1020   a_ok(a);
1021 }
1022
1023 /* --- The command table and help --- */
1024
1025 typedef struct acmd {
1026   const char *name;
1027   const char *help;
1028   unsigned argmin, argmax;
1029   void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1030 } acmd;
1031
1032 static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1033
1034 static const acmd acmdtab[] = {
1035   { "help",     "help",                 0,      0,      acmd_help },
1036   { "version",  "version",              0,      0,      acmd_version },
1037 #ifndef NTRACE
1038   { "trace",    "trace [OPTIONS]",      0,      1,      acmd_trace },
1039 #endif
1040   { "watch",    "watch [OPTIONS]",      0,      1,      acmd_watch },
1041   { "notify",   "notify MESSAGE ...",   1,      0xffff, acmd_notify },
1042   { "warn",     "warn MESSAGE ...",     1,      0xffff, acmd_warn },
1043   { "port",     "port",                 0,      0,      acmd_port },
1044   { "daemon",   "daemon",               0,      0,      acmd_daemon },
1045   { "list",     "list",                 0,      0,      acmd_list },
1046   { "ifname",   "ifname PEER",          1,      1,      acmd_ifname },
1047   { "addr",     "addr PEER",            1,      1,      acmd_addr },
1048   { "stats",    "stats PEER",           1,      1,      acmd_stats },
1049   { "ping",     "ping [OPTIONS] PEER",  1,      0xffff, acmd_ping },
1050   { "eping",    "eping [OPTIONS] PEER", 1,      0xffff, acmd_eping },
1051   { "kill",     "kill PEER",            1,      1,      acmd_kill },
1052   { "forcekx",  "forcekx PEER",         1,      1,      acmd_forcekx },
1053   { "add",      "add PEER [OPTIONS] ADDR ...",
1054                                         2,      0xffff, acmd_add },
1055   { "tunnels",  "tunnels",              0,      0,      acmd_tunnels },
1056   { "quit",     "quit",                 0,      0,      acmd_quit },
1057   { 0,          0,                      0,      0,      0 }
1058 };
1059
1060 static void acmd_help(admin *a, unsigned ac, char *av[])
1061 {
1062   const acmd *c;
1063   for (c = acmdtab; c->name; c++)
1064     a_info(a, "%s", c->help);
1065   a_ok(a);
1066 }
1067
1068 /*----- Connection handling -----------------------------------------------*/
1069
1070 /* --- @a_lock@ --- *
1071  *
1072  * Arguments:   @admin *a@ = pointer to an admin block
1073  *
1074  * Returns:     ---
1075  *
1076  * Use:         Locks an admin block so that it won't be destroyed
1077  *              immediately.
1078  */
1079
1080 static void a_lock(admin *a) { assert(!(a->f & AF_LOCK)); a->f |= AF_LOCK; }
1081
1082 /* --- @a_unlock@ --- *
1083  *
1084  * Arguments:   @admin *a@ = pointer to an admin block
1085  *
1086  * Returns:     ---
1087  *
1088  * Use:         Unlocks an admin block, allowing its destruction.  This is
1089  *              also the second half of @a_destroy@.
1090  */
1091
1092 static void a_unlock(admin *a)
1093 {
1094   assert(a->f & AF_LOCK);
1095   if (!(a->f & AF_DEAD)) {
1096     a->f &= ~AF_LOCK;
1097     return;
1098   }
1099
1100   T( trace(T_ADMIN, "admin: completing destruction of connection %u",
1101            a->seq); )
1102
1103   selbuf_destroy(&a->b);
1104   if (a->peer.name) {
1105     xfree(a->peer.name);
1106     xfree(a->paddr);
1107     bres_abort(&a->r);
1108     sel_rmtimer(&a->t);
1109   }
1110   if (a->ping.p)
1111     p_pingdone(&a->ping, PING_NONOTIFY);
1112   if (a->b.reader.fd != a->w.fd)
1113     close(a->b.reader.fd);
1114   close(a->w.fd);
1115
1116   if (a_stdin == a)
1117     a_stdin = 0;
1118   if (a->next)
1119     a->next->prev = a->prev;
1120   if (a->prev)
1121     a->prev->next = a->next;
1122   else
1123     admins = a->next;
1124   DESTROY(a);
1125 }
1126
1127 /* --- @a_destroy@ --- *
1128  *
1129  * Arguments:   @admin *a@ = pointer to an admin block
1130  *
1131  * Returns:     ---
1132  *
1133  * Use:         Destroys an admin block.  This requires a certain amount of
1134  *              care.
1135  */
1136
1137 static void a_destroy(admin *a)
1138 {
1139   /* --- Don't multiply destroy admin blocks --- */
1140
1141   if (a->f & AF_DEAD)
1142     return;
1143
1144   /* --- Make sure nobody expects it to work --- */
1145
1146   a->f |= AF_DEAD;
1147   T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
1148
1149   /* --- Free the output buffers --- */
1150
1151   if (a->o_head) {
1152     obuf *o, *oo;
1153     sel_rmfile(&a->w);
1154     for (o = a->o_head; o; o = oo) {
1155       oo = o->next;
1156       xfree(o);
1157     }
1158     a->o_head = 0;
1159   }
1160
1161   /* --- If the block is locked, that's all we can manage --- */
1162
1163   if (a->f & AF_LOCK) {
1164     T( trace(T_ADMIN, "admin: deferring destruction..."); )
1165     return;
1166   }
1167   a->f |= AF_LOCK;
1168   a_unlock(a);
1169 }
1170
1171 /* --- @a_line@ --- *
1172  *
1173  * Arguments:   @char *p@ = pointer to the line read
1174  *              @size_t len@ = length of the line
1175  *              @void *vp@ = pointer to my admin block
1176  *
1177  * Returns:     ---
1178  *
1179  * Use:         Handles a line of input.
1180  */
1181
1182 static void a_line(char *p, size_t len, void *vp)
1183 {
1184   admin *a = vp;
1185   const acmd *c;
1186   char *av[16];
1187   size_t ac;
1188
1189   TIMER;
1190   if (a->f & AF_DEAD)
1191     return;
1192   if (!p) {
1193     a_destroy(a);
1194     return;
1195   }
1196   ac = str_qsplit(p, av, 16, 0, STRF_QUOTE);
1197   if (!ac)
1198     return;
1199   for (c = acmdtab; c->name; c++) {
1200     if (mystrieq(av[0], c->name)) {
1201       ac--;
1202       if (c->argmin > ac || ac > c->argmax)
1203         a_fail(a, "bad-syntax -- %s", c->help);
1204       else {
1205         a_lock(a);
1206         c->func(a, ac, av + 1);
1207         a_unlock(a);
1208       }
1209       return;
1210     }
1211   }
1212   a_fail(a, "unknown-command %s", av[0]);
1213 }
1214
1215 /* --- @a_create@ --- *
1216  *
1217  * Arguments:   @int fd_in, fd_out@ = file descriptors to use
1218  *              @unsigned f@ = initial flags to set
1219  *
1220  * Returns:     ---
1221  *
1222  * Use:         Creates a new admin connection.
1223  */
1224
1225 void a_create(int fd_in, int fd_out, unsigned f)
1226 {
1227   admin *a = CREATE(admin);
1228
1229   T( static unsigned seq = 0;
1230      a->seq = seq++; )
1231   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
1232   a->peer.name = 0;
1233   a->ping.p = 0;
1234   a->f = f;
1235   if (fd_in == STDIN_FILENO)
1236     a_stdin = a;
1237   fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1238   if (fd_out != fd_in)
1239     fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1240   selbuf_init(&a->b, &sel, fd_in, a_line, a);
1241   sel_initfile(&sel, &a->w, fd_out, SEL_WRITE, a_flush, a);
1242   a->o_head = 0;
1243   a->o_tail = 0;
1244   a->next = admins;
1245   a->prev = 0;
1246   if (admins)
1247     admins->prev = a;
1248   admins = a;
1249 }
1250
1251 /* --- @a_accept@ --- *
1252  *
1253  * Arguments:   @int fd@ = file descriptor to accept
1254  *              @unsigned mode@ = what to do
1255  *              @void *v@ = uninteresting pointer
1256  *
1257  * Returns:     ---
1258  *
1259  * Use:         Accepts a new admin connection.
1260  */
1261
1262 static void a_accept(int fd, unsigned mode, void *v)
1263 {
1264   int nfd;
1265   struct sockaddr_un sun;
1266   size_t sz = sizeof(sun);
1267
1268   if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
1269     if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK &&
1270         errno != ECONNABORTED && errno != EPROTO)
1271       a_warn("ADMIN accept-error -- %s", strerror(errno));
1272     return;
1273   }
1274   a_create(nfd, nfd, 0);
1275 }
1276
1277 /* --- @a_daemon@ --- *
1278  *
1279  * Arguments:   ---
1280  *
1281  * Returns:     ---
1282  *
1283  * Use:         Informs the admin module that it's a daemon.
1284  */
1285
1286 void a_daemon(void)
1287 {
1288   flags |= F_DAEMON;
1289 }
1290
1291 /* --- @a_init@ --- *
1292  *
1293  * Arguments:   @const char *name@ = socket name to create
1294  *
1295  * Returns:     ---
1296  *
1297  * Use:         Creates the admin listening socket.
1298  */
1299
1300 void a_init(const char *name)
1301 {
1302   int fd;
1303   int n = 5;
1304   struct sockaddr_un sun;
1305   struct sigaction sa;
1306   size_t sz;
1307
1308   /* --- Set up the socket address --- */
1309
1310   sz = strlen(name) + 1;
1311   if (sz > sizeof(sun.sun_path))
1312     die(EXIT_FAILURE, "socket name `%s' too long", name);
1313   BURN(sun);
1314   sun.sun_family = AF_UNIX;
1315   memcpy(sun.sun_path, name, sz);
1316   sz += offsetof(struct sockaddr_un, sun_path);
1317
1318   /* --- Attempt to bind to the socket --- */
1319
1320   umask(0077);
1321 again:
1322   if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1323     die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
1324   if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
1325     struct stat st;
1326     int e = errno;
1327     if (errno != EADDRINUSE) {
1328       die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
1329           sun.sun_path, strerror(e));
1330     }
1331     if (!n)
1332       die(EXIT_FAILURE, "too many retries; giving up");
1333     n--;
1334     if (!connect(fd, (struct sockaddr *)&sun, sz)) {
1335       die(EXIT_FAILURE, "server already listening on admin socket `%s'",
1336           sun.sun_path);
1337     }
1338     if (errno != ECONNREFUSED)
1339       die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
1340     if (stat(sun.sun_path, &st)) {
1341       die(EXIT_FAILURE, "couldn't stat `%s': %s",
1342           sun.sun_path, strerror(errno));
1343     }
1344     if (!S_ISSOCK(st.st_mode))
1345       die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
1346     T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
1347     unlink(sun.sun_path);
1348     close(fd);
1349     goto again;
1350   }
1351   chmod(sun.sun_path, 0600);
1352   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1353   if (listen(fd, 5))
1354     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
1355
1356   /* --- Listen to the socket --- */
1357
1358   sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
1359   sel_addfile(&sock);
1360   sockname = name;
1361   bres_init(&sel);
1362   T( trace_custom(a_trace, 0);
1363      trace(T_ADMIN, "admin: enabled custom tracing"); )
1364   flags |= F_INIT;
1365
1366   /* --- Set up signal handlers --- */
1367
1368   sig_add(&s_term, SIGTERM, a_sigdie, 0);
1369   sig_add(&s_hup, SIGHUP, a_sighup, 0);
1370   signal(SIGPIPE, SIG_IGN);
1371   sigaction(SIGINT, 0, &sa);
1372   if (sa.sa_handler != SIG_IGN)
1373     sig_add(&s_int, SIGINT, a_sigdie, 0);
1374 }
1375
1376 /*----- That's all, folks -------------------------------------------------*/