chiark / gitweb /
admin, resolve ops bugfix: initialize tag in resop early.
[tripe] / admin.c
CommitLineData
410c8acf 1/* -*-c-*-
2 *
2d752320 3 * $Id$
410c8acf 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
410c8acf 29/*----- Header files ------------------------------------------------------*/
30
31#include "tripe.h"
32
33/*----- Global variables --------------------------------------------------*/
34
35#ifndef NTRACE
36
37const trace_opt tr_opts[] = {
38 { 't', T_TUNNEL, "tunnel events" },
39 { 'r', T_PEER, "peer events" },
40 { 'a', T_ADMIN, "admin interface" },
410c8acf 41 { 's', T_KEYSET, "symmetric keyset management" },
42 { 'x', T_KEYEXCH, "key exchange" },
43 { 'm', T_KEYMGMT, "key management" },
37941236 44 { 'l', T_CHAL, "challenge management" },
45 { 'p', T_PACKET, "packet contents" },
46 { 'c', T_CRYPTO, "crypto details" },
410c8acf 47 { 'A', T_ALL, "all of the above" },
48 { 0, 0, 0 }
49};
50
51unsigned tr_flags = 0;
52#endif
53
3cdc3f3a 54static const trace_opt w_opts[] = {
55 { 't', AF_TRACE, "trace messages" },
56 { 'n', AF_NOTE, "asynchronous notifications" },
57 { 'w', AF_WARN, "warnings" },
58 { 'A', AF_ALLMSGS, "all of the above" },
59 { 0, 0, 0 }
60};
61
410c8acf 62/*----- Static variables --------------------------------------------------*/
63
64static admin *admins;
65static sel_file sock;
66static const char *sockname;
67static unsigned flags = 0;
68static admin *a_stdin = 0;
69static sig s_term, s_int, s_hup;
70
71#define F_DAEMON 1u
72#define F_INIT 2u
73
74#define T_RESOLVE SEC(30)
0ba8de86 75#define T_PING SEC(5)
410c8acf 76
fd3cf232 77static void a_destroy(admin */*a*/);
78static void a_lock(admin */*a*/);
79static void a_unlock(admin */*a*/);
80
060ca767 81#define BOOL(x) ((x) ? "t" : "nil")
82
fd3cf232 83/*----- Output functions --------------------------------------------------*/
84
85/* --- @trywrite@ --- *
86 *
87 * Arguments: @admin *a@ = pointer to an admin block
88 * @const char *p@ = pointer to buffer to write
89 * @size_t sz@ = size of data to write
90 *
91 * Returns: The number of bytes written, or less than zero on error.
92 *
93 * Use: Attempts to write data to a client.
94 */
95
96static ssize_t trywrite(admin *a, const char *p, size_t sz)
97{
98 ssize_t n, done = 0;
99
100again:
101 if (!sz)
102 return (done);
103 n = write(a->w.fd, p, sz);
104 if (n > 0) {
105 done += n;
106 p += n;
107 sz -= n;
108 goto again;
109 }
110 if (n < 0) {
111 if (errno == EINTR)
112 goto again;
113 if (errno != EAGAIN && errno != EWOULDBLOCK) {
114 a_destroy(a);
f43df819 115 a_warn("ADMIN", "client-write-error", "?ERRNO", A_END);
fd3cf232 116 return (-1);
117 }
118 }
119 return (done);
120}
121
de014da6 122/* --- @doqueue@ -- *
fd3cf232 123 *
de014da6 124 * Arguments: @oqueue *q@ = pointer to output queue
fd3cf232 125 * @const char *p@ = pointer to buffer to write
de014da6 126 * @size_t sz@ = size of buffer
fd3cf232 127 *
de014da6 128 * Returns: Nonzero if the queue was previously empty.
fd3cf232 129 *
de014da6 130 * Use: Queues data to be written later.
fd3cf232 131 */
132
de014da6 133static int doqueue(oqueue *q, const char *p, size_t sz)
fd3cf232 134{
fd3cf232 135 obuf *o;
de014da6 136 int rc = 0;
137 size_t n;
fd3cf232 138
de014da6 139 o = q->tl;
fd3cf232 140 if (!o)
de014da6 141 rc = 1;
fd3cf232 142 else if (o->p_in < o->buf + OBUFSZ)
143 goto noalloc;
144
145 do {
146 o = xmalloc(sizeof(obuf));
147 o->next = 0;
148 o->p_in = o->p_out = o->buf;
de014da6 149 if (q->tl)
150 q->tl->next = o;
fd3cf232 151 else
de014da6 152 q->hd = o;
153 q->tl = o;
fd3cf232 154
155 noalloc:
156 n = o->buf + OBUFSZ - o->p_in;
157 if (n > sz)
158 n = sz;
159 memcpy(o->p_in, p, n);
160 o->p_in += n;
161 p += n;
162 sz -= n;
163 } while (sz);
de014da6 164
165 return (rc);
166}
167
168/* --- @dosend@ --- *
169 *
170 * Arguemnts: @admin *a@ = pointer to an admin block
171 * @const char *p@ = pointer to buffer to write
172 * @size_t sz@ = size of data to write
173 *
174 * Returns: ---
175 *
176 * Use: Sends data to an admin client.
177 */
178
179static void dosend(admin *a, const char *p, size_t sz)
180{
181 ssize_t n;
182
183 if (a->f & AF_DEAD)
184 return;
185 if (!a->out.hd) {
186 if ((n = trywrite(a, p, sz)) < 0)
187 return;
188 p += n;
189 sz -= n;
190 if (!sz)
191 return;
192 }
193 if (doqueue(&a->out, p, sz))
194 sel_addfile(&a->w);
fd3cf232 195}
196
197/* --- @a_flush@ --- *
198 *
199 * Arguments: @int fd@ = file descriptor
200 * @unsigned mode@ = what's happening
201 * @void *v@ = pointer to my admin block
202 *
203 * Returns: ---
204 *
205 * Use: Flushes buffers when a client is ready to read again.
206 */
207
208static void a_flush(int fd, unsigned mode, void *v)
209{
210 admin *a = v;
211 obuf *o, *oo;
212 ssize_t n;
213
de014da6 214 o = a->out.hd;
fd3cf232 215 while (o) {
216 if ((n = trywrite(a, o->p_out, o->p_in - o->p_out)) < 0)
217 return;
218 o->p_out += n;
219 if (o->p_in < o->p_out)
220 break;
221 oo = o;
222 o = o->next;
223 xfree(oo);
224 }
de014da6 225 a->out.hd = o;
fd3cf232 226 if (!o) {
de014da6 227 a->out.tl = 0;
fd3cf232 228 sel_rmfile(&a->w);
229 }
230}
231
410c8acf 232/*----- Utility functions -------------------------------------------------*/
233
f43df819
MW
234/* --- @quotify@ --- *
235 *
236 * Arguments: @dstr *d@ = where to write the answer
237 * @const char *p@ = string to quotify
238 *
239 * Returns: ---
240 *
241 * Use: Quotes the given string if necessary, according to our
242 * quoting rules.
243 */
244
245static void quotify(dstr *d, const char *p)
246{
247 if (d->len)
248 dstr_putc(d, ' ');
249 if (*p && !p[strcspn(p, "\"' \t\n\v")])
250 dstr_puts(d, p);
251 else {
252 dstr_putc(d, '\"');
253 while (*p) {
254 if (*p == '\\' || *p == '\"')
255 dstr_putc(d, '\\');
256 dstr_putc(d, *p++);
257 }
258 dstr_putc(d, '\"');
259 }
260}
261
262/* --- @a_vformat@ --- *
263 *
264 * Arguments: @dstr *d@ = where to leave the formatted message
265 * @const char *fmt@ = pointer to format string
266 * @va_list ap@ = arguments in list
267 *
268 * Returns: ---
269 *
270 * Use: Main message token formatting driver.
271 */
272
273static void a_vformat(dstr *d, const char *fmt, va_list ap)
274{
275 dstr dd = DSTR_INIT;
276
277 while (fmt) {
278 if (*fmt == '*') {
279 dstr_putc(d, ' ');
280 dstr_vputf(d, fmt + 1, &ap);
281 } else if (*fmt == '?') {
282 if (strcmp(fmt, "?ADDR") == 0) {
283 const addr *a = va_arg(ap, const addr *);
284 switch (a->sa.sa_family) {
285 case AF_INET:
286 quotify(d, "INET");
287 quotify(d, inet_ntoa(a->sin.sin_addr));
288 dstr_putf(d, " %u", (unsigned)ntohs(a->sin.sin_port));
289 break;
290 default:
291 abort();
292 }
293 } else if (strcmp(fmt, "?B64") == 0) {
294 const octet *p = va_arg(ap, const octet *);
295 size_t n = va_arg(ap, size_t);
296 base64_ctx b64;
297 dstr_putc(d, ' ');
298 base64_init(&b64);
299 b64.indent = "";
300 b64.maxline = 0;
301 base64_encode(&b64, p, n, d);
302 base64_encode(&b64, 0, 0, d);
303 while (d->len && d->buf[d->len - 1] == '=') d->len--;
304 } else if (strcmp(fmt, "?PEER") == 0)
305 quotify(d, p_name(va_arg(ap, peer *)));
306 else if (strcmp(fmt, "?ERRNO") == 0) {
307 dstr_putf(d, " E%d", errno);
308 quotify(d, strerror(errno));
309 } else
310 abort();
311 } else {
312 if (*fmt == '!') fmt++;
313 DRESET(&dd);
314 dstr_vputf(&dd, fmt, &ap);
315 quotify(d, dd.buf);
316 }
317 fmt = va_arg(ap, const char *);
318 }
319
320 dstr_destroy(&dd);
321}
322
3cdc3f3a 323/* --- @a_write@, @a_vwrite@ --- *
410c8acf 324 *
325 * Arguments: @admin *a@ = admin connection to write to
de014da6 326 * @const char *status@ = status code to report
327 * @const char *tag@ = tag string, or null
410c8acf 328 * @const char *fmt@ = pointer to format string
3cdc3f3a 329 * @va_list ap@ = arguments in list
410c8acf 330 * @...@ = other arguments
331 *
332 * Returns: ---
333 *
334 * Use: Sends a message to an admin connection.
335 */
336
de014da6 337static void a_vwrite(admin *a, const char *status, const char *tag,
338 const char *fmt, va_list ap)
410c8acf 339{
410c8acf 340 dstr d = DSTR_INIT;
de014da6 341 if (tag) dstr_puts(&d, "BG");
342 dstr_puts(&d, status);
f43df819
MW
343 if (tag) quotify(&d, tag);
344 a_vformat(&d, fmt, ap);
3cdc3f3a 345 dstr_putc(&d, '\n');
346 dosend(a, d.buf, d.len);
347 dstr_destroy(&d);
348}
349
de014da6 350static void a_write(admin *a, const char *status, const char *tag,
351 const char *fmt, ...)
3cdc3f3a 352{
353 va_list ap;
410c8acf 354 va_start(ap, fmt);
de014da6 355 a_vwrite(a, status, tag, fmt, ap);
410c8acf 356 va_end(ap);
3cdc3f3a 357}
358
359/* --- @a_ok@, @a_info@, @a_fail@ --- *
360 *
361 * Arguments: @admin *a@ = connection
362 * @const char *fmt@ = format string
363 * @...@ = other arguments
364 *
365 * Returns: ---
366 *
367 * Use: Convenience functions for @a_write@.
368 */
369
f43df819 370static void a_ok(admin *a) { a_write(a, "OK", 0, A_END); }
3cdc3f3a 371
372static void a_info(admin *a, const char *fmt, ...)
373{
374 va_list ap;
375 va_start(ap, fmt);
de014da6 376 a_vwrite(a, "INFO", 0, fmt, ap);
3cdc3f3a 377 va_end(ap);
378}
379
380static void a_fail(admin *a, const char *fmt, ...)
381{
382 va_list ap;
383 va_start(ap, fmt);
de014da6 384 a_vwrite(a, "FAIL", 0, fmt, ap);
3cdc3f3a 385 va_end(ap);
386}
387
388/* --- @a_alert@, @a_valert@, @a_rawalert@ --- *
389 *
390 * Arguments: @unsigned f_and, f_eq@ = filter for connections
de014da6 391 * @const char *status@ = status string
3cdc3f3a 392 * @const char *fmt@ = pointer to format string
de014da6 393 * @const char *p@ = pointer to raw string
3cdc3f3a 394 * @size_t sz@ = size of raw string
395 * @va_list ap@ = arguments in list
396 * @...@ = other arguments
397 *
398 * Returns: ---
399 *
400 * Use: Write a message to all admin connections matched by the given
401 * filter.
402 */
403
de014da6 404static void a_rawalert(unsigned f_and, unsigned f_eq, const char *status,
3cdc3f3a 405 const char *p, size_t sz)
406{
407 admin *a, *aa;
408 dstr d = DSTR_INIT;
409
410 if (!(flags & F_INIT))
411 return;
de014da6 412 dstr_puts(&d, status);
413 if (p) {
414 dstr_putc(&d, ' ');
3cdc3f3a 415 dstr_putm(&d, p, sz);
de014da6 416 }
3cdc3f3a 417 dstr_putc(&d, '\n');
418 p = d.buf;
419 sz = d.len;
420 for (a = admins; a; a = aa) {
421 aa = a->next;
422 if ((a->f & f_and) == f_eq)
423 dosend(a, d.buf, d.len);
424 }
410c8acf 425 dstr_destroy(&d);
426}
427
3cdc3f3a 428static void a_valert(unsigned f_and, unsigned f_eq, const char *tag,
429 const char *fmt, va_list ap)
430{
431 dstr d = DSTR_INIT;
432
433 if (!(flags & F_INIT))
434 return;
f43df819 435 a_vformat(&d, fmt, ap);
3cdc3f3a 436 a_rawalert(f_and, f_eq, tag, fmt ? d.buf : 0, fmt ? d.len : 0);
437 dstr_destroy(&d);
438}
439
440#if 0 /*unused*/
441static void a_alert(unsigned f_and, unsigned f_eq, const char *tag,
442 const char *fmt, ...)
443{
444 va_list ap;
445 va_start(ap, fmt);
446 a_valert(f_and, f_eq, tag, fmt, ap);
447 va_end(ap);
448}
449#endif
450
410c8acf 451/* --- @a_warn@ --- *
452 *
453 * Arguments: @const char *fmt@ = pointer to format string
454 * @...@ = other arguments
455 *
456 * Returns: ---
457 *
458 * Use: Informs all admin connections of a warning.
459 */
460
461void a_warn(const char *fmt, ...)
462{
463 va_list ap;
410c8acf 464
410c8acf 465 va_start(ap, fmt);
3cdc3f3a 466 if (flags & F_INIT)
467 a_valert(0, 0, "WARN", fmt, ap);
410c8acf 468 else {
f43df819 469 dstr d = DSTR_INIT;
3cdc3f3a 470 fprintf(stderr, "%s: ", QUIS);
f43df819
MW
471 a_vformat(&d, fmt, ap);
472 dstr_putc(&d, '\n');
473 dstr_write(&d, stderr);
474 dstr_destroy(&d);
410c8acf 475 }
3cdc3f3a 476 va_end(ap);
410c8acf 477}
478
479/* --- @a_trace@ --- *
480 *
481 * Arguments: @const char *p@ = pointer to a buffer
482 * @size_t sz@ = size of the buffer
483 * @void *v@ = uninteresting pointer
484 *
485 * Returns: ---
486 *
3cdc3f3a 487 * Use: Custom trace output handler. Sends trace messages to
488 * interested admin connections.
410c8acf 489 */
490
491#ifndef NTRACE
492static void a_trace(const char *p, size_t sz, void *v)
de014da6 493 { a_rawalert(AF_TRACE, AF_TRACE, "TRACE", p, sz); }
410c8acf 494#endif
495
3cdc3f3a 496/* --- @a_notify@ --- *
497 *
498 * Arguments: @const char *fmt@ = pointer to format string
499 * @...@ = other arguments
500 *
501 * Returns: ---
502 *
503 * Use: Sends a notification to interested admin connections.
504 */
505
506void a_notify(const char *fmt, ...)
507{
508 va_list ap;
509
510 va_start(ap, fmt);
511 a_valert(AF_NOTE, AF_NOTE, "NOTE", fmt, ap);
512 va_end(ap);
513}
514
410c8acf 515/* --- @a_quit@ --- *
516 *
517 * Arguments: ---
518 *
519 * Returns: ---
520 *
521 * Use: Shuts things down nicely.
522 */
523
524void a_quit(void)
525{
42da2a58 526 peer *p;
527
410c8acf 528 close(sock.fd);
529 unlink(sockname);
de014da6 530 while ((p = p_first()) != 0)
531 p_destroy(p);
410c8acf 532 exit(0);
533}
534
535/* --- @a_sigdie@ --- *
536 *
537 * Arguments: @int sig@ = signal number
538 * @void *v@ = an uninteresting argument
539 *
540 * Returns: ---
541 *
542 * Use Shuts down on receipt of a fatal signal.
543 */
544
545static void a_sigdie(int sig, void *v)
546{
547 char *p;
548 char buf[20];
549
550 switch (sig) {
551 case SIGTERM: p = "SIGTERM"; break;
552 case SIGINT: p = "SIGINT"; break;
553 default:
3cdc3f3a 554 sprintf(buf, "%i", sig);
410c8acf 555 p = buf;
556 break;
557 }
f43df819 558 a_warn("SERVER", "quit", "signal", "%s", p, A_END);
410c8acf 559 a_quit();
560}
561
562/* --- @a_sighup@ --- *
563 *
564 * Arguments: @int sig@ = signal number
565 * @void *v@ = an uninteresting argument
566 *
567 * Returns: ---
568 *
569 * Use Logs a message about SIGHUP not being useful.
570 */
571
572static void a_sighup(int sig, void *v)
573{
f43df819 574 a_warn("SERVER", "ignore", "signal", "SIGHUP", A_END);
410c8acf 575}
576
0ba8de86 577/* --- @a_parsetime@ --- *
578 *
579 * Arguments; @const char *p@ = time string to parse
580 *
581 * Returns: Time in seconds, or @< 0@ on error.
582 */
583
584static long a_parsetime(const char *p)
585{
586 char *q;
587 long t = strtol(p, &q, 0);
588
589 switch (*q) {
590 case 'd': t *= 24;
591 case 'h': t *= 60;
592 case 'm': t *= 60;
593 case 's': if (q[1] != 0)
594 default: t = -1;
595 case 0: break;
596 }
597 return (t);
598}
599
37941236 600/* --- @a_findpeer@ --- *
601 *
602 * Arguments: @admin *a@ = admin connection
603 * @const char *pn@ = peer name
604 *
605 * Returns: The peer, or null if not there.
606 *
607 * Use: Finds a peer, reporting an error if it failed.
608 */
609
610static peer *a_findpeer(admin *a, const char *pn)
611{
612 peer *p;
613
614 if ((p = p_find(pn)) == 0)
f43df819 615 a_fail(a, "unknown-peer", "%s", pn, A_END);
37941236 616 return (p);
617}
618
de014da6 619/*----- Backgrounded operations -------------------------------------------*/
620
060ca767 621#define BGTAG(bg) \
622 (((admin_bgop *)(bg))->tag ? ((admin_bgop *)(bg))->tag : "<foreground>")
623
de014da6 624/* --- @a_bgrelease@ --- *
625 *
626 * Arguments: @admin_bgop *bg@ = backgrounded operation
627 *
628 * Returns: ---
629 *
630 * Use: Removes a backgrounded operation from the queue, since
631 * (presumably) it's done.
632 */
633
634static void a_bgrelease(admin_bgop *bg)
635{
636 admin *a = bg->a;
637
060ca767 638 T( trace(T_ADMIN, "admin: release bgop %s", BGTAG(bg)); )
639 if (bg->tag) xfree(bg->tag);
640 else selbuf_enable(&a->b);
641 if (bg->next) bg->next->prev = bg->prev;
642 if (bg->prev) bg->prev->next = bg->next;
643 else a->bg = bg->next;
de014da6 644 xfree(bg);
060ca767 645 if (a->f & AF_CLOSE) a_destroy(a);
646 a_unlock(a);
de014da6 647}
648
649/* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- *
650 *
651 * Arguments: @admin_bgop *bg@ = backgrounded operation
652 * @const char *fmt@ = format string
653 * @...@ = other arguments
654 *
655 * Returns: ---
656 *
657 * Use: Convenience functions for @a_write@.
658 */
659
660static void a_bgok(admin_bgop *bg)
f43df819 661 { a_write(bg->a, "OK", bg->tag, A_END); }
de014da6 662
663static void a_bginfo(admin_bgop *bg, const char *fmt, ...)
664{
665 va_list ap;
666 va_start(ap, fmt);
667 a_vwrite(bg->a, "INFO", bg->tag, fmt, ap);
668 va_end(ap);
669}
670
671static void a_bgfail(admin_bgop *bg, const char *fmt, ...)
672{
673 va_list ap;
674 va_start(ap, fmt);
675 a_vwrite(bg->a, "FAIL", bg->tag, fmt, ap);
676 va_end(ap);
677}
678
679/* --- @a_bgadd@ --- *
680 *
681 * Arguments: @admin *a@ = administration connection
682 * @admin_bgop *bg@ = pointer to background operation
683 * @const char *tag@ = background tag, or null for foreground
684 * @void (*cancel)(admin_bgop *)@ = cancel function
685 *
686 * Returns: ---
687 *
688 * Use: Links a background job into the list.
689 */
690
691static void a_bgadd(admin *a, admin_bgop *bg, const char *tag,
692 void (*cancel)(admin_bgop *))
693{
694 if (tag)
695 bg->tag = xstrdup(tag);
696 else {
697 bg->tag = 0;
698 selbuf_disable(&a->b);
699 }
700 bg->a = a;
701 bg->cancel = cancel;
702 bg->next = a->bg;
703 bg->prev = 0;
060ca767 704 if (a->bg) a->bg->prev = bg;
de014da6 705 a->bg = bg;
060ca767 706 a_lock(a);
707 T( trace(T_ADMIN, "admin: add bgop %s", BGTAG(bg)); )
f43df819 708 if (tag) a_write(a, "DETACH", tag, A_END);
de014da6 709}
710
37941236 711/*----- Name resolution operations ----------------------------------------*/
de014da6 712
37941236 713/* --- @a_resolved@ --- *
de014da6 714 *
37941236 715 * Arguments: @struct hostent *h@ = pointer to resolved hostname
716 * @void *v@ = pointer to resolver operation
de014da6 717 *
718 * Returns: ---
719 *
37941236 720 * Use: Handles a completed name resolution.
de014da6 721 */
722
37941236 723static void a_resolved(struct hostent *h, void *v)
de014da6 724{
37941236 725 admin_resop *r = v;
726
727 T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); )
728 TIMER;
729 if (!h) {
f43df819 730 a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END);
37941236 731 r->func(r, ARES_FAIL);
732 } else {
733 memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
734 r->func(r, ARES_OK);
735 }
736 sel_rmtimer(&r->t);
737 xfree(r->addr);
738 a_bgrelease(&r->bg);
739}
de014da6 740
37941236 741/* --- @a_restimer@ --- *
de014da6 742 *
37941236 743 * Arguments: @struct timeval *tv@ = timer
744 * @void *v@ = pointer to resolver operation
de014da6 745 *
746 * Returns: ---
747 *
37941236 748 * Use: Times out a resolver.
de014da6 749 */
750
37941236 751static void a_restimer(struct timeval *tv, void *v)
de014da6 752{
37941236 753 admin_resop *r = v;
754
755 T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); )
f43df819 756 a_bgfail(&r->bg, "resolver-timeout", "%s", r->addr, A_END);
37941236 757 r->func(r, ARES_FAIL);
758 bres_abort(&r->r);
759 xfree(r->addr);
760 a_bgrelease(&r->bg);
de014da6 761}
762
37941236 763/* --- @a_rescancel@ --- *
de014da6 764 *
37941236 765 * Arguments: @admin_bgop *bg@ = background operation
de014da6 766 *
767 * Returns: ---
768 *
37941236 769 * Use: Cancels an add operation.
de014da6 770 */
771
37941236 772static void a_rescancel(admin_bgop *bg)
de014da6 773{
37941236 774 admin_resop *r = (admin_resop *)bg;
775
776 T( trace(T_ADMIN, "admin: cancel resop %s", BGTAG(r)); )
777 r->func(r, ARES_FAIL);
778 sel_rmtimer(&r->t);
779 xfree(r->addr);
780 bres_abort(&r->r);
de014da6 781}
37941236 782
783/* --- @a_resolve@ --- *
410c8acf 784 *
37941236 785 * Arguments: @admin *a@ = administration connection
786 * @admin_resop *r@ = resolver operation to run
787 * @const char *tag@ = background operation tag
788 * @void (*func)(struct admin_resop *, int@ = handler function
789 * @unsigned ac@ = number of remaining arguments
790 * @char *av[]@ = pointer to remaining arguments
410c8acf 791 *
792 * Returns: ---
793 *
37941236 794 * Use: Cranks up a resolver job.
410c8acf 795 */
796
37941236 797static void a_resolve(admin *a, admin_resop *r, const char *tag,
798 void (*func)(struct admin_resop *, int),
799 unsigned ac, char *av[])
410c8acf 800{
37941236 801 struct timeval tv;
802 unsigned long pt;
803 char *p;
804 int i = 0;
fd3cf232 805
37941236 806 /* --- Fill in the easy bits of address --- */
807
452bf3f6 808 r->bg.tag = "<starting>";
37941236 809 r->addr = 0;
810 r->func = func;
811 if (mystrieq(av[i], "inet")) i++;
812 if (ac - i != 2) {
f43df819 813 a_fail(a, "bad-addr-syntax", "[inet] ADDRESS PORT", A_END);
37941236 814 goto fail;
815 }
816 r->sa.sin.sin_family = AF_INET;
817 r->sasz = sizeof(r->sa.sin);
818 r->addr = xstrdup(av[i]);
819 pt = strtoul(av[i + 1], &p, 0);
820 if (*p) {
821 struct servent *s = getservbyname(av[i + 1], "udp");
822 if (!s) {
f43df819 823 a_fail(a, "unknown-service", "%s", av[i + 1], A_END);
37941236 824 goto fail;
825 }
826 pt = ntohs(s->s_port);
410c8acf 827 }
37941236 828 if (pt == 0 || pt >= 65536) {
f43df819 829 a_fail(a, "invalid-port", "%lu", pt, A_END);
37941236 830 goto fail;
831 }
832 r->sa.sin.sin_port = htons(pt);
833
834 /* --- Report backgrounding --- *
835 *
836 * Do this for consistency of interface, even if we're going to get the
837 * answer straight away.
838 */
839
840 a_bgadd(a, &r->bg, tag, a_rescancel);
841 T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
842 a->seq, BGTAG(r), r->addr); )
843
844 /* --- If the name is numeric, do it the easy way --- */
845
846 if (inet_aton(av[i], &r->sa.sin.sin_addr)) {
847 T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
848 func(r, ARES_OK);
849 xfree(r->addr);
850 a_bgrelease(&r->bg);
851 return;
852 }
853
854 /* --- Store everything for later and crank up the resolver --- */
855
856 gettimeofday(&tv, 0);
857 tv.tv_sec += T_RESOLVE;
858 sel_addtimer(&sel, &r->t, &tv, a_restimer, r);
859 bres_byname(&r->r, r->addr, a_resolved, r);
860 return;
861
862fail:
863 func(r, ARES_FAIL);
864 if (r->addr) xfree(r->addr);
865 xfree(r);
410c8acf 866}
867
37941236 868/*----- Adding peers ------------------------------------------------------*/
869
870/* --- @a_doadd@ --- *
410c8acf 871 *
37941236 872 * Arguments: @admin_resop *r@ = resolver operation
873 * @int rc@ = how it worked
410c8acf 874 *
875 * Returns: ---
876 *
37941236 877 * Use: Handles a completed resolution.
410c8acf 878 */
879
37941236 880static void a_doadd(admin_resop *r, int rc)
410c8acf 881{
37941236 882 admin_addop *add = (admin_addop *)r;
883
884 T( trace(T_ADMIN, "admin: done add op %s", BGTAG(add)); )
885
886 if (rc == ARES_OK) {
887 add->peer.sasz = add->r.sasz;
888 add->peer.sa = add->r.sa;
889 if (p_find(add->peer.name))
f43df819 890 a_bgfail(&add->r.bg, "peer-exists", "%s", add->peer.name, A_END);
37941236 891 else if (!p_create(&add->peer))
f43df819 892 a_bgfail(&add->r.bg, "peer-create-fail", "%s", add->peer.name, A_END);
37941236 893 else
894 a_bgok(&add->r.bg);
895 }
de014da6 896
37941236 897 xfree(add->peer.name);
410c8acf 898}
899
900/* --- @acmd_add@ --- *
901 *
902 * Arguments: @admin *a@ = connection which requested the addition
903 * @unsigned ac@ = argument count
904 * @char *av[]@ = pointer to the argument list
905 *
906 * Returns: ---
907 *
908 * Use: Adds a new peer.
909 */
910
911static void acmd_add(admin *a, unsigned ac, char *av[])
912{
42da2a58 913 unsigned i, j;
de014da6 914 const char *tag = 0;
915 admin_addop *add = 0;
410c8acf 916
0ba8de86 917 /* --- Set stuff up --- */
918
de014da6 919 add = xmalloc(sizeof(*add));
920 add->peer.name = xstrdup(av[0]);
921 add->peer.t_ka = 0;
922 add->peer.tops = tun_default;
37941236 923
924 /* --- Make sure someone's not got there already --- */
925
926 if (p_find(av[0])) {
f43df819 927 a_fail(a, "peer-exists", "%s", av[0], A_END);
37941236 928 goto fail;
929 }
0ba8de86 930
42da2a58 931 /* --- Parse options --- */
932
933 i = 1;
934 for (;;) {
935 if (!av[i])
936 goto bad_syntax;
de014da6 937 if (mystrieq(av[i], "-background")) {
938 if (!av[++i]) goto bad_syntax;
939 tag = av[i];
940 } else if (mystrieq(av[i], "-tunnel")) {
0ba8de86 941 if (!av[++i]) goto bad_syntax;
42da2a58 942 for (j = 0;; j++) {
943 if (!tunnels[j]) {
f43df819 944 a_fail(a, "unknown-tunnel", "%s", av[i], A_END);
37941236 945 goto fail;
42da2a58 946 }
947 if (mystrieq(av[i], tunnels[j]->name)) {
de014da6 948 add->peer.tops = tunnels[j];
42da2a58 949 break;
950 }
951 }
0ba8de86 952 } else if (mystrieq(av[i], "-keepalive")) {
953 long t;
954 if (!av[++i]) goto bad_syntax;
955 if ((t = a_parsetime(av[i])) < 0) {
f43df819 956 a_fail(a, "bad-time-spec", "%s", av[i], A_END);
37941236 957 goto fail;
0ba8de86 958 }
de014da6 959 add->peer.t_ka = t;
42da2a58 960 } else if (mystrieq(av[i], "--")) {
961 i++;
962 break;
963 } else
964 break;
0ba8de86 965 i++;
42da2a58 966 }
967
37941236 968 /* --- Crank up the resolver --- */
de014da6 969
37941236 970 a_resolve(a, &add->r, tag, a_doadd, ac - i, av + i);
42da2a58 971 return;
972
37941236 973 /* --- Clearing up --- */
974
42da2a58 975bad_syntax:
f43df819 976 a_fail(a, "bad-syntax", "add", "PEER [OPTIONS] ADDR ...", A_END);
de014da6 977fail:
37941236 978 xfree(add->peer.name);
979 xfree(add);
0ba8de86 980 return;
981}
982
983/*----- Ping --------------------------------------------------------------*/
984
de014da6 985/* --- @a_pingcancel@ --- *
986 *
987 * Arguments: @admin_bgop *bg@ = background operation block
988 *
989 * Returns: ---
990 *
991 * Use: Cancels a running ping.
992 */
993
994static void a_pingcancel(admin_bgop *bg)
995{
996 admin_pingop *pg = (admin_pingop *)bg;
060ca767 997 T( trace(T_ADMIN, "admin: cancel ping op %s", BGTAG(pg)); )
de014da6 998 p_pingdone(&pg->ping, PING_NONOTIFY);
999}
1000
0ba8de86 1001/* --- @a_pong@ --- *
1002 *
1003 * Arguments: @int rc@ = return code
de014da6 1004 * @void *v@ = ping operation block
0ba8de86 1005 *
1006 * Returns: ---
1007 *
1008 * Use: Collects what happened to a ping message.
1009 */
1010
de014da6 1011static void a_pong(int rc, void *v)
0ba8de86 1012{
de014da6 1013 admin_pingop *pg = v;
0ba8de86 1014 struct timeval tv;
1015 double millis;
1016
0ba8de86 1017 switch (rc) {
1018 case PING_OK:
1019 gettimeofday(&tv, 0);
de014da6 1020 tv_sub(&tv, &tv, &pg->pingtime);
0ba8de86 1021 millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
f43df819 1022 a_bginfo(&pg->bg, "ping-ok", "%.1f", millis, A_END);
de014da6 1023 a_bgok(&pg->bg);
0ba8de86 1024 break;
1025 case PING_TIMEOUT:
f43df819 1026 a_bginfo(&pg->bg, "ping-timeout", A_END);
de014da6 1027 a_bgok(&pg->bg);
0ba8de86 1028 break;
1029 case PING_PEERDIED:
f43df819 1030 a_bginfo(&pg->bg, "ping-peer-died", A_END);
de014da6 1031 a_bgok(&pg->bg);
0ba8de86 1032 break;
1033 default:
1034 abort();
1035 }
060ca767 1036 T( trace(T_ADMIN, "admin: ponged ping op %s", BGTAG(pg)); )
de014da6 1037 a_bgrelease(&pg->bg);
0ba8de86 1038}
1039
1040/* --- @acmd_ping@, @acmd_eping@ --- *
1041 *
1042 * Arguments: @admin *a@ = connection which requested the ping
1043 * @unsigned ac@ = argument count
1044 * @char *av[]@ = pointer to the argument list
1045 *
1046 * Returns: ---
1047 *
1048 * Use: Pings a peer.
1049 */
1050
1051static void a_ping(admin *a, unsigned ac, char *av[],
1052 const char *cmd, unsigned msg)
1053{
1054 long t = T_PING;
1055 int i;
1056 peer *p;
de014da6 1057 admin_pingop *pg = 0;
1058 const char *tag = 0;
0ba8de86 1059
1060 i = 0;
1061 for (;;) {
1062 if (!av[i])
1063 goto bad_syntax;
de014da6 1064 if (mystrieq(av[i], "-background")) {
1065 if (!av[++i]) goto bad_syntax;
1066 tag = av[i];
1067 } else if (mystrieq(av[i], "-timeout")) {
0ba8de86 1068 if (!av[++i]) goto bad_syntax;
1069 if ((t = a_parsetime(av[i])) < 0) {
f43df819 1070 a_fail(a, "bad-time-spec", "%s", av[i], A_END);
0ba8de86 1071 return;
1072 }
1073 } else if (mystrieq(av[i], "--")) {
1074 i++;
1075 break;
1076 } else
1077 break;
1078 i++;
1079 }
1080
1081 if (!av[i]) goto bad_syntax;
37941236 1082 if ((p = a_findpeer(a, av[i])) == 0)
0ba8de86 1083 return;
de014da6 1084 pg = xmalloc(sizeof(*pg));
1085 gettimeofday(&pg->pingtime, 0);
1086 a_bgadd(a, &pg->bg, tag, a_pingcancel);
060ca767 1087 T( trace(T_ADMIN, "admin: ping op %s: %s to %s",
1088 BGTAG(pg), cmd, p_name(p)); )
de014da6 1089 if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) {
f43df819 1090 a_bgfail(&pg->bg, "ping-send-failed", A_END);
de014da6 1091 a_bgrelease(&pg->bg);
1092 }
0ba8de86 1093 return;
1094
1095bad_syntax:
f43df819 1096 a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
42da2a58 1097 return;
410c8acf 1098}
1099
0ba8de86 1100static void acmd_ping(admin *a, unsigned ac, char *av[])
1101 { a_ping(a, ac, av, "ping", MISC_PING); }
1102static void acmd_eping(admin *a, unsigned ac, char *av[])
1103 { a_ping(a, ac, av, "eping", MISC_EPING); }
0ba8de86 1104
410c8acf 1105/*----- Administration commands -------------------------------------------*/
1106
1107/* --- Miscellaneous commands --- */
1108
3cdc3f3a 1109/* --- @traceish@ --- *
1110 *
1111 * Arguments: @admin *a@ = connection to complain on
1112 * @unsigned ac@ = number of arguments
1113 * @char *av[]@ = vector of arguments
1114 * @const char *what@ = what we're messing with
1115 * @const trace_opt *tt@ = options table
1116 * @unsigned *ff@ = where the flags are
1117 *
1118 * Returns: Nonzero if anything changed.
1119 *
1120 * Use: Guts of trace-ish commands like `trace' and `watch'.
1121 */
410c8acf 1122
3cdc3f3a 1123static int traceish(admin *a, unsigned ac, char *av[],
1124 const char *what, const trace_opt *tt, unsigned *ff)
410c8acf 1125{
3cdc3f3a 1126 int ch = 0;
1127
410c8acf 1128 if (!ac || strcmp(av[0], "?") == 0) {
1129 const trace_opt *t;
3cdc3f3a 1130 for (t = tt; t->ch; t++) {
f43df819
MW
1131 a_info(a, "*%c%c %s",
1132 t->ch, (*ff & t->f) == t->f ? '+' : ' ', t->help, A_END);
410c8acf 1133 }
1134 } else {
1135 unsigned sense = 1;
3cdc3f3a 1136 unsigned f = *ff;
1137 const trace_opt *t;
410c8acf 1138 char *p = av[0];
1139
1140 while (*p) {
1141 switch (*p) {
1142 case '+': sense = 1; break;
1143 case '-': sense = 0; break;
1144 default:
3cdc3f3a 1145 for (t = tt; t->ch; t++) {
1146 if (t->ch == *p) {
1147 if (sense) f |= t->f;
1148 else f &= ~t->f;
410c8acf 1149 goto tropt_ok;
1150 }
1151 }
f43df819 1152 a_fail(a, "bad-%s-option", what, "%c", *p, A_END);
3cdc3f3a 1153 return (0);
410c8acf 1154 tropt_ok:;
1155 break;
1156 }
1157 p++;
1158 }
3cdc3f3a 1159 *ff = f;
1160 ch = 1;
410c8acf 1161 }
3cdc3f3a 1162 a_ok(a);
1163 return (ch);
1164}
1165
1166#ifndef NTRACE
1167
1168static void acmd_trace(admin *a, unsigned ac, char *av[])
1169{
1170 if (traceish(a, ac, av, "trace", tr_opts, &tr_flags))
1171 trace_level(tr_flags);
410c8acf 1172}
1173
1174#endif
1175
3cdc3f3a 1176static void acmd_watch(admin *a, unsigned ac, char *av[])
1177{
1178 traceish(a, ac, av, "watch", w_opts, &a->f);
1179}
1180
3cdc3f3a 1181static void alertcmd(admin *a, unsigned f_and, unsigned f_eq,
1182 const char *tag, unsigned ac, char *av[])
1183{
1184 dstr d = DSTR_INIT;
1185 unsigned i;
1186
1187 dstr_puts(&d, "USER");
1188 for (i = 0; i < ac; i++)
1189 quotify(&d, av[i]);
1190 dstr_putz(&d);
1191 a_rawalert(f_and, f_eq, tag, d.buf, d.len);
1192 dstr_destroy(&d);
1193 a_ok(a);
1194}
1195
1196static void acmd_notify(admin *a, unsigned ac, char *av[])
1197 { alertcmd(a, AF_NOTE, AF_NOTE, "NOTE", ac, av); }
1198static void acmd_warn(admin *a, unsigned ac, char *av[])
1199 { alertcmd(a, AF_WARN, AF_WARN, "WARN", ac, av); }
1200
410c8acf 1201static void acmd_port(admin *a, unsigned ac, char *av[])
1202{
f43df819 1203 a_info(a, "%u", p_port(), A_END);
3cdc3f3a 1204 a_ok(a);
410c8acf 1205}
1206
410c8acf 1207static void acmd_daemon(admin *a, unsigned ac, char *av[])
1208{
1209 if (flags & F_DAEMON)
f43df819 1210 a_fail(a, "already-daemon", A_END);
410c8acf 1211 else {
f43df819 1212 a_notify("DAEMON", A_END);
93bee2c4 1213 if (a_stdin)
410c8acf 1214 a_destroy(a_stdin);
410c8acf 1215 if (u_daemon())
f43df819 1216 a_fail(a, "daemon-error", "?ERRNO", A_END);
93bee2c4 1217 else {
410c8acf 1218 flags |= F_DAEMON;
3cdc3f3a 1219 a_ok(a);
93bee2c4 1220 }
410c8acf 1221 }
1222}
1223
1224static void acmd_list(admin *a, unsigned ac, char *av[])
1225{
1226 peer *p;
1227 for (p = p_first(); p; p = p_next(p))
f43df819 1228 a_info(a, "%s", p_name(p), A_END);
3cdc3f3a 1229 a_ok(a);
410c8acf 1230}
1231
1232static void acmd_ifname(admin *a, unsigned ac, char *av[])
1233{
1234 peer *p;
1235
37941236 1236 if ((p = a_findpeer(a, av[0])) != 0) {
f43df819 1237 a_info(a, "%s", p_ifname(p), A_END);
3cdc3f3a 1238 a_ok(a);
1239 }
410c8acf 1240}
1241
37941236 1242static void acmd_getchal(admin *a, unsigned ac, char *av[])
1243{
1244 buf b;
1245
1246 buf_init(&b, buf_i, PKBUFSZ);
1247 c_new(&b);
f43df819 1248 a_info(a, "?B64", BBASE(&b), (size_t)BLEN(&b), A_END);
37941236 1249 a_ok(a);
1250}
1251
1252static void acmd_checkchal(admin *a, unsigned ac, char *av[])
1253{
1254 base64_ctx b64;
1255 buf b;
1256 dstr d = DSTR_INIT;
1257
1258 base64_init(&b64);
1259 base64_decode(&b64, av[0], strlen(av[0]), &d);
1260 base64_decode(&b64, 0, 0, &d);
1261 buf_init(&b, d.buf, d.len);
1262 if (c_check(&b) || BBAD(&b) || BLEFT(&b))
f43df819 1263 a_fail(a, "invalid-challenge", A_END);
37941236 1264 else
1265 a_ok(a);
1266 dstr_destroy(&d);
1267}
1268
1269static void acmd_greet(admin *a, unsigned ac, char *av[])
1270{
1271 peer *p;
1272 base64_ctx b64;
1273 dstr d = DSTR_INIT;
1274
1275 if ((p = a_findpeer(a, av[0])) != 0) {
1276 base64_init(&b64);
1277 base64_decode(&b64, av[1], strlen(av[1]), &d);
1278 base64_decode(&b64, 0, 0, &d);
1279 p_greet(p, d.buf, d.len);
1280 dstr_destroy(&d);
1281 a_ok(a);
1282 }
1283}
1284
410c8acf 1285static void acmd_addr(admin *a, unsigned ac, char *av[])
1286{
1287 peer *p;
1288 const addr *ad;
1289
37941236 1290 if ((p = a_findpeer(a, av[0])) != 0) {
410c8acf 1291 ad = p_addr(p);
1292 assert(ad->sa.sa_family == AF_INET);
f43df819 1293 a_info(a, "?ADDR", ad, A_END);
3cdc3f3a 1294 a_ok(a);
410c8acf 1295 }
1296}
1297
060ca767 1298static void acmd_peerinfo(admin *a, unsigned ac, char *av[])
1299{
1300 peer *p;
1301 const peerspec *ps;
1302
37941236 1303 if ((p = a_findpeer(a, av[0])) != 0) {
1304 ps = p_spec(p);
f43df819
MW
1305 a_info(a, "tunnel=%s", ps->tops->name, A_END);
1306 a_info(a, "keepalive=%lu", ps->t_ka, A_END);
37941236 1307 a_ok(a);
060ca767 1308 }
060ca767 1309}
1310
1311static void acmd_servinfo(admin *a, unsigned ac, char *av[])
1312{
f43df819
MW
1313 a_info(a, "implementation=edgeware-tripe", A_END);
1314 a_info(a, "version=%s", VERSION, A_END);
1315 a_info(a, "daemon=%s", BOOL(flags & F_DAEMON), A_END);
060ca767 1316 a_ok(a);
1317}
1318
c7053da9 1319static void acmd_stats(admin *a, unsigned ac, char *av[])
1320{
1321 peer *p;
1322 stats *st;
1323
37941236 1324 if ((p = a_findpeer(a, av[0])) == 0)
060ca767 1325 return;
060ca767 1326
1327 st = p_stats(p);
f43df819
MW
1328 a_info(a, "start-time=%s", timestr(st->t_start), A_END);
1329 a_info(a, "last-packet-time=%s", timestr(st->t_last), A_END);
1330 a_info(a, "last-keyexch-time=%s", timestr(st->t_kx), A_END);
1331 a_info(a, "packets-in=%lu bytes-in=%lu", st->n_in, st->sz_in, A_END);
060ca767 1332 a_info(a, "packets-out=%lu bytes-out=%lu",
f43df819 1333 st->n_out, st->sz_out, A_END);
060ca767 1334 a_info(a, "keyexch-packets-in=%lu keyexch-bytes-in=%lu",
f43df819 1335 st->n_kxin, st->sz_kxin, A_END);
060ca767 1336 a_info(a, "keyexch-packets-out=%lu keyexch-bytes-out=%lu",
f43df819 1337 st->n_kxout, st->sz_kxout, A_END);
060ca767 1338 a_info(a, "ip-packets-in=%lu ip-bytes-in=%lu",
f43df819 1339 st->n_ipin, st->sz_ipin, A_END);
060ca767 1340 a_info(a, "ip-packets-out=%lu ip-bytes-out=%lu",
f43df819
MW
1341 st->n_ipout, st->sz_ipout, A_END);
1342 a_info(a, "rejected-packets=%lu", st->n_reject, A_END);
060ca767 1343 a_ok(a);
c7053da9 1344}
1345
410c8acf 1346static void acmd_kill(admin *a, unsigned ac, char *av[])
1347{
1348 peer *p;
37941236 1349 if ((p = a_findpeer(a, av[0])) != 0) {
410c8acf 1350 p_destroy(p);
3cdc3f3a 1351 a_ok(a);
410c8acf 1352 }
1353}
1354
0ba8de86 1355static void acmd_forcekx(admin *a, unsigned ac, char *av[])
1356{
1357 peer *p;
37941236 1358 if ((p = a_findpeer(a, av[0])) != 0) {
de014da6 1359 kx_start(&p->kx, 1);
0ba8de86 1360 a_ok(a);
1361 }
1362}
1363
de014da6 1364static void acmd_reload(admin *a, unsigned ac, char *av[])
1365 { p_keyreload(); a_ok(a); }
1366
410c8acf 1367static void acmd_quit(admin *a, unsigned ac, char *av[])
1368{
f43df819 1369 a_warn("SERVER", "quit", "admin-request", A_END);
3cdc3f3a 1370 a_ok(a);
de014da6 1371 a_unlock(a);
410c8acf 1372 a_quit();
1373}
1374
3cdc3f3a 1375static void acmd_version(admin *a, unsigned ac, char *av[])
1376{
f43df819 1377 a_info(a, "%s", PACKAGE, "%s", VERSION, A_END);
3cdc3f3a 1378 a_ok(a);
1379}
1380
42da2a58 1381static void acmd_tunnels(admin *a, unsigned ac, char *av[])
1382{
1383 int i;
1384 for (i = 0; tunnels[i]; i++)
f43df819 1385 a_info(a, "%s", tunnels[i]->name, A_END);
42da2a58 1386 a_ok(a);
1387}
1388
410c8acf 1389/* --- The command table and help --- */
1390
1391typedef struct acmd {
1392 const char *name;
1393 const char *help;
1394 unsigned argmin, argmax;
1395 void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1396} acmd;
1397
1398static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1399
1400static const acmd acmdtab[] = {
f43df819
MW
1401 { "add", "PEER [OPTIONS] ADDR ...", 2, 0xffff, acmd_add },
1402 { "addr", "PEER", 1, 1, acmd_addr },
1403 { "checkchal", "CHAL", 1, 1, acmd_checkchal },
1404 { "daemon", 0, 0, 0, acmd_daemon },
1405 { "eping", "[OPTIONS] PEER", 1, 0xffff, acmd_eping },
1406 { "forcekx", "PEER", 1, 1, acmd_forcekx },
1407 { "getchal", 0, 0, 0, acmd_getchal },
1408 { "greet", "PEER CHAL", 2, 2, acmd_greet },
1409 { "help", 0, 0, 0, acmd_help },
1410 { "ifname", "PEER", 1, 1, acmd_ifname },
1411 { "kill", "PEER", 1, 1, acmd_kill },
1412 { "list", 0, 0, 0, acmd_list },
1413 { "notify", "MESSAGE ...", 1, 0xffff, acmd_notify },
1414 { "peerinfo", "PEER", 1, 1, acmd_peerinfo },
1415 { "ping", "[OPTIONS] PEER", 1, 0xffff, acmd_ping },
1416 { "port", 0, 0, 0, acmd_port },
1417 { "quit", 0, 0, 0, acmd_quit },
1418 { "reload", 0, 0, 0, acmd_reload },
1419 { "servinfo", 0, 0, 0, acmd_servinfo },
1420 { "stats", "PEER", 1, 1, acmd_stats },
de014da6 1421#ifndef NTRACE
f43df819 1422 { "trace", "[OPTIONS]", 0, 1, acmd_trace },
de014da6 1423#endif
f43df819
MW
1424 { "tunnels", 0, 0, 0, acmd_tunnels },
1425 { "version", 0, 0, 0, acmd_version },
1426 { "warn", "MESSAGE ...", 1, 0xffff, acmd_warn },
1427 { "watch", "[OPTIONS]", 0, 1, acmd_watch },
410c8acf 1428 { 0, 0, 0, 0, 0 }
1429};
1430
1431static void acmd_help(admin *a, unsigned ac, char *av[])
1432{
1433 const acmd *c;
f43df819
MW
1434 for (c = acmdtab; c->name; c++) {
1435 if (c->help)
1436 a_info(a, "%s", c->name, "*%s", c->help, A_END);
1437 else
1438 a_info(a, "%s", c->name, A_END);
1439 }
3cdc3f3a 1440 a_ok(a);
410c8acf 1441}
1442
1443/*----- Connection handling -----------------------------------------------*/
1444
fd3cf232 1445/* --- @a_lock@ --- *
410c8acf 1446 *
1447 * Arguments: @admin *a@ = pointer to an admin block
1448 *
1449 * Returns: ---
1450 *
fd3cf232 1451 * Use: Locks an admin block so that it won't be destroyed
1452 * immediately.
410c8acf 1453 */
1454
060ca767 1455static void a_lock(admin *a) { a->ref++; }
fd3cf232 1456
060ca767 1457/* --- @a_dodestroy@ --- *
fd3cf232 1458 *
1459 * Arguments: @admin *a@ = pointer to an admin block
1460 *
1461 * Returns: ---
1462 *
060ca767 1463 * Use: Actually does the legwork of destroying an admin block.
fd3cf232 1464 */
1465
060ca767 1466static void a_dodestroy(admin *a)
410c8acf 1467{
de014da6 1468 admin_bgop *bg, *bbg;
de014da6 1469
fd3cf232 1470 T( trace(T_ADMIN, "admin: completing destruction of connection %u",
1471 a->seq); )
1472
410c8acf 1473 selbuf_destroy(&a->b);
de014da6 1474 for (bg = a->bg; bg; bg = bbg) {
1475 bbg = bg->next;
1476 bg->cancel(bg);
1477 if (bg->tag) xfree(bg->tag);
1478 xfree(bg);
410c8acf 1479 }
de014da6 1480 if (a->b.reader.fd != a->w.fd) close(a->b.reader.fd);
fd3cf232 1481 close(a->w.fd);
1482
1483 if (a_stdin == a)
1484 a_stdin = 0;
410c8acf 1485 if (a->next)
1486 a->next->prev = a->prev;
1487 if (a->prev)
1488 a->prev->next = a->next;
1489 else
1490 admins = a->next;
410c8acf 1491 DESTROY(a);
1492}
1493
060ca767 1494/* --- @a_unlock@ --- *
1495 *
1496 * Arguments: @admin *a@ = pointer to an admin block
1497 *
1498 * Returns: ---
1499 *
1500 * Use: Unlocks an admin block, allowing its destruction. This is
1501 * also the second half of @a_destroy@.
1502 */
1503
1504static void a_unlock(admin *a)
1505{
1506 assert(a->ref);
1507 if (!--a->ref && (a->f & AF_DEAD))
1508 a_dodestroy(a);
1509}
1510
fd3cf232 1511/* --- @a_destroy@ --- *
1512 *
1513 * Arguments: @admin *a@ = pointer to an admin block
1514 *
1515 * Returns: ---
1516 *
1517 * Use: Destroys an admin block. This requires a certain amount of
1518 * care.
1519 */
1520
de014da6 1521static void freequeue(oqueue *q)
1522{
1523 obuf *o, *oo;
1524
1525 for (o = q->hd; o; o = oo) {
1526 oo = o->next;
1527 xfree(o);
1528 }
1529 q->hd = q->tl = 0;
1530}
1531
fd3cf232 1532static void a_destroy(admin *a)
1533{
1534 /* --- Don't multiply destroy admin blocks --- */
1535
1536 if (a->f & AF_DEAD)
1537 return;
1538
1539 /* --- Make sure nobody expects it to work --- */
1540
1541 a->f |= AF_DEAD;
1542 T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
1543
1544 /* --- Free the output buffers --- */
1545
de014da6 1546 if (a->out.hd)
fd3cf232 1547 sel_rmfile(&a->w);
de014da6 1548 freequeue(&a->out);
fd3cf232 1549
1550 /* --- If the block is locked, that's all we can manage --- */
1551
060ca767 1552 if (!a->ref)
1553 a_dodestroy(a);
1554 T( else
1555 trace(T_ADMIN, "admin: deferring destruction..."); )
fd3cf232 1556}
1557
410c8acf 1558/* --- @a_line@ --- *
1559 *
1560 * Arguments: @char *p@ = pointer to the line read
3008eabf 1561 * @size_t len@ = length of the line
410c8acf 1562 * @void *vp@ = pointer to my admin block
1563 *
1564 * Returns: ---
1565 *
1566 * Use: Handles a line of input.
1567 */
1568
3008eabf 1569static void a_line(char *p, size_t len, void *vp)
410c8acf 1570{
1571 admin *a = vp;
1572 const acmd *c;
3cdc3f3a 1573 char *av[16];
410c8acf 1574 size_t ac;
1575
8d0c7a83 1576 TIMER;
fd3cf232 1577 if (a->f & AF_DEAD)
1578 return;
410c8acf 1579 if (!p) {
de014da6 1580 if (!a->bg)
1581 a_destroy(a);
1582 else {
1583 a->f |= AF_CLOSE;
1584 selbuf_disable(&a->b);
1585 }
410c8acf 1586 return;
1587 }
3cdc3f3a 1588 ac = str_qsplit(p, av, 16, 0, STRF_QUOTE);
410c8acf 1589 if (!ac)
1590 return;
410c8acf 1591 for (c = acmdtab; c->name; c++) {
3cdc3f3a 1592 if (mystrieq(av[0], c->name)) {
410c8acf 1593 ac--;
f43df819
MW
1594 if (c->argmin > ac || ac > c->argmax) {
1595 if (!c->help)
1596 a_fail(a, "bad-syntax", "%s", c->name, "", A_END);
1597 else
1598 a_fail(a, "bad-syntax", "%s", c->name, "%s", c->help, A_END);
1599 } else {
fd3cf232 1600 a_lock(a);
410c8acf 1601 c->func(a, ac, av + 1);
fd3cf232 1602 a_unlock(a);
1603 }
410c8acf 1604 return;
1605 }
1606 }
f43df819 1607 a_fail(a, "unknown-command", "%s", av[0], A_END);
410c8acf 1608}
1609
1610/* --- @a_create@ --- *
1611 *
1612 * Arguments: @int fd_in, fd_out@ = file descriptors to use
3cdc3f3a 1613 * @unsigned f@ = initial flags to set
410c8acf 1614 *
1615 * Returns: ---
1616 *
1617 * Use: Creates a new admin connection.
1618 */
1619
3cdc3f3a 1620void a_create(int fd_in, int fd_out, unsigned f)
410c8acf 1621{
1622 admin *a = CREATE(admin);
3cdc3f3a 1623
93bee2c4 1624 T( static unsigned seq = 0;
1625 a->seq = seq++; )
410c8acf 1626 T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
de014da6 1627 a->bg = 0;
042d5c20 1628 a->ref = 0;
3cdc3f3a 1629 a->f = f;
de014da6 1630 if (fd_in == STDIN_FILENO) a_stdin = a;
fd3cf232 1631 fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
410c8acf 1632 if (fd_out != fd_in)
fd3cf232 1633 fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
410c8acf 1634 selbuf_init(&a->b, &sel, fd_in, a_line, a);
fd3cf232 1635 sel_initfile(&sel, &a->w, fd_out, SEL_WRITE, a_flush, a);
de014da6 1636 a->out.hd = a->out.tl = 0;
410c8acf 1637 a->next = admins;
1638 a->prev = 0;
de014da6 1639 if (admins) admins->prev = a;
410c8acf 1640 admins = a;
1641}
1642
1643/* --- @a_accept@ --- *
1644 *
1645 * Arguments: @int fd@ = file descriptor to accept
1646 * @unsigned mode@ = what to do
1647 * @void *v@ = uninteresting pointer
1648 *
1649 * Returns: ---
1650 *
1651 * Use: Accepts a new admin connection.
1652 */
1653
1654static void a_accept(int fd, unsigned mode, void *v)
1655{
1656 int nfd;
1657 struct sockaddr_un sun;
1658 size_t sz = sizeof(sun);
1659
1660 if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
1ecee057 1661 if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK &&
1662 errno != ECONNABORTED && errno != EPROTO)
f43df819 1663 a_warn("ADMIN", "accept-error", "?ERRNO", A_END);
410c8acf 1664 return;
1665 }
3cdc3f3a 1666 a_create(nfd, nfd, 0);
410c8acf 1667}
1668
1669/* --- @a_daemon@ --- *
1670 *
1671 * Arguments: ---
1672 *
1673 * Returns: ---
1674 *
1675 * Use: Informs the admin module that it's a daemon.
1676 */
1677
1678void a_daemon(void)
1679{
1680 flags |= F_DAEMON;
1681}
1682
1683/* --- @a_init@ --- *
1684 *
1685 * Arguments: @const char *name@ = socket name to create
1686 *
1687 * Returns: ---
1688 *
1689 * Use: Creates the admin listening socket.
1690 */
1691
1692void a_init(const char *name)
1693{
1694 int fd;
1695 int n = 5;
1696 struct sockaddr_un sun;
1697 struct sigaction sa;
1698 size_t sz;
1699
1700 /* --- Set up the socket address --- */
1701
1702 sz = strlen(name) + 1;
1703 if (sz > sizeof(sun.sun_path))
1704 die(EXIT_FAILURE, "socket name `%s' too long", name);
1705 BURN(sun);
1706 sun.sun_family = AF_UNIX;
1707 memcpy(sun.sun_path, name, sz);
1708 sz += offsetof(struct sockaddr_un, sun_path);
1709
1710 /* --- Attempt to bind to the socket --- */
1711
1712 umask(0077);
1713again:
1714 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1715 die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
1716 if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
1717 struct stat st;
1718 int e = errno;
1719 if (errno != EADDRINUSE) {
1720 die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
1721 sun.sun_path, strerror(e));
1722 }
1723 if (!n)
1724 die(EXIT_FAILURE, "too many retries; giving up");
1725 n--;
1726 if (!connect(fd, (struct sockaddr *)&sun, sz)) {
1727 die(EXIT_FAILURE, "server already listening on admin socket `%s'",
1728 sun.sun_path);
1729 }
1730 if (errno != ECONNREFUSED)
1731 die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
1732 if (stat(sun.sun_path, &st)) {
1733 die(EXIT_FAILURE, "couldn't stat `%s': %s",
1734 sun.sun_path, strerror(errno));
1735 }
1736 if (!S_ISSOCK(st.st_mode))
1737 die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
c7053da9 1738 T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
410c8acf 1739 unlink(sun.sun_path);
1740 close(fd);
1741 goto again;
1742 }
1743 chmod(sun.sun_path, 0600);
1744 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1745 if (listen(fd, 5))
1746 die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
1747
1748 /* --- Listen to the socket --- */
1749
1750 sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
1751 sel_addfile(&sock);
1752 sockname = name;
1753 bres_init(&sel);
1754 T( trace_custom(a_trace, 0);
1755 trace(T_ADMIN, "admin: enabled custom tracing"); )
1756 flags |= F_INIT;
1757
1758 /* --- Set up signal handlers --- */
1759
1760 sig_add(&s_term, SIGTERM, a_sigdie, 0);
1761 sig_add(&s_hup, SIGHUP, a_sighup, 0);
3cdc3f3a 1762 signal(SIGPIPE, SIG_IGN);
410c8acf 1763 sigaction(SIGINT, 0, &sa);
1764 if (sa.sa_handler != SIG_IGN)
1765 sig_add(&s_int, SIGINT, a_sigdie, 0);
1766}
1767
1768/*----- That's all, folks -------------------------------------------------*/