chiark / gitweb /
Merge branch 'tokens'
[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
808 r->addr = 0;
809 r->func = func;
810 if (mystrieq(av[i], "inet")) i++;
811 if (ac - i != 2) {
f43df819 812 a_fail(a, "bad-addr-syntax", "[inet] ADDRESS PORT", A_END);
37941236 813 goto fail;
814 }
815 r->sa.sin.sin_family = AF_INET;
816 r->sasz = sizeof(r->sa.sin);
817 r->addr = xstrdup(av[i]);
818 pt = strtoul(av[i + 1], &p, 0);
819 if (*p) {
820 struct servent *s = getservbyname(av[i + 1], "udp");
821 if (!s) {
f43df819 822 a_fail(a, "unknown-service", "%s", av[i + 1], A_END);
37941236 823 goto fail;
824 }
825 pt = ntohs(s->s_port);
410c8acf 826 }
37941236 827 if (pt == 0 || pt >= 65536) {
f43df819 828 a_fail(a, "invalid-port", "%lu", pt, A_END);
37941236 829 goto fail;
830 }
831 r->sa.sin.sin_port = htons(pt);
832
833 /* --- Report backgrounding --- *
834 *
835 * Do this for consistency of interface, even if we're going to get the
836 * answer straight away.
837 */
838
839 a_bgadd(a, &r->bg, tag, a_rescancel);
840 T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'",
841 a->seq, BGTAG(r), r->addr); )
842
843 /* --- If the name is numeric, do it the easy way --- */
844
845 if (inet_aton(av[i], &r->sa.sin.sin_addr)) {
846 T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); )
847 func(r, ARES_OK);
848 xfree(r->addr);
849 a_bgrelease(&r->bg);
850 return;
851 }
852
853 /* --- Store everything for later and crank up the resolver --- */
854
855 gettimeofday(&tv, 0);
856 tv.tv_sec += T_RESOLVE;
857 sel_addtimer(&sel, &r->t, &tv, a_restimer, r);
858 bres_byname(&r->r, r->addr, a_resolved, r);
859 return;
860
861fail:
862 func(r, ARES_FAIL);
863 if (r->addr) xfree(r->addr);
864 xfree(r);
410c8acf 865}
866
37941236 867/*----- Adding peers ------------------------------------------------------*/
868
869/* --- @a_doadd@ --- *
410c8acf 870 *
37941236 871 * Arguments: @admin_resop *r@ = resolver operation
872 * @int rc@ = how it worked
410c8acf 873 *
874 * Returns: ---
875 *
37941236 876 * Use: Handles a completed resolution.
410c8acf 877 */
878
37941236 879static void a_doadd(admin_resop *r, int rc)
410c8acf 880{
37941236 881 admin_addop *add = (admin_addop *)r;
882
883 T( trace(T_ADMIN, "admin: done add op %s", BGTAG(add)); )
884
885 if (rc == ARES_OK) {
886 add->peer.sasz = add->r.sasz;
887 add->peer.sa = add->r.sa;
888 if (p_find(add->peer.name))
f43df819 889 a_bgfail(&add->r.bg, "peer-exists", "%s", add->peer.name, A_END);
37941236 890 else if (!p_create(&add->peer))
f43df819 891 a_bgfail(&add->r.bg, "peer-create-fail", "%s", add->peer.name, A_END);
37941236 892 else
893 a_bgok(&add->r.bg);
894 }
de014da6 895
37941236 896 xfree(add->peer.name);
410c8acf 897}
898
899/* --- @acmd_add@ --- *
900 *
901 * Arguments: @admin *a@ = connection which requested the addition
902 * @unsigned ac@ = argument count
903 * @char *av[]@ = pointer to the argument list
904 *
905 * Returns: ---
906 *
907 * Use: Adds a new peer.
908 */
909
910static void acmd_add(admin *a, unsigned ac, char *av[])
911{
42da2a58 912 unsigned i, j;
de014da6 913 const char *tag = 0;
914 admin_addop *add = 0;
410c8acf 915
0ba8de86 916 /* --- Set stuff up --- */
917
de014da6 918 add = xmalloc(sizeof(*add));
919 add->peer.name = xstrdup(av[0]);
920 add->peer.t_ka = 0;
921 add->peer.tops = tun_default;
37941236 922
923 /* --- Make sure someone's not got there already --- */
924
925 if (p_find(av[0])) {
f43df819 926 a_fail(a, "peer-exists", "%s", av[0], A_END);
37941236 927 goto fail;
928 }
0ba8de86 929
42da2a58 930 /* --- Parse options --- */
931
932 i = 1;
933 for (;;) {
934 if (!av[i])
935 goto bad_syntax;
de014da6 936 if (mystrieq(av[i], "-background")) {
937 if (!av[++i]) goto bad_syntax;
938 tag = av[i];
939 } else if (mystrieq(av[i], "-tunnel")) {
0ba8de86 940 if (!av[++i]) goto bad_syntax;
42da2a58 941 for (j = 0;; j++) {
942 if (!tunnels[j]) {
f43df819 943 a_fail(a, "unknown-tunnel", "%s", av[i], A_END);
37941236 944 goto fail;
42da2a58 945 }
946 if (mystrieq(av[i], tunnels[j]->name)) {
de014da6 947 add->peer.tops = tunnels[j];
42da2a58 948 break;
949 }
950 }
0ba8de86 951 } else if (mystrieq(av[i], "-keepalive")) {
952 long t;
953 if (!av[++i]) goto bad_syntax;
954 if ((t = a_parsetime(av[i])) < 0) {
f43df819 955 a_fail(a, "bad-time-spec", "%s", av[i], A_END);
37941236 956 goto fail;
0ba8de86 957 }
de014da6 958 add->peer.t_ka = t;
42da2a58 959 } else if (mystrieq(av[i], "--")) {
960 i++;
961 break;
962 } else
963 break;
0ba8de86 964 i++;
42da2a58 965 }
966
37941236 967 /* --- Crank up the resolver --- */
de014da6 968
37941236 969 a_resolve(a, &add->r, tag, a_doadd, ac - i, av + i);
42da2a58 970 return;
971
37941236 972 /* --- Clearing up --- */
973
42da2a58 974bad_syntax:
f43df819 975 a_fail(a, "bad-syntax", "add", "PEER [OPTIONS] ADDR ...", A_END);
de014da6 976fail:
37941236 977 xfree(add->peer.name);
978 xfree(add);
0ba8de86 979 return;
980}
981
982/*----- Ping --------------------------------------------------------------*/
983
de014da6 984/* --- @a_pingcancel@ --- *
985 *
986 * Arguments: @admin_bgop *bg@ = background operation block
987 *
988 * Returns: ---
989 *
990 * Use: Cancels a running ping.
991 */
992
993static void a_pingcancel(admin_bgop *bg)
994{
995 admin_pingop *pg = (admin_pingop *)bg;
060ca767 996 T( trace(T_ADMIN, "admin: cancel ping op %s", BGTAG(pg)); )
de014da6 997 p_pingdone(&pg->ping, PING_NONOTIFY);
998}
999
0ba8de86 1000/* --- @a_pong@ --- *
1001 *
1002 * Arguments: @int rc@ = return code
de014da6 1003 * @void *v@ = ping operation block
0ba8de86 1004 *
1005 * Returns: ---
1006 *
1007 * Use: Collects what happened to a ping message.
1008 */
1009
de014da6 1010static void a_pong(int rc, void *v)
0ba8de86 1011{
de014da6 1012 admin_pingop *pg = v;
0ba8de86 1013 struct timeval tv;
1014 double millis;
1015
0ba8de86 1016 switch (rc) {
1017 case PING_OK:
1018 gettimeofday(&tv, 0);
de014da6 1019 tv_sub(&tv, &tv, &pg->pingtime);
0ba8de86 1020 millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
f43df819 1021 a_bginfo(&pg->bg, "ping-ok", "%.1f", millis, A_END);
de014da6 1022 a_bgok(&pg->bg);
0ba8de86 1023 break;
1024 case PING_TIMEOUT:
f43df819 1025 a_bginfo(&pg->bg, "ping-timeout", A_END);
de014da6 1026 a_bgok(&pg->bg);
0ba8de86 1027 break;
1028 case PING_PEERDIED:
f43df819 1029 a_bginfo(&pg->bg, "ping-peer-died", A_END);
de014da6 1030 a_bgok(&pg->bg);
0ba8de86 1031 break;
1032 default:
1033 abort();
1034 }
060ca767 1035 T( trace(T_ADMIN, "admin: ponged ping op %s", BGTAG(pg)); )
de014da6 1036 a_bgrelease(&pg->bg);
0ba8de86 1037}
1038
1039/* --- @acmd_ping@, @acmd_eping@ --- *
1040 *
1041 * Arguments: @admin *a@ = connection which requested the ping
1042 * @unsigned ac@ = argument count
1043 * @char *av[]@ = pointer to the argument list
1044 *
1045 * Returns: ---
1046 *
1047 * Use: Pings a peer.
1048 */
1049
1050static void a_ping(admin *a, unsigned ac, char *av[],
1051 const char *cmd, unsigned msg)
1052{
1053 long t = T_PING;
1054 int i;
1055 peer *p;
de014da6 1056 admin_pingop *pg = 0;
1057 const char *tag = 0;
0ba8de86 1058
1059 i = 0;
1060 for (;;) {
1061 if (!av[i])
1062 goto bad_syntax;
de014da6 1063 if (mystrieq(av[i], "-background")) {
1064 if (!av[++i]) goto bad_syntax;
1065 tag = av[i];
1066 } else if (mystrieq(av[i], "-timeout")) {
0ba8de86 1067 if (!av[++i]) goto bad_syntax;
1068 if ((t = a_parsetime(av[i])) < 0) {
f43df819 1069 a_fail(a, "bad-time-spec", "%s", av[i], A_END);
0ba8de86 1070 return;
1071 }
1072 } else if (mystrieq(av[i], "--")) {
1073 i++;
1074 break;
1075 } else
1076 break;
1077 i++;
1078 }
1079
1080 if (!av[i]) goto bad_syntax;
37941236 1081 if ((p = a_findpeer(a, av[i])) == 0)
0ba8de86 1082 return;
de014da6 1083 pg = xmalloc(sizeof(*pg));
1084 gettimeofday(&pg->pingtime, 0);
1085 a_bgadd(a, &pg->bg, tag, a_pingcancel);
060ca767 1086 T( trace(T_ADMIN, "admin: ping op %s: %s to %s",
1087 BGTAG(pg), cmd, p_name(p)); )
de014da6 1088 if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) {
f43df819 1089 a_bgfail(&pg->bg, "ping-send-failed", A_END);
de014da6 1090 a_bgrelease(&pg->bg);
1091 }
0ba8de86 1092 return;
1093
1094bad_syntax:
f43df819 1095 a_fail(a, "bad-syntax", "%s", cmd, "[OPTIONS] PEER", cmd, A_END);
42da2a58 1096 return;
410c8acf 1097}
1098
0ba8de86 1099static void acmd_ping(admin *a, unsigned ac, char *av[])
1100 { a_ping(a, ac, av, "ping", MISC_PING); }
1101static void acmd_eping(admin *a, unsigned ac, char *av[])
1102 { a_ping(a, ac, av, "eping", MISC_EPING); }
0ba8de86 1103
410c8acf 1104/*----- Administration commands -------------------------------------------*/
1105
1106/* --- Miscellaneous commands --- */
1107
3cdc3f3a 1108/* --- @traceish@ --- *
1109 *
1110 * Arguments: @admin *a@ = connection to complain on
1111 * @unsigned ac@ = number of arguments
1112 * @char *av[]@ = vector of arguments
1113 * @const char *what@ = what we're messing with
1114 * @const trace_opt *tt@ = options table
1115 * @unsigned *ff@ = where the flags are
1116 *
1117 * Returns: Nonzero if anything changed.
1118 *
1119 * Use: Guts of trace-ish commands like `trace' and `watch'.
1120 */
410c8acf 1121
3cdc3f3a 1122static int traceish(admin *a, unsigned ac, char *av[],
1123 const char *what, const trace_opt *tt, unsigned *ff)
410c8acf 1124{
3cdc3f3a 1125 int ch = 0;
1126
410c8acf 1127 if (!ac || strcmp(av[0], "?") == 0) {
1128 const trace_opt *t;
3cdc3f3a 1129 for (t = tt; t->ch; t++) {
f43df819
MW
1130 a_info(a, "*%c%c %s",
1131 t->ch, (*ff & t->f) == t->f ? '+' : ' ', t->help, A_END);
410c8acf 1132 }
1133 } else {
1134 unsigned sense = 1;
3cdc3f3a 1135 unsigned f = *ff;
1136 const trace_opt *t;
410c8acf 1137 char *p = av[0];
1138
1139 while (*p) {
1140 switch (*p) {
1141 case '+': sense = 1; break;
1142 case '-': sense = 0; break;
1143 default:
3cdc3f3a 1144 for (t = tt; t->ch; t++) {
1145 if (t->ch == *p) {
1146 if (sense) f |= t->f;
1147 else f &= ~t->f;
410c8acf 1148 goto tropt_ok;
1149 }
1150 }
f43df819 1151 a_fail(a, "bad-%s-option", what, "%c", *p, A_END);
3cdc3f3a 1152 return (0);
410c8acf 1153 tropt_ok:;
1154 break;
1155 }
1156 p++;
1157 }
3cdc3f3a 1158 *ff = f;
1159 ch = 1;
410c8acf 1160 }
3cdc3f3a 1161 a_ok(a);
1162 return (ch);
1163}
1164
1165#ifndef NTRACE
1166
1167static void acmd_trace(admin *a, unsigned ac, char *av[])
1168{
1169 if (traceish(a, ac, av, "trace", tr_opts, &tr_flags))
1170 trace_level(tr_flags);
410c8acf 1171}
1172
1173#endif
1174
3cdc3f3a 1175static void acmd_watch(admin *a, unsigned ac, char *av[])
1176{
1177 traceish(a, ac, av, "watch", w_opts, &a->f);
1178}
1179
3cdc3f3a 1180static void alertcmd(admin *a, unsigned f_and, unsigned f_eq,
1181 const char *tag, unsigned ac, char *av[])
1182{
1183 dstr d = DSTR_INIT;
1184 unsigned i;
1185
1186 dstr_puts(&d, "USER");
1187 for (i = 0; i < ac; i++)
1188 quotify(&d, av[i]);
1189 dstr_putz(&d);
1190 a_rawalert(f_and, f_eq, tag, d.buf, d.len);
1191 dstr_destroy(&d);
1192 a_ok(a);
1193}
1194
1195static void acmd_notify(admin *a, unsigned ac, char *av[])
1196 { alertcmd(a, AF_NOTE, AF_NOTE, "NOTE", ac, av); }
1197static void acmd_warn(admin *a, unsigned ac, char *av[])
1198 { alertcmd(a, AF_WARN, AF_WARN, "WARN", ac, av); }
1199
410c8acf 1200static void acmd_port(admin *a, unsigned ac, char *av[])
1201{
f43df819 1202 a_info(a, "%u", p_port(), A_END);
3cdc3f3a 1203 a_ok(a);
410c8acf 1204}
1205
410c8acf 1206static void acmd_daemon(admin *a, unsigned ac, char *av[])
1207{
1208 if (flags & F_DAEMON)
f43df819 1209 a_fail(a, "already-daemon", A_END);
410c8acf 1210 else {
f43df819 1211 a_notify("DAEMON", A_END);
93bee2c4 1212 if (a_stdin)
410c8acf 1213 a_destroy(a_stdin);
410c8acf 1214 if (u_daemon())
f43df819 1215 a_fail(a, "daemon-error", "?ERRNO", A_END);
93bee2c4 1216 else {
410c8acf 1217 flags |= F_DAEMON;
3cdc3f3a 1218 a_ok(a);
93bee2c4 1219 }
410c8acf 1220 }
1221}
1222
1223static void acmd_list(admin *a, unsigned ac, char *av[])
1224{
1225 peer *p;
1226 for (p = p_first(); p; p = p_next(p))
f43df819 1227 a_info(a, "%s", p_name(p), A_END);
3cdc3f3a 1228 a_ok(a);
410c8acf 1229}
1230
1231static void acmd_ifname(admin *a, unsigned ac, char *av[])
1232{
1233 peer *p;
1234
37941236 1235 if ((p = a_findpeer(a, av[0])) != 0) {
f43df819 1236 a_info(a, "%s", p_ifname(p), A_END);
3cdc3f3a 1237 a_ok(a);
1238 }
410c8acf 1239}
1240
37941236 1241static void acmd_getchal(admin *a, unsigned ac, char *av[])
1242{
1243 buf b;
1244
1245 buf_init(&b, buf_i, PKBUFSZ);
1246 c_new(&b);
f43df819 1247 a_info(a, "?B64", BBASE(&b), (size_t)BLEN(&b), A_END);
37941236 1248 a_ok(a);
1249}
1250
1251static void acmd_checkchal(admin *a, unsigned ac, char *av[])
1252{
1253 base64_ctx b64;
1254 buf b;
1255 dstr d = DSTR_INIT;
1256
1257 base64_init(&b64);
1258 base64_decode(&b64, av[0], strlen(av[0]), &d);
1259 base64_decode(&b64, 0, 0, &d);
1260 buf_init(&b, d.buf, d.len);
1261 if (c_check(&b) || BBAD(&b) || BLEFT(&b))
f43df819 1262 a_fail(a, "invalid-challenge", A_END);
37941236 1263 else
1264 a_ok(a);
1265 dstr_destroy(&d);
1266}
1267
1268static void acmd_greet(admin *a, unsigned ac, char *av[])
1269{
1270 peer *p;
1271 base64_ctx b64;
1272 dstr d = DSTR_INIT;
1273
1274 if ((p = a_findpeer(a, av[0])) != 0) {
1275 base64_init(&b64);
1276 base64_decode(&b64, av[1], strlen(av[1]), &d);
1277 base64_decode(&b64, 0, 0, &d);
1278 p_greet(p, d.buf, d.len);
1279 dstr_destroy(&d);
1280 a_ok(a);
1281 }
1282}
1283
410c8acf 1284static void acmd_addr(admin *a, unsigned ac, char *av[])
1285{
1286 peer *p;
1287 const addr *ad;
1288
37941236 1289 if ((p = a_findpeer(a, av[0])) != 0) {
410c8acf 1290 ad = p_addr(p);
1291 assert(ad->sa.sa_family == AF_INET);
f43df819 1292 a_info(a, "?ADDR", ad, A_END);
3cdc3f3a 1293 a_ok(a);
410c8acf 1294 }
1295}
1296
060ca767 1297static void acmd_peerinfo(admin *a, unsigned ac, char *av[])
1298{
1299 peer *p;
1300 const peerspec *ps;
1301
37941236 1302 if ((p = a_findpeer(a, av[0])) != 0) {
1303 ps = p_spec(p);
f43df819
MW
1304 a_info(a, "tunnel=%s", ps->tops->name, A_END);
1305 a_info(a, "keepalive=%lu", ps->t_ka, A_END);
37941236 1306 a_ok(a);
060ca767 1307 }
060ca767 1308}
1309
1310static void acmd_servinfo(admin *a, unsigned ac, char *av[])
1311{
f43df819
MW
1312 a_info(a, "implementation=edgeware-tripe", A_END);
1313 a_info(a, "version=%s", VERSION, A_END);
1314 a_info(a, "daemon=%s", BOOL(flags & F_DAEMON), A_END);
060ca767 1315 a_ok(a);
1316}
1317
c7053da9 1318static void acmd_stats(admin *a, unsigned ac, char *av[])
1319{
1320 peer *p;
1321 stats *st;
1322
37941236 1323 if ((p = a_findpeer(a, av[0])) == 0)
060ca767 1324 return;
060ca767 1325
1326 st = p_stats(p);
f43df819
MW
1327 a_info(a, "start-time=%s", timestr(st->t_start), A_END);
1328 a_info(a, "last-packet-time=%s", timestr(st->t_last), A_END);
1329 a_info(a, "last-keyexch-time=%s", timestr(st->t_kx), A_END);
1330 a_info(a, "packets-in=%lu bytes-in=%lu", st->n_in, st->sz_in, A_END);
060ca767 1331 a_info(a, "packets-out=%lu bytes-out=%lu",
f43df819 1332 st->n_out, st->sz_out, A_END);
060ca767 1333 a_info(a, "keyexch-packets-in=%lu keyexch-bytes-in=%lu",
f43df819 1334 st->n_kxin, st->sz_kxin, A_END);
060ca767 1335 a_info(a, "keyexch-packets-out=%lu keyexch-bytes-out=%lu",
f43df819 1336 st->n_kxout, st->sz_kxout, A_END);
060ca767 1337 a_info(a, "ip-packets-in=%lu ip-bytes-in=%lu",
f43df819 1338 st->n_ipin, st->sz_ipin, A_END);
060ca767 1339 a_info(a, "ip-packets-out=%lu ip-bytes-out=%lu",
f43df819
MW
1340 st->n_ipout, st->sz_ipout, A_END);
1341 a_info(a, "rejected-packets=%lu", st->n_reject, A_END);
060ca767 1342 a_ok(a);
c7053da9 1343}
1344
410c8acf 1345static void acmd_kill(admin *a, unsigned ac, char *av[])
1346{
1347 peer *p;
37941236 1348 if ((p = a_findpeer(a, av[0])) != 0) {
410c8acf 1349 p_destroy(p);
3cdc3f3a 1350 a_ok(a);
410c8acf 1351 }
1352}
1353
0ba8de86 1354static void acmd_forcekx(admin *a, unsigned ac, char *av[])
1355{
1356 peer *p;
37941236 1357 if ((p = a_findpeer(a, av[0])) != 0) {
de014da6 1358 kx_start(&p->kx, 1);
0ba8de86 1359 a_ok(a);
1360 }
1361}
1362
de014da6 1363static void acmd_reload(admin *a, unsigned ac, char *av[])
1364 { p_keyreload(); a_ok(a); }
1365
410c8acf 1366static void acmd_quit(admin *a, unsigned ac, char *av[])
1367{
f43df819 1368 a_warn("SERVER", "quit", "admin-request", A_END);
3cdc3f3a 1369 a_ok(a);
de014da6 1370 a_unlock(a);
410c8acf 1371 a_quit();
1372}
1373
3cdc3f3a 1374static void acmd_version(admin *a, unsigned ac, char *av[])
1375{
f43df819 1376 a_info(a, "%s", PACKAGE, "%s", VERSION, A_END);
3cdc3f3a 1377 a_ok(a);
1378}
1379
42da2a58 1380static void acmd_tunnels(admin *a, unsigned ac, char *av[])
1381{
1382 int i;
1383 for (i = 0; tunnels[i]; i++)
f43df819 1384 a_info(a, "%s", tunnels[i]->name, A_END);
42da2a58 1385 a_ok(a);
1386}
1387
410c8acf 1388/* --- The command table and help --- */
1389
1390typedef struct acmd {
1391 const char *name;
1392 const char *help;
1393 unsigned argmin, argmax;
1394 void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1395} acmd;
1396
1397static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
1398
1399static const acmd acmdtab[] = {
f43df819
MW
1400 { "add", "PEER [OPTIONS] ADDR ...", 2, 0xffff, acmd_add },
1401 { "addr", "PEER", 1, 1, acmd_addr },
1402 { "checkchal", "CHAL", 1, 1, acmd_checkchal },
1403 { "daemon", 0, 0, 0, acmd_daemon },
1404 { "eping", "[OPTIONS] PEER", 1, 0xffff, acmd_eping },
1405 { "forcekx", "PEER", 1, 1, acmd_forcekx },
1406 { "getchal", 0, 0, 0, acmd_getchal },
1407 { "greet", "PEER CHAL", 2, 2, acmd_greet },
1408 { "help", 0, 0, 0, acmd_help },
1409 { "ifname", "PEER", 1, 1, acmd_ifname },
1410 { "kill", "PEER", 1, 1, acmd_kill },
1411 { "list", 0, 0, 0, acmd_list },
1412 { "notify", "MESSAGE ...", 1, 0xffff, acmd_notify },
1413 { "peerinfo", "PEER", 1, 1, acmd_peerinfo },
1414 { "ping", "[OPTIONS] PEER", 1, 0xffff, acmd_ping },
1415 { "port", 0, 0, 0, acmd_port },
1416 { "quit", 0, 0, 0, acmd_quit },
1417 { "reload", 0, 0, 0, acmd_reload },
1418 { "servinfo", 0, 0, 0, acmd_servinfo },
1419 { "stats", "PEER", 1, 1, acmd_stats },
de014da6 1420#ifndef NTRACE
f43df819 1421 { "trace", "[OPTIONS]", 0, 1, acmd_trace },
de014da6 1422#endif
f43df819
MW
1423 { "tunnels", 0, 0, 0, acmd_tunnels },
1424 { "version", 0, 0, 0, acmd_version },
1425 { "warn", "MESSAGE ...", 1, 0xffff, acmd_warn },
1426 { "watch", "[OPTIONS]", 0, 1, acmd_watch },
410c8acf 1427 { 0, 0, 0, 0, 0 }
1428};
1429
1430static void acmd_help(admin *a, unsigned ac, char *av[])
1431{
1432 const acmd *c;
f43df819
MW
1433 for (c = acmdtab; c->name; c++) {
1434 if (c->help)
1435 a_info(a, "%s", c->name, "*%s", c->help, A_END);
1436 else
1437 a_info(a, "%s", c->name, A_END);
1438 }
3cdc3f3a 1439 a_ok(a);
410c8acf 1440}
1441
1442/*----- Connection handling -----------------------------------------------*/
1443
fd3cf232 1444/* --- @a_lock@ --- *
410c8acf 1445 *
1446 * Arguments: @admin *a@ = pointer to an admin block
1447 *
1448 * Returns: ---
1449 *
fd3cf232 1450 * Use: Locks an admin block so that it won't be destroyed
1451 * immediately.
410c8acf 1452 */
1453
060ca767 1454static void a_lock(admin *a) { a->ref++; }
fd3cf232 1455
060ca767 1456/* --- @a_dodestroy@ --- *
fd3cf232 1457 *
1458 * Arguments: @admin *a@ = pointer to an admin block
1459 *
1460 * Returns: ---
1461 *
060ca767 1462 * Use: Actually does the legwork of destroying an admin block.
fd3cf232 1463 */
1464
060ca767 1465static void a_dodestroy(admin *a)
410c8acf 1466{
de014da6 1467 admin_bgop *bg, *bbg;
de014da6 1468
fd3cf232 1469 T( trace(T_ADMIN, "admin: completing destruction of connection %u",
1470 a->seq); )
1471
410c8acf 1472 selbuf_destroy(&a->b);
de014da6 1473 for (bg = a->bg; bg; bg = bbg) {
1474 bbg = bg->next;
1475 bg->cancel(bg);
1476 if (bg->tag) xfree(bg->tag);
1477 xfree(bg);
410c8acf 1478 }
de014da6 1479 if (a->b.reader.fd != a->w.fd) close(a->b.reader.fd);
fd3cf232 1480 close(a->w.fd);
1481
1482 if (a_stdin == a)
1483 a_stdin = 0;
410c8acf 1484 if (a->next)
1485 a->next->prev = a->prev;
1486 if (a->prev)
1487 a->prev->next = a->next;
1488 else
1489 admins = a->next;
410c8acf 1490 DESTROY(a);
1491}
1492
060ca767 1493/* --- @a_unlock@ --- *
1494 *
1495 * Arguments: @admin *a@ = pointer to an admin block
1496 *
1497 * Returns: ---
1498 *
1499 * Use: Unlocks an admin block, allowing its destruction. This is
1500 * also the second half of @a_destroy@.
1501 */
1502
1503static void a_unlock(admin *a)
1504{
1505 assert(a->ref);
1506 if (!--a->ref && (a->f & AF_DEAD))
1507 a_dodestroy(a);
1508}
1509
fd3cf232 1510/* --- @a_destroy@ --- *
1511 *
1512 * Arguments: @admin *a@ = pointer to an admin block
1513 *
1514 * Returns: ---
1515 *
1516 * Use: Destroys an admin block. This requires a certain amount of
1517 * care.
1518 */
1519
de014da6 1520static void freequeue(oqueue *q)
1521{
1522 obuf *o, *oo;
1523
1524 for (o = q->hd; o; o = oo) {
1525 oo = o->next;
1526 xfree(o);
1527 }
1528 q->hd = q->tl = 0;
1529}
1530
fd3cf232 1531static void a_destroy(admin *a)
1532{
1533 /* --- Don't multiply destroy admin blocks --- */
1534
1535 if (a->f & AF_DEAD)
1536 return;
1537
1538 /* --- Make sure nobody expects it to work --- */
1539
1540 a->f |= AF_DEAD;
1541 T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
1542
1543 /* --- Free the output buffers --- */
1544
de014da6 1545 if (a->out.hd)
fd3cf232 1546 sel_rmfile(&a->w);
de014da6 1547 freequeue(&a->out);
fd3cf232 1548
1549 /* --- If the block is locked, that's all we can manage --- */
1550
060ca767 1551 if (!a->ref)
1552 a_dodestroy(a);
1553 T( else
1554 trace(T_ADMIN, "admin: deferring destruction..."); )
fd3cf232 1555}
1556
410c8acf 1557/* --- @a_line@ --- *
1558 *
1559 * Arguments: @char *p@ = pointer to the line read
3008eabf 1560 * @size_t len@ = length of the line
410c8acf 1561 * @void *vp@ = pointer to my admin block
1562 *
1563 * Returns: ---
1564 *
1565 * Use: Handles a line of input.
1566 */
1567
3008eabf 1568static void a_line(char *p, size_t len, void *vp)
410c8acf 1569{
1570 admin *a = vp;
1571 const acmd *c;
3cdc3f3a 1572 char *av[16];
410c8acf 1573 size_t ac;
1574
8d0c7a83 1575 TIMER;
fd3cf232 1576 if (a->f & AF_DEAD)
1577 return;
410c8acf 1578 if (!p) {
de014da6 1579 if (!a->bg)
1580 a_destroy(a);
1581 else {
1582 a->f |= AF_CLOSE;
1583 selbuf_disable(&a->b);
1584 }
410c8acf 1585 return;
1586 }
3cdc3f3a 1587 ac = str_qsplit(p, av, 16, 0, STRF_QUOTE);
410c8acf 1588 if (!ac)
1589 return;
410c8acf 1590 for (c = acmdtab; c->name; c++) {
3cdc3f3a 1591 if (mystrieq(av[0], c->name)) {
410c8acf 1592 ac--;
f43df819
MW
1593 if (c->argmin > ac || ac > c->argmax) {
1594 if (!c->help)
1595 a_fail(a, "bad-syntax", "%s", c->name, "", A_END);
1596 else
1597 a_fail(a, "bad-syntax", "%s", c->name, "%s", c->help, A_END);
1598 } else {
fd3cf232 1599 a_lock(a);
410c8acf 1600 c->func(a, ac, av + 1);
fd3cf232 1601 a_unlock(a);
1602 }
410c8acf 1603 return;
1604 }
1605 }
f43df819 1606 a_fail(a, "unknown-command", "%s", av[0], A_END);
410c8acf 1607}
1608
1609/* --- @a_create@ --- *
1610 *
1611 * Arguments: @int fd_in, fd_out@ = file descriptors to use
3cdc3f3a 1612 * @unsigned f@ = initial flags to set
410c8acf 1613 *
1614 * Returns: ---
1615 *
1616 * Use: Creates a new admin connection.
1617 */
1618
3cdc3f3a 1619void a_create(int fd_in, int fd_out, unsigned f)
410c8acf 1620{
1621 admin *a = CREATE(admin);
3cdc3f3a 1622
93bee2c4 1623 T( static unsigned seq = 0;
1624 a->seq = seq++; )
410c8acf 1625 T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
de014da6 1626 a->bg = 0;
3cdc3f3a 1627 a->f = f;
de014da6 1628 if (fd_in == STDIN_FILENO) a_stdin = a;
fd3cf232 1629 fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
410c8acf 1630 if (fd_out != fd_in)
fd3cf232 1631 fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
410c8acf 1632 selbuf_init(&a->b, &sel, fd_in, a_line, a);
fd3cf232 1633 sel_initfile(&sel, &a->w, fd_out, SEL_WRITE, a_flush, a);
de014da6 1634 a->out.hd = a->out.tl = 0;
410c8acf 1635 a->next = admins;
1636 a->prev = 0;
de014da6 1637 if (admins) admins->prev = a;
410c8acf 1638 admins = a;
1639}
1640
1641/* --- @a_accept@ --- *
1642 *
1643 * Arguments: @int fd@ = file descriptor to accept
1644 * @unsigned mode@ = what to do
1645 * @void *v@ = uninteresting pointer
1646 *
1647 * Returns: ---
1648 *
1649 * Use: Accepts a new admin connection.
1650 */
1651
1652static void a_accept(int fd, unsigned mode, void *v)
1653{
1654 int nfd;
1655 struct sockaddr_un sun;
1656 size_t sz = sizeof(sun);
1657
1658 if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
1ecee057 1659 if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK &&
1660 errno != ECONNABORTED && errno != EPROTO)
f43df819 1661 a_warn("ADMIN", "accept-error", "?ERRNO", A_END);
410c8acf 1662 return;
1663 }
3cdc3f3a 1664 a_create(nfd, nfd, 0);
410c8acf 1665}
1666
1667/* --- @a_daemon@ --- *
1668 *
1669 * Arguments: ---
1670 *
1671 * Returns: ---
1672 *
1673 * Use: Informs the admin module that it's a daemon.
1674 */
1675
1676void a_daemon(void)
1677{
1678 flags |= F_DAEMON;
1679}
1680
1681/* --- @a_init@ --- *
1682 *
1683 * Arguments: @const char *name@ = socket name to create
1684 *
1685 * Returns: ---
1686 *
1687 * Use: Creates the admin listening socket.
1688 */
1689
1690void a_init(const char *name)
1691{
1692 int fd;
1693 int n = 5;
1694 struct sockaddr_un sun;
1695 struct sigaction sa;
1696 size_t sz;
1697
1698 /* --- Set up the socket address --- */
1699
1700 sz = strlen(name) + 1;
1701 if (sz > sizeof(sun.sun_path))
1702 die(EXIT_FAILURE, "socket name `%s' too long", name);
1703 BURN(sun);
1704 sun.sun_family = AF_UNIX;
1705 memcpy(sun.sun_path, name, sz);
1706 sz += offsetof(struct sockaddr_un, sun_path);
1707
1708 /* --- Attempt to bind to the socket --- */
1709
1710 umask(0077);
1711again:
1712 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
1713 die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
1714 if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
1715 struct stat st;
1716 int e = errno;
1717 if (errno != EADDRINUSE) {
1718 die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
1719 sun.sun_path, strerror(e));
1720 }
1721 if (!n)
1722 die(EXIT_FAILURE, "too many retries; giving up");
1723 n--;
1724 if (!connect(fd, (struct sockaddr *)&sun, sz)) {
1725 die(EXIT_FAILURE, "server already listening on admin socket `%s'",
1726 sun.sun_path);
1727 }
1728 if (errno != ECONNREFUSED)
1729 die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
1730 if (stat(sun.sun_path, &st)) {
1731 die(EXIT_FAILURE, "couldn't stat `%s': %s",
1732 sun.sun_path, strerror(errno));
1733 }
1734 if (!S_ISSOCK(st.st_mode))
1735 die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
c7053da9 1736 T( trace(T_ADMIN, "admin: stale socket found; removing it"); )
410c8acf 1737 unlink(sun.sun_path);
1738 close(fd);
1739 goto again;
1740 }
1741 chmod(sun.sun_path, 0600);
1742 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
1743 if (listen(fd, 5))
1744 die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
1745
1746 /* --- Listen to the socket --- */
1747
1748 sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
1749 sel_addfile(&sock);
1750 sockname = name;
1751 bres_init(&sel);
1752 T( trace_custom(a_trace, 0);
1753 trace(T_ADMIN, "admin: enabled custom tracing"); )
1754 flags |= F_INIT;
1755
1756 /* --- Set up signal handlers --- */
1757
1758 sig_add(&s_term, SIGTERM, a_sigdie, 0);
1759 sig_add(&s_hup, SIGHUP, a_sighup, 0);
3cdc3f3a 1760 signal(SIGPIPE, SIG_IGN);
410c8acf 1761 sigaction(SIGINT, 0, &sa);
1762 if (sa.sa_handler != SIG_IGN)
1763 sig_add(&s_int, SIGINT, a_sigdie, 0);
1764}
1765
1766/*----- That's all, folks -------------------------------------------------*/