From 35294332b2e75151b4b614719ee6522e1afd8748 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 15 May 2015 10:25:07 +0200 Subject: ripe-atlas-fw: imported version 4550 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- .config | 4 +- eperd/Config.in | 6 + eperd/Kbuild | 2 +- eperd/eooqd.c | 20 +- eperd/eperd.c | 9 + eperd/eperd.h | 1 + eperd/evhttpget.c | 2 +- eperd/evsslgetcert.c | 53 ++ eperd/evtdig.c | 29 +- eperd/httpget.c | 264 +++++++-- eperd/ping.c | 40 +- eperd/sslgetcert.c | 1301 +++++++++++++++++++++++++++++++++++++++++++++ eperd/tcputil.c | 5 +- eperd/traceroute.c | 9 + include/applets.h | 2 + include/libbb.h | 3 +- include/usage.h | 9 + libbb/Kbuild | 1 + libbb/validate_atlas_id.c | 8 + networking/Config.in | 6 + networking/Kbuild | 1 + networking/httppost.c | 9 + networking/route.c | 2 +- networking/rptaddr6.c | 493 +++++++++++++++++ networking/rptra6.c | 2 +- shell/hush.c | 13 + 26 files changed, 2214 insertions(+), 80 deletions(-) create mode 100644 eperd/evsslgetcert.c create mode 100644 eperd/sslgetcert.c create mode 100644 libbb/validate_atlas_id.c create mode 100644 networking/rptaddr6.c diff --git a/.config b/.config index 283a61b..cea4e44 100644 --- a/.config +++ b/.config @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Busybox version: 1.13.3 -# Mon May 6 12:33:49 2013 +# Thu Aug 22 15:10:00 2013 # CONFIG_HAVE_DOT_CONFIG=y @@ -339,6 +339,7 @@ CONFIG_EOOQD=y CONFIG_EPERD=y CONFIG_EVHTTPGET=y CONFIG_EVPING=y +CONFIG_EVSSLGETCERT=y CONFIG_EVTDIG=y # CONFIG_FEATURE_EVTDIG_DEBUG is not set CONFIG_EVTRACEROUTE=y @@ -710,6 +711,7 @@ CONFIG_FEATURE_FANCY_PING=y CONFIG_PSCAN=y CONFIG_ROUTE=y CONFIG_RPTRA6=y +CONFIG_RPTADDR6=y CONFIG_RXTXRPT=y CONFIG_SLATTACH=y CONFIG_SSLGETCERT=y diff --git a/eperd/Config.in b/eperd/Config.in index f547875..ed1f7df 100644 --- a/eperd/Config.in +++ b/eperd/Config.in @@ -33,6 +33,12 @@ config EVPING help standalone version of event-driven ping +config EVSSLGETCERT + bool "evsslgetcert" + default n + help + standalone version of event-driven sslgetcert + config EVTDIG bool "evtdig" default n diff --git a/eperd/Kbuild b/eperd/Kbuild index 24d257a..ae83fed 100644 --- a/eperd/Kbuild +++ b/eperd/Kbuild @@ -5,4 +5,4 @@ # Licensed under the GPL v2, see the file LICENSE in this tarball. lib-y:= -lib-$(CONFIG_EPERD) += eooqd.o eperd.o condmv.o httpget.o ping.o traceroute.o evhttpget.o evping.o evtdig.o evtraceroute.o tcputil.o readresolv.o +lib-$(CONFIG_EPERD) += eooqd.o eperd.o condmv.o httpget.o ping.o sslgetcert.o traceroute.o evhttpget.o evping.o evsslgetcert.o evtdig.o evtraceroute.o tcputil.o readresolv.o diff --git a/eperd/eooqd.c b/eperd/eooqd.c index 298e63c..49371ad 100644 --- a/eperd/eooqd.c +++ b/eperd/eooqd.c @@ -28,6 +28,7 @@ struct slot { void *cmdstate; + struct builtin *bp; }; static struct @@ -51,6 +52,7 @@ static struct builtin { "evhttpget", &httpget_ops }, { "evping", &ping_ops }, { "evtdig", &tdig_ops }, + { "evsslgetcert", &sslgetcert_ops }, { "evtraceroute", &traceroute_ops }, { NULL, NULL } }; @@ -123,11 +125,6 @@ int eooqd_main(int argc, char *argv[]) DnsBase= evdns_base_new(EventBase, 1 /*initialize*/); if (!DnsBase) { - crondlog(DIE9 "evdns_base_new failed"); /* exits */ - } - - DnsBase = evdns_base_new(EventBase, 1); - if(!DnsBase) { event_base_free(EventBase); crondlog(DIE9 "evdns_base_new failed"); /* exits */ } @@ -369,6 +366,7 @@ static void add_line(void) if (cmdstate != NULL) { state->slots[slot].cmdstate= cmdstate; + state->slots[slot].bp= bp; state->curr_index= slot; state->curr_busy++; @@ -421,7 +419,7 @@ error: static void cmddone(void *cmdstate) { - int i; + int i, r; char from_filename[80]; char to_filename[80]; struct stat sb; @@ -439,8 +437,14 @@ static void cmddone(void *cmdstate) report("cmddone: state state %p", cmdstate); return; } - state->slots[i].cmdstate= NULL; - state->curr_busy--; + r= state->slots[i].bp->testops->delete(cmdstate); + if (r != 0) + { + state->slots[i].cmdstate= NULL; + state->curr_busy--; + } + else + report("cmddone: strange, cmd %p is busy", cmdstate); snprintf(from_filename, sizeof(from_filename), "/home/atlas/data/new/ooq.%d", i); diff --git a/eperd/eperd.c b/eperd/eperd.c index 48a1ef0..b240e6e 100644 --- a/eperd/eperd.c +++ b/eperd/eperd.c @@ -21,6 +21,8 @@ #include "eperd.h" +#define SAFE_PREFIX ATLAS_DATA_NEW + /* glibc frees previous setenv'ed value when we do next setenv() * of the same variable. uclibc does not do this! */ #if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */ @@ -261,6 +263,12 @@ int eperd_main(int argc UNUSED_PARAM, char **argv) USE_FEATURE_PERD_D(,&LogLevel), &out_filename); /* both -d N and -l N set the same variable: LogLevel */ + if (out_filename && !validate_filename(out_filename, SAFE_PREFIX)) + { + crondlog(DIE9 "insecure file '%s'. allowed path '%s'", + out_filename, SAFE_PREFIX); + } + if (!(opt & OPT_f)) { /* close stdin, stdout, stderr. * close unused descriptors - don't need them. */ @@ -786,6 +794,7 @@ static struct builtin { { "evhttpget", &httpget_ops }, { "evping", &ping_ops }, + { "evsslgetcert", &sslgetcert_ops }, { "evtdig", &tdig_ops }, { "evtraceroute", &traceroute_ops }, { "condmv", &condmv_ops }, diff --git a/eperd/eperd.h b/eperd/eperd.h index ecdab64..8156022 100644 --- a/eperd/eperd.h +++ b/eperd/eperd.h @@ -44,6 +44,7 @@ struct testops extern struct testops condmv_ops; extern struct testops httpget_ops; extern struct testops ping_ops; +extern struct testops sslgetcert_ops; extern struct testops tdig_ops; extern struct testops traceroute_ops; diff --git a/eperd/evhttpget.c b/eperd/evhttpget.c index 91e26ab..212b7c6 100644 --- a/eperd/evhttpget.c +++ b/eperd/evhttpget.c @@ -41,7 +41,7 @@ int evhttpget_main(int argc UNUSED_PARAM, char **argv) state= httpget_ops.init(argc, argv, done); if (!state) { - fprintf(stderr, "evhttpget: traceroute_ops.init failed\n"); + fprintf(stderr, "evhttpget: httpget_ops.init failed\n"); exit(1); } httpget_ops.start(state); diff --git a/eperd/evsslgetcert.c b/eperd/evsslgetcert.c new file mode 100644 index 0000000..e23936a --- /dev/null +++ b/eperd/evsslgetcert.c @@ -0,0 +1,53 @@ +/* Standalone version of the event-based sslgetcert. */ + +#include "libbb.h" +#include +#include +#include +#include + +#include "eperd.h" + +static void done(void *state UNUSED_PARAM) +{ + fprintf(stderr, "And we are done\n"); + exit(0); +} + +int evsslgetcert_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int evsslgetcert_main(int argc UNUSED_PARAM, char **argv) +{ + int r; + void *state; + + /* Create libevent event base */ + EventBase= event_base_new(); + if (!EventBase) + { + fprintf(stderr, "evsslgetcert_base_new failed\n"); + exit(1); + } + DnsBase= evdns_base_new(EventBase, 1 /*initialize*/); + if (!DnsBase) + { + fprintf(stderr, "evdns_base_new failed\n"); + exit(1); + } + + state= sslgetcert_ops.init(argc, argv, done); + if (!state) + { + fprintf(stderr, "evsslgetcert: sslgetcert_ops.init failed\n"); + exit(1); + } + sslgetcert_ops.start(state); + + r= event_base_loop(EventBase, 0); + if (r != 0) + { + fprintf(stderr, "evsslgetcert: event_base_loop failed\n"); + exit(1); + } + return 0; /* not reached */ +} + diff --git a/eperd/evtdig.c b/eperd/evtdig.c index ee6139b..c0d606c 100644 --- a/eperd/evtdig.c +++ b/eperd/evtdig.c @@ -377,7 +377,7 @@ int evtdig_main(int argc, char **argv) crondlog(LVL9 "event_base_new failed"); /* exits */ } - qry = tdig_init(argc, argv, NULL); + qry = tdig_init(argc, argv, local_exit); if(!qry) { crondlog(DIE9 "evdns_base_new failed"); /* exits */ event_base_free (EventBase); @@ -425,7 +425,7 @@ void print_txt_json(unsigned char *rdata, int txt_len, FILE *fh) static void local_exit(void *state UNUSED_PARAM) { - //fprintf(stderr, "And we are done\n"); + fprintf(stderr, "And we are done\n"); exit(0); } @@ -1094,6 +1094,13 @@ static void *tdig_init(int argc, char *argv[], void (*done)(void *state)) break; case 'A': + if (!validate_atlas_id(optarg)) + { + crondlog(LVL8 "bad atlas ID '%s'", + optarg); + tdig_delete(qry); + return NULL; + } qry->str_Atlas = strdup(optarg); break; case 'b': @@ -1756,15 +1763,21 @@ void printErrorQuick (struct query_state *qry) fh = stdout; fprintf(fh, "RESULT { "); - if(qry->str_Atlas) - { - JS(id, qry->str_Atlas); - } + + JS(id, "9202"); gettimeofday(&now, NULL); JS1(time, %ld, now.tv_sec); - snprintf(line, DEFAULT_LINE_LENGTH, "\"query busy\": \"too frequent. previous one is not done yet\""); - fprintf(fh, "\"error\" : { %s }" , line); + snprintf(line, DEFAULT_LINE_LENGTH, "\"query busy\": \"too frequent." + "previous one is not done yet\""); + fprintf(fh, "\"error\" : [{ %s }" , line); + if(qry->str_Atlas) + { + fprintf(fh, ",{" , line); + JS_NC(id, qry->str_Atlas); + fprintf(fh, "}" , line); + } + fprintf(fh, "]"); fprintf(fh, " }"); fprintf(fh, "\n"); diff --git a/eperd/httpget.c b/eperd/httpget.c index c380f18..d0d3e4c 100644 --- a/eperd/httpget.c +++ b/eperd/httpget.c @@ -19,7 +19,7 @@ #define SAFE_PREFIX_IN ATLAS_DATA_OUT #define SAFE_PREFIX_OUT ATLAS_DATA_NEW -#define CONN_TO 5 +#define CONN_TO 5000 /* Timeout in milliseconds */ #define ENV2STATE(env) \ ((struct hgstate *)((char *)env - offsetof(struct hgstate, tu_env))) @@ -39,17 +39,21 @@ static struct option longopts[]= { "post-file", required_argument, NULL, 'p' }, { "post-header", required_argument, NULL, 'h' }, { "post-footer", required_argument, NULL, 'f' }, + { "read-limit", required_argument, NULL, 'r' }, { "store-headers", required_argument, NULL, 'H' }, { "store-body", required_argument, NULL, 'B' }, { "user-agent", required_argument, NULL, 'u' }, + { "timeout", required_argument, NULL, 'S' }, + { "etim", no_argument, NULL, 't' }, + { "eetim", no_argument, NULL, 'T' }, { NULL, } }; -enum readstate { READ_STATUS, READ_HEADER, READ_BODY, READ_SIMPLE, +enum readstate { READ_FIRST, READ_STATUS, READ_HEADER, READ_BODY, READ_SIMPLE, READ_CHUNKED, READ_CHUNK_BODY, READ_CHUNK_END, READ_CHUNKED_TRAILER, READ_DONE }; -enum writestate { WRITE_HEADER, WRITE_POST_HEADER, WRITE_POST_FILE, - WRITE_POST_FOOTER, WRITE_DONE }; +enum writestate { WRITE_FIRST, WRITE_HEADER, WRITE_POST_HEADER, + WRITE_POST_FILE, WRITE_POST_FOOTER, WRITE_DONE }; struct hgbase { @@ -84,6 +88,9 @@ struct hgstate char *post_footer; int max_headers; int max_body; + int etim; + size_t read_limit; + unsigned timeout; /* State */ char busy; @@ -111,6 +118,13 @@ struct hgstate time_t gstart; struct timeval start; double resptime; + double ttr; /* Time to resolve */ + double ttc; /* Time to connect */ + double ttfb; /* Time to first byte */ + int roffset; + int report_roffset; + int first_connect; + int read_truncated; FILE *post_fh; char *post_buf; @@ -131,6 +145,10 @@ struct hgstate char *result; size_t reslen; size_t resmax; + + char *result2; + size_t reslen2; + size_t resmax2; }; static struct hgbase *hg_base; @@ -138,6 +156,7 @@ static struct hgbase *hg_base; static void report(struct hgstate *state); static void add_str(struct hgstate *state, const char *str); static void add_str_quoted(struct hgstate *state, char *str); +static void add_str2(struct hgstate *state, const char *str); static struct hgbase *httpget_base_new(struct event_base *event_base) { @@ -304,6 +323,7 @@ static void timeout_callback(int __attribute((unused)) unused, } switch(state->readstate) { + case READ_FIRST: case READ_STATUS: add_str(state, DBQ(err) ":" DBQ(timeout reading status) ", "); report(state); @@ -342,11 +362,13 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], { int c, i, do_combine, do_get, do_head, do_post, max_headers, max_body, only_v4, only_v6, - do_all, do_http10; - size_t newsiz; + do_all, do_http10, do_etim, do_eetim; + size_t newsiz, read_limit; + unsigned timeout; char *url, *check; char *post_file, *output_file, *post_footer, *post_header, - *A_arg, *store_headers, *store_body; + *A_arg, *store_headers, *store_body, *read_limit_str, + *timeout_str; const char *user_agent; char *host, *port, *hostport, *path; struct hgstate *state; @@ -365,9 +387,13 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], output_file= NULL; store_headers= NULL; store_body= NULL; + read_limit_str= NULL; + timeout_str= NULL; A_arg= NULL; only_v4= 0; only_v6= 0; + do_etim= 0; + do_eetim= 0; user_agent= "httpget for atlas.ripe.net"; if (!hg_base) @@ -390,55 +416,67 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], case '1': do_http10= 0; break; - case 'a': /* --all */ - do_all= 1; + case '4': + only_v4= 1; + only_v6= 0; + break; + case '6': + only_v6= 1; + only_v4= 0; break; case 'A': A_arg= optarg; break; + case 'a': /* --all */ + do_all= 1; + break; + case 'B': /* --store-body */ + store_body= optarg; + break; case 'c': /* --combine */ do_combine= 1; break; - case 'O': - output_file= optarg; + case 'E': /* --head */ + do_get = 0; + do_head = 1; + do_post = 0; break; case 'g': /* --get */ do_get = 1; do_head = 0; do_post = 0; break; - case 'E': /* --head */ - do_get = 0; - do_head = 1; - do_post = 0; + case 'f': /* --post-footer */ + post_footer= optarg; break; - case 'P': /* --post */ - do_get = 0; - do_head = 0; - do_post = 1; + case 'H': /* --store-headers */ + store_headers= optarg; break; case 'h': /* --post-header */ post_header= optarg; break; - case 'f': /* --post-footer */ - post_footer= optarg; + case 'O': + output_file= optarg; + break; + case 'P': /* --post */ + do_get = 0; + do_head = 0; + do_post = 1; break; case 'p': /* --post-file */ post_file= optarg; break; - case 'H': /* --store-headers */ - store_headers= optarg; + case 'r': + read_limit_str= optarg; /* --read-limit */ break; - case 'B': /* --store-body */ - store_body= optarg; + case 'T': /* --etim */ + do_eetim= 1; break; - case '4': - only_v4= 1; - only_v6= 0; + case 't': /* --etim */ + do_etim= 1; break; - case '6': - only_v6= 1; - only_v4= 0; + case 'S': + timeout_str= optarg; /* --timeout */ break; case 'u': /* --user-agent */ user_agent= optarg; @@ -472,6 +510,16 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], } fclose(fh); } + + if (A_arg) + { + if (!validate_atlas_id(A_arg)) + { + crondlog(LVL8 "bad atlas ID '%s'", A_arg); + return NULL; + } + } + if (post_header && !validate_filename(post_header, SAFE_PREFIX_IN)) { crondlog(LVL8 "insecure file '%s'", post_header); @@ -515,6 +563,32 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], } } + read_limit= 0; + if (read_limit_str) + { + read_limit= strtoul(read_limit_str, &check, 10); + if (check[0] != '\0') + { + crondlog(LVL8 + "unable to parse argument (--read-limit) '%s'", + read_limit_str); + return NULL; + } + } + + timeout= CONN_TO; + if (timeout_str) + { + timeout= strtoul(timeout_str, &check, 10); + if (check[0] != '\0') + { + crondlog(LVL8 + "unable to parse argument (--timeout) '%s'", + timeout_str); + return NULL; + } + } + if (!parse_url(url, &host, &port, &hostport, &path)) { /* Do we need to report an error? */ @@ -546,11 +620,21 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], state->user_agent= user_agent ? strdup(user_agent) : NULL; state->max_headers= max_headers; state->max_body= max_body; + state->read_limit= read_limit; + state->timeout= timeout; state->only_v4= 2; state->only_v4= !!only_v4; /* Gcc bug? */ state->only_v6= !!only_v6; + + if (do_eetim) + state->etim= 2; + else if (do_etim) + state->etim= 1; + else + state->etim= 0; + //evtimer_assign(&state->timer, state->base->event_base, // timeout_callback, state); @@ -649,12 +733,26 @@ static void report(struct hgstate *state) state->sin6.sin6_family == AF_INET6 ? 6 : 4); add_str(state, line); + if (state->read_truncated) + add_str(state, ", " DBQ(read-truncated) ": True"); + getnameinfo((struct sockaddr *)&state->sin6, state->socklen, namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); snprintf(line, sizeof(line), ", " DBQ(dst_addr) ":" DBQ(%s), namebuf); add_str(state, line); + + /* End of readtiming */ + if (state->etim >= 2) + { + add_str2(state, " ]"); + add_str(state, state->result2); + free(state->result2); + state->result2= NULL; + state->resmax2= 0; + state->reslen2= 0; + } } if (!state->connecting && !state->dnserr) @@ -684,6 +782,17 @@ static void report(struct hgstate *state) state->headers_size, state->content_offset); add_str(state, line); + if (state->etim >= 1) + { + snprintf(line, sizeof(line), + ", " DBQ(ttr) ":%f" + ", " DBQ(ttc) ":%f" + ", " DBQ(ttfb) ":%f", + state->ttr, + state->ttc, + state->ttfb); + add_str(state, line); + } } if (!state->dnserr) @@ -726,14 +835,17 @@ static void report(struct hgstate *state) tu_cleanup(&state->tu_env); + state->busy= 0; if (state->base->done) state->base->done(state); - state->busy= 0; } static int get_input(struct hgstate *state) { int n; + double t; + struct timeval endtime; + char line[80]; /* Assume that we always end up with a full buffer anyway */ if (state->linemax == 0) @@ -764,12 +876,27 @@ static int get_input(struct hgstate *state) return -1; /* We cannot get more data */ } + if (state->etim >= 2 && state->report_roffset) + { + gettimeofday(&endtime, NULL); + t= (endtime.tv_sec-state->start.tv_sec)*1e3 + + (endtime.tv_usec-state->start.tv_usec)/1e3; + if (state->roffset != 0) + add_str2(state, ","); + snprintf(line, sizeof(line), + " { " DBQ(o) ":" DBQ(%d) + ", " DBQ(t) ": %f }", state->roffset, t); + add_str2(state, line); + state->report_roffset= 0; + } + n= bufferevent_read(state->bev, &state->line[state->linelen], state->linemax-state->linelen); if (n < 0) return -1; state->linelen += n; + state->roffset += n; return 0; } @@ -823,9 +950,24 @@ static void add_str_quoted(struct hgstate *state, char *str) } } +static void add_str2(struct hgstate *state, const char *str) +{ + size_t len; + + len= strlen(str); + if (state->reslen2 + len+1 > state->resmax2) + { + state->resmax2= 2*state->reslen2 + len+1 + 80; + state->result2= xrealloc(state->result2, state->resmax2); + } + memcpy(state->result2+state->reslen2, str, len+1); + state->reslen2 += len; +} + static void err_status(struct hgstate *state, const char *reason) { char line[80]; + snprintf(line, sizeof(line), DBQ(err) ":" DBQ(bad status line: %s) ", ", reason); @@ -836,6 +978,7 @@ static void err_status(struct hgstate *state, const char *reason) static void err_header(struct hgstate *state, const char *reason) { char line[80]; + if (state->max_headers != 0) add_str(state, " ], "); snprintf(line, sizeof(line), @@ -864,10 +1007,39 @@ static void readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) state= ENV2STATE(ptr); + state->report_roffset= 1; for (;;) { + if (state->read_limit > 0 && + state->roffset >= state->read_limit) + { + state->read_truncated= 1; + switch(state->readstate) + { + case READ_FIRST: + case READ_STATUS: + err_status(state, "read truncated"); + return; + case READ_HEADER: + err_header(state, "read truncated"); + return; + default: + state->readstate= READ_DONE; + break; + } + } switch(state->readstate) { + case READ_FIRST: + gettimeofday(&endtime, NULL); + state->ttfb= (endtime.tv_sec- + state->start.tv_sec)*1e3 + + (endtime.tv_usec-state->start.tv_usec)/1e3; + state->readstate= READ_STATUS; + state->roffset= 0; + if (state->etim >= 2) + add_str2(state, ", " DBQ(readtiming) ": ["); + continue; case READ_STATUS: case READ_HEADER: case READ_CHUNKED: @@ -1425,6 +1597,7 @@ static void writecb(struct bufferevent *bev, void *ptr) struct evbuffer *output; off_t cLength; struct stat sb; + struct timeval endtime; state= ENV2STATE(ptr); @@ -1432,6 +1605,13 @@ static void writecb(struct bufferevent *bev, void *ptr) { switch(state->writestate) { + case WRITE_FIRST: + gettimeofday(&endtime, NULL); + state->ttc= (endtime.tv_sec- + state->start.tv_sec)*1e3 + + (endtime.tv_usec-state->start.tv_usec)/1e3; + state->writestate= WRITE_HEADER; + continue; case WRITE_HEADER: output= bufferevent_get_output(bev); evbuffer_add_printf(output, "%s %s HTTP/1.%c\r\n", @@ -1583,6 +1763,7 @@ static void beforeconnect(struct tu_env *env, struct sockaddr *addr, socklen_t addrlen) { struct hgstate *state; + struct timeval endtime; state= ENV2STATE(env); @@ -1590,13 +1771,15 @@ static void beforeconnect(struct tu_env *env, memcpy(&state->sin6, addr, state->socklen); state->connecting= 1; - state->readstate= READ_STATUS; - state->writestate= WRITE_HEADER; + state->readstate= READ_FIRST; + state->writestate= WRITE_FIRST; state->linelen= 0; state->lineoffset= 0; state->headers_size= 0; state->tot_headers= 0; + state->roffset= 0; + state->read_truncated= 0; /* Clear result */ if (!state->do_all || !state->do_combine) @@ -1604,6 +1787,13 @@ static void beforeconnect(struct tu_env *env, add_str(state, "{ "); + if (state->first_connect) + { + gettimeofday(&endtime, NULL); + state->ttr= (endtime.tv_sec-state->start.tv_sec)*1e3 + + (endtime.tv_usec-state->start.tv_usec)/1e3; + state->first_connect= 0; + } gettimeofday(&state->start, NULL); } @@ -1688,6 +1878,8 @@ static void httpget_start(void *state) hgstate->readstate= READ_STATUS; hgstate->writestate= WRITE_HEADER; hgstate->gstart= time(NULL); + gettimeofday(&hgstate->start, NULL); + hgstate->first_connect= 1; memset(&hints, '\0', sizeof(hints)); hints.ai_socktype= SOCK_STREAM; @@ -1695,8 +1887,8 @@ static void httpget_start(void *state) hints.ai_family= AF_INET; else if (hgstate->only_v6) hints.ai_family= AF_INET6; - interval.tv_sec= CONN_TO; - interval.tv_usec= 0; + interval.tv_sec= hgstate->timeout / 1000; + interval.tv_usec= (hgstate->timeout % 1000) * 1000; tu_connect_to_name(&hgstate->tu_env, hgstate->host, hgstate->port, &interval, &hints, timeout_callback, reporterr, dnscount, beforeconnect, diff --git a/eperd/ping.c b/eperd/ping.c index 639ce9c..31d75c1 100644 --- a/eperd/ping.c +++ b/eperd/ping.c @@ -164,13 +164,6 @@ msecstotv(time_t msecs, struct timeval *tv) tv->tv_usec = msecs % 1000 * 1000; } -/* The time since 'tv' in microseconds */ -static time_t -tvtousecs (struct timeval *tv) -{ - return tv->tv_sec * 1000000.0 + tv->tv_usec; -} - static void add_str(struct pingstate *state, const char *str) { size_t len; @@ -257,6 +250,7 @@ static void report(struct pingstate *state) fprintf(fh, ", \"result\": [ %s ] }\n", state->result); free(state->result); state->result= NULL; + state->busy= 0; if (state->out_filename) @@ -274,6 +268,8 @@ static void ping_cb(int result, int bytes, char namebuf[NI_MAXHOST]; char line[256]; + (void)socklen; /* Suppress GCC unused parameter warning */ + pingstate= arg; #if 0 @@ -544,6 +540,11 @@ static void ping_xmit(struct pingstate *host) (struct sockaddr *)&host->loc_sin6, host->loc_socklen, 0, host->rcvd_ttl, NULL, host); + if (host->dns_res) + { + evutil_freeaddrinfo(host->dns_res); + host->dns_res= NULL; + } if (host->base->done) host->base->done(host); @@ -751,14 +752,10 @@ printf("ready_callback4: too short\n"); { /* Use the User Data to relate Echo Request/Reply and evaluate the Round Trip Time */ struct timeval elapsed; /* response time */ - time_t usecs; /* Compute time difference to calculate the round trip */ evutil_timersub (&now, &data->ts, &elapsed); - /* Update counters */ - usecs = tvtousecs(&elapsed); - /* Set destination address of packet as local address */ sin4p= &loc_sin4; memset(sin4p, '\0', sizeof(*sin4p)); @@ -870,14 +867,10 @@ static void ready_callback6 (int __attribute((unused)) unused, { /* Use the User Data to relate Echo Request/Reply and evaluate the Round Trip Time */ struct timeval elapsed; /* response time */ - time_t usecs; /* Compute time difference to calculate the round trip */ evutil_timersub (&now, &data->ts, &elapsed); - /* Update counters */ - usecs = tvtousecs(&elapsed); - /* Set destination address of packet as local address */ memset(&loc_sin6, '\0', sizeof(loc_sin6)); for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr; @@ -1051,6 +1044,15 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[], fclose(fh); } + if (str_Atlas) + { + if (!validate_atlas_id(str_Atlas)) + { + crondlog(LVL8 "bad atlas ID '%s'", str_Atlas); + return NULL; + } + } + af= AF_UNSPEC; if (opt & opt_4) af= AF_INET; @@ -1217,11 +1219,13 @@ static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx) static void ping_start(void *state) { struct pingstate *pingstate; - struct evdns_getaddrinfo_request *evdns_req; struct evutil_addrinfo hints; pingstate= state; + if (pingstate->busy) + return; + if (pingstate->result) free(pingstate->result); pingstate->resmax= 80; pingstate->result= xmalloc(pingstate->resmax); @@ -1247,8 +1251,8 @@ static void ping_start(void *state) hints.ai_family= pingstate->af; printf("hostname '%s', family %d\n", pingstate->hostname, hints.ai_family); - evdns_req= evdns_getaddrinfo(DnsBase, pingstate->hostname, - NULL, &hints, dns_cb, pingstate); + (void) evdns_getaddrinfo(DnsBase, pingstate->hostname, NULL, + &hints, dns_cb, pingstate); } static int ping_delete(void *state) diff --git a/eperd/sslgetcert.c b/eperd/sslgetcert.c new file mode 100644 index 0000000..5b18b33 --- /dev/null +++ b/eperd/sslgetcert.c @@ -0,0 +1,1301 @@ +/* +sslgetcert.c -- libevent-based version of sslgetcert + +Created: April 2013 by Philip Homburg for RIPE NCC +*/ + +#include "libbb.h" +#include +#include +#include +#include +#include +#include +#include + +#include "eperd.h" +#include "tcputil.h" + +#define SAFE_PREFIX_IN ATLAS_DATA_OUT +#define SAFE_PREFIX_OUT ATLAS_DATA_NEW + +#define CONN_TO 5 + +#define ENV2STATE(env) \ + ((struct state *)((char *)env - offsetof(struct state, tu_env))) + +#define DBQ(str) "\"" #str "\"" + +#define MAX_LINE_LEN 2048 /* We don't deal with lines longer than this */ +#define POST_BUF_SIZE 2048 /* Big enough to be efficient? */ + +static struct option longopts[]= +{ + { NULL, } +}; + +enum readstate { READ_HELLO, READ_CERTS, READ_DONE }; +enum writestate { WRITE_HELLO, WRITE_DONE }; + +struct hgbase +{ + struct event_base *event_base; + + struct state **table; + int tabsiz; + + /* For standalone sslgetcert. Called when a sslgetcert instance is + * done. Just one pointer for all instances. It is up to the caller + * to keep it consistent. + */ + void (*done)(void *state); +}; + +struct buf +{ + size_t offset; + size_t size; + size_t maxsize; + char *buf; + struct bufferevent *bev; +}; + +struct msgbuf +{ + struct buf *inbuf; + struct buf *outbuf; + + struct buf buffer; +}; + +struct state +{ + /* Parameters */ + char *output_file; + char *atlas; + char only_v4; + char only_v6; + + /* State */ + char busy; + struct tu_env tu_env; + char dnserr; + char connecting; + char *hostname; + char *portname; + struct bufferevent *bev; + enum readstate readstate; + enum writestate writestate; + int http_result; + char res_major; + char res_minor; + int headers_size; + int tot_headers; + int chunked; + int tot_chunked; + int content_length; + int content_offset; + int subid; + int submax; + time_t gstart; + struct timeval start; + struct timeval t_connect; + double resptime; + FILE *post_fh; + char *post_buf; + + struct buf inbuf; + struct msgbuf msginbuf; + + char *line; + size_t linemax; /* Allocated size of line */ + size_t linelen; /* Current amount of data in line */ + size_t lineoffset; /* Offset in line where to start processing */ + + /* Base and index in table */ + struct hgbase *base; + int index; + + struct sockaddr_in6 sin6; + socklen_t socklen; + struct sockaddr_in6 loc_sin6; + socklen_t loc_socklen; + + char *result; + size_t reslen; + size_t resmax; +}; + +#define BUF_CHUNK 4096 + +#define MSG_HANDSHAKE 22 +#define HS_CLIENT_HELLO 1 +#define HS_SERVER_HELLO 2 +#define HS_CERTIFICATE 11 + +struct hsbuf +{ + struct buf buffer; +}; + +#define URANDOM_DEV "/dev/urandom" + +static struct hgbase *hg_base; + +static int eat_server_hello(struct state *state); +static int eat_certificate(struct state *state); +static void report(struct state *state); +static void add_str(struct state *state, const char *str); + +static void buf_init(struct buf *buf, struct bufferevent *bev) +{ + buf->maxsize= 0; + buf->size= 0; + buf->offset= 0; + buf->buf= NULL; + buf->bev= bev; +} + +static void buf_add(struct buf *buf, const void *data, size_t len) +{ + size_t maxsize; + void *newbuf; + + if (buf->size+len <= buf->maxsize) + { + /* Easy case, just add data */ + memcpy(buf->buf+buf->size, data, len); + buf->size += len; + return; + } + + /* Just get a new buffer */ + maxsize= buf->size-buf->offset + len + BUF_CHUNK; + newbuf= malloc(maxsize); + if (!newbuf) + { + fprintf(stderr, "unable to allocate %ld bytes\n", maxsize); + exit(1); + } + + if (buf->offset < buf->size) + { + /* Copy existing data */ + memcpy(newbuf, buf->buf+buf->offset, buf->size-buf->offset); + buf->size -= buf->offset; + buf->offset= 0; + } + else + { + buf->size= buf->offset= 0; + } + buf->maxsize= maxsize; + free(buf->buf); + buf->buf= newbuf; + + memcpy(buf->buf+buf->size, data, len); + buf->size += len; +} + +static void buf_add_b64(struct buf *buf, void *data, size_t len) +{ + char b64[]= + "ABCDEFGHIJKLMNOP" + "QRSTUVWXYZabcdef" + "ghijklmnopqrstuv" + "wxyz0123456789+/"; + int i; + uint8_t *p; + uint32_t v; + char str[4]; + + p= data; + + for (i= 0; i+3 <= len; i += 3, p += 3) + { + v= (p[0] << 16) + (p[1] << 8) + p[2]; + str[0]= b64[(v >> 18) & 63]; + str[1]= b64[(v >> 12) & 63]; + str[2]= b64[(v >> 6) & 63]; + str[3]= b64[(v >> 0) & 63]; + buf_add(buf, str, 4); + if (i % 48 == 45) + buf_add(buf, "\n", 1); + } + switch(len-i) + { + case 0: break; /* Nothing to do */ + case 1: + v= (p[0] << 16); + str[0]= b64[(v >> 18) & 63]; + str[1]= b64[(v >> 12) & 63]; + str[2]= '='; + str[3]= '='; + buf_add(buf, str, 4); + break; + case 2: + v= (p[0] << 16) + (p[1] << 8); + str[0]= b64[(v >> 18) & 63]; + str[1]= b64[(v >> 12) & 63]; + str[2]= b64[(v >> 6) & 63]; + str[3]= '='; + buf_add(buf, str, 4); + break; + default: + fprintf(stderr, "bad state in buf_add_b64"); + return; + } +} + +static int buf_read(struct buf *buf) +{ + int r; + size_t maxsize; + void *newbuf; + + if (buf->size >= buf->maxsize) + { + if (buf->size-buf->offset + BUF_CHUNK <= buf->maxsize) + { + /* The buffer is big enough, just need to compact */ + fprintf(stderr, "buf_read: should compact"); + errno= ENOSYS; + return -1; + } + else + { + maxsize= buf->size-buf->offset + BUF_CHUNK; + newbuf= malloc(maxsize); + if (!newbuf) + { + fprintf(stderr, "unable to allocate %lu bytes", + (unsigned long)maxsize); + errno= ENOMEM; + return -1; + } + buf->maxsize= maxsize; + + if (buf->size > buf->offset) + { + memcpy(newbuf, buf->buf+buf->offset, + buf->size-buf->offset); + buf->size -= buf->offset; + buf->offset= 0; + } + else + { + buf->size= buf->offset= 0; + } + free(buf->buf); + buf->buf= newbuf; + } + } + + r= bufferevent_read(buf->bev, + buf->buf+buf->size, buf->maxsize-buf->size); + if (r > 0) + { + buf->size += r; + return 0; + } + if (r == 0) + { + errno= EAGAIN; + return -1; + } + fprintf(stderr, "read error: %s", + r == 0 ? "eof" : strerror(errno)); + return -1; +} + +static int buf_write(struct buf *buf) +{ + int r; + size_t len; + struct evbuffer *output; + + output= bufferevent_get_output(buf->bev); + while (buf->offset < buf->size) + { + len= buf->size - buf->offset; + r= evbuffer_add(output, buf->buf+buf->offset, len); + if (r >= 0) + { + buf->offset += len; + continue; + } + fprintf(stderr, "write to %p failed: %s\n", + buf->bev, r == 0 ? "eof" : strerror(errno)); + return -1; + } + return 0; +} + +static void buf_cleanup(struct buf *buf) +{ + free(buf->buf); + buf->offset= buf->size= buf->maxsize= 0; +} + +static void msgbuf_init(struct msgbuf *msgbuf, + struct buf *inbuf, struct buf *outbuf) +{ + buf_init(&msgbuf->buffer, NULL); + msgbuf->inbuf= inbuf; + msgbuf->outbuf= outbuf; +} + +static void msgbuf_add(struct msgbuf *msgbuf, void *buf, size_t size) +{ + buf_add(&msgbuf->buffer, buf, size); +} + +static int msgbuf_read(struct msgbuf *msgbuf, int type) +{ + int r; + size_t len; + uint8_t *p; + + for(;;) + { + if (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5) + { + r= buf_read(msgbuf->inbuf); + if (r < 0) + { + fprintf(stderr, + "msgbuf_read: buf_read failed: %s\n", + strerror(errno)); + return -1; + } + continue; + } + p= (uint8_t *)msgbuf->inbuf->buf+msgbuf->inbuf->offset; + type= p[0]; + if (p[0] != type) + { + fprintf(stderr, "msgbuf_read: got type %d\n", p[0]); + return -1; + } + if (p[1] != 3 || p[2] != 0) + { + fprintf(stderr, + "msgbuf_read: got bad major/minor %d.%d\n", + p[1], p[2]); + return -1; + } + len= (p[3] << 8) + p[4]; + if (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5 + len) + { + r= buf_read(msgbuf->inbuf); + if (r < 0) + { + if (errno != EAGAIN) + { + fprintf(stderr, + "msgbuf_read: buf_read failed: %s\n", + strerror(errno)); + } + return -1; + } + continue; + } + + /* Move the data to the msg buffer */ + msgbuf_add(msgbuf, msgbuf->inbuf->buf+msgbuf->inbuf->offset+5, + len); + msgbuf->inbuf->offset += 5+len; + break; + } + return 0; +} + +static void msgbuf_final(struct msgbuf *msgbuf, int type) +{ + uint8_t c; + size_t len; + + while (msgbuf->buffer.offset < msgbuf->buffer.size) + { + len= msgbuf->buffer.size-msgbuf->buffer.offset; + if (len > 0x4000) + len= 0x4000; + + c= type; + buf_add(msgbuf->outbuf, &c, 1); + + c= 3; + buf_add(msgbuf->outbuf, &c, 1); + + c= 0; + buf_add(msgbuf->outbuf, &c, 1); + + c= len >> 8; + buf_add(msgbuf->outbuf, &c, 1); + + c= len; + buf_add(msgbuf->outbuf, &c, 1); + + buf_add(msgbuf->outbuf, + msgbuf->buffer.buf + msgbuf->buffer.offset, len); + + msgbuf->buffer.offset += len; + } +} + +static void msgbuf_cleanup(struct msgbuf *msgbuf) +{ + buf_cleanup(&msgbuf->buffer); +} + +static void hsbuf_init(struct hsbuf *hsbuf) +{ + buf_init(&hsbuf->buffer, NULL); +} + +static void hsbuf_add(struct hsbuf *hsbuf, const void *buf, size_t len) +{ + buf_add(&hsbuf->buffer, buf, len); +} + +static void hsbuf_cleanup(struct hsbuf *hsbuf) +{ + buf_cleanup(&hsbuf->buffer); +} + +static void hsbuf_final(struct hsbuf *hsbuf, int type, struct msgbuf *msgbuf) +{ + uint8_t c; + size_t len; + + len= hsbuf->buffer.size - hsbuf->buffer.offset; + + c= type; + msgbuf_add(msgbuf, &c, 1); + + c= (len >> 16); + msgbuf_add(msgbuf, &c, 1); + + c= (len >> 8); + msgbuf_add(msgbuf, &c, 1); + + c= len; + msgbuf_add(msgbuf, &c, 1); + + msgbuf_add(msgbuf, hsbuf->buffer.buf + hsbuf->buffer.offset, len); + hsbuf->buffer.offset += len; +} + +static void add_random(struct hsbuf *hsbuf) +{ + int fd; + time_t t; + uint8_t buf[32]; + + t= time(NULL); + buf[0]= t >> 24; + buf[1]= t >> 16; + buf[2]= t >> 8; + buf[3]= t; + + fd= open(URANDOM_DEV, O_RDONLY); + + /* Best effort, just ignore errors */ + if (fd != -1) + { + read(fd, buf+4, sizeof(buf)-4); + close(fd); + } + hsbuf_add(hsbuf, buf, sizeof(buf)); +} + +static void add_sessionid(struct hsbuf *hsbuf) +{ + uint8_t c; + + c= 0; + hsbuf_add(hsbuf, &c, 1); +} + +static void add_ciphers(struct hsbuf *hsbuf) +{ + uint8_t c; + size_t len; + uint8_t ciphers[]= { 0x0,0xff, 0x0,0x88, 0x0,0x87, 0x0,0x39, 0x0,0x38, + 0x0,0x84, 0x0,0x35, 0x0,0x45, 0x0,0x44, 0x0,0x33, 0x0,0x32, + 0x0,0x96, 0x0,0x41, 0x0,0x4, 0x0,0x5, 0x0,0x2f, 0x0,0x16, + 0x0,0x13, 0xfe,0xff, 0x0,0xa }; + + len= sizeof(ciphers); + + c= len >> 8; + hsbuf_add(hsbuf, &c, 1); + c= len; + hsbuf_add(hsbuf, &c, 1); + + hsbuf_add(hsbuf, ciphers, len); +} + +static void add_compression(struct hsbuf *hsbuf) +{ + uint8_t c; + size_t len; + uint8_t compression[]= { 0x1, 0x0 }; + + len= sizeof(compression); + + c= len; + hsbuf_add(hsbuf, &c, 1); + + hsbuf_add(hsbuf, compression, len); +} + + +static struct hgbase *sslgetcert_base_new(struct event_base *event_base) +{ + struct hgbase *base; + + base= xzalloc(sizeof(*base)); + + base->event_base= event_base; + + base->tabsiz= 10; + base->table= xzalloc(base->tabsiz * sizeof(*base->table)); + + return base; +} + +static void timeout_callback(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + struct state *state; + + state= ENV2STATE(s); + + if (state->connecting) + { + add_str(state, DBQ(err) ":" DBQ(connect: timeout)); + if (0 /*state->do_all*/) + report(state); + else + tu_restart_connect(&state->tu_env); + return; + } + switch(state->readstate) + { + case READ_HELLO: + add_str(state, DBQ(err) ":" DBQ(timeout reading hello)); + report(state); + break; + case READ_CERTS: + add_str(state, DBQ(err) ":" DBQ(timeout reading certificates)); + report(state); + break; + default: + printf("in timeout_callback, unhandled case: %d\n", + state->readstate); + } +} + +static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], + void (*done)(void *state)) +{ + int c, i, only_v4, only_v6; + size_t newsiz; + char *hostname, *str_port; + char *output_file, *A_arg; + struct state *state; + FILE *fh; + + /* Arguments */ + output_file= NULL; + A_arg= NULL; + str_port= NULL; + only_v4= 0; + only_v6= 0; + + if (!hg_base) + { + hg_base= sslgetcert_base_new(EventBase); + if (!hg_base) + crondlog(DIE9 "sslgetcert_base_new failed"); + } + + + /* Allow us to be called directly by another program in busybox */ + optind= 0; + while (c= getopt_long(argc, argv, "A:O:p:46", longopts, NULL), c != -1) + { + switch(c) + { + case 'A': + A_arg= optarg; + break; + case 'O': + output_file= optarg; + break; + case 'p': + str_port= optarg; + break; + case '4': + only_v4= 1; + only_v6= 0; + break; + case '6': + only_v6= 1; + only_v4= 0; + break; + default: + crondlog(LVL8 "bad option '%c'", c); + return NULL; + } + } + + if (optind != argc-1) + { + crondlog(LVL8 "exactly one hostname expected"); + return NULL; + } + hostname= argv[optind]; + + if (output_file) + { + if (!validate_filename(output_file, SAFE_PREFIX_OUT)) + { + crondlog(LVL8 "insecure file '%s'", output_file); + return NULL; + } + fh= fopen(output_file, "a"); + if (!fh) + { + crondlog(LVL8 "unable to append to '%s'", + output_file); + return NULL; + } + fclose(fh); + } + + if (A_arg) + { + if (!validate_atlas_id(A_arg)) + { + crondlog(LVL8 "bad atlas ID '%s'", A_arg); + return NULL; + } + } + + state= xzalloc(sizeof(*state)); + state->base= hg_base; + state->atlas= A_arg ? strdup(A_arg) : NULL; + state->output_file= output_file ? strdup(output_file) : NULL; + state->hostname= strdup(hostname); + if (str_port) + state->portname= strdup(str_port); + else + state->portname= strdup("443"); + + state->only_v4= 2; + + state->only_v4= !!only_v4; /* Gcc bug? */ + state->only_v6= !!only_v6; + + state->line= NULL; + state->linemax= 0; + state->linelen= 0; + state->lineoffset= 0; + + for (i= 0; itabsiz; i++) + { + if (hg_base->table[i] == NULL) + break; + } + if (i >= hg_base->tabsiz) + { + newsiz= 2*hg_base->tabsiz; + hg_base->table= xrealloc(hg_base->table, + newsiz*sizeof(*hg_base->table)); + for (i= hg_base->tabsiz; itable[i]= NULL; + i= hg_base->tabsiz; + hg_base->tabsiz= newsiz; + } + state->index= i; + hg_base->table[i]= state; + hg_base->done= done; + + return state; +} + +static void report(struct state *state) +{ + FILE *fh; + char hostbuf[NI_MAXHOST]; + // char line[160]; + + fh= NULL; + if (state->output_file) + { + fh= fopen(state->output_file, "a"); + if (!fh) + crondlog(DIE9 "unable to append to '%s'", + state->output_file); + } + else + fh= stdout; + + fprintf(fh, "RESULT { "); + if (state->atlas) + { + fprintf(fh, DBQ(id) ":" DBQ(%s) ", " + DBQ(fw) ":%d, " + DBQ(time) ":%ld, ", + state->atlas, get_atlas_fw_version(), + state->gstart); + } + + fprintf(fh, DBQ(dst_name) ":" DBQ(%s) ", " + DBQ(dst_port) ":" DBQ(%s) ", ", + state->hostname, state->portname); + + if (!state->dnserr) + { + getnameinfo((struct sockaddr *)&state->sin6, state->socklen, + hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + fprintf(fh, DBQ(dst_addr) ":" DBQ(%s) ", ", hostbuf); + fprintf(fh, DBQ(af) ": %d, ", + state->sin6.sin6_family == AF_INET6 ? 6 : 4); + +#if 0 + getnameinfo((struct sockaddr *)&state->loc_sin6, + state->loc_socklen, hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + fprintf(fh, ", " DBQ(src_addr) ":" DBQ(%s), hostbuf); +#endif + } + + fprintf(fh, "%s }\n", state->result); + free(state->result); + state->result= NULL; + state->resmax= 0; + state->reslen= 0; + + if (state->output_file) + fclose(fh); + + free(state->post_buf); + state->post_buf= NULL; + + if (state->linemax) + { + state->linemax= 0; + free(state->line); + state->line= NULL; + } + + state->bev= NULL; + + tu_cleanup(&state->tu_env); + + state->busy= 0; + if (state->base->done) + state->base->done(state); +} + + +static void add_str(struct state *state, const char *str) +{ + size_t len; + + len= strlen(str); + if (state->reslen + len+1 > state->resmax) + { + state->resmax= state->reslen + len+1 + 80; + state->result= xrealloc(state->result, state->resmax); + } + memcpy(state->result+state->reslen, str, len+1); + state->reslen += len; + //printf("add_str: result = '%s'\n", state->result); +} + +static void readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) +{ + int r; + struct state *state; + + state= ENV2STATE(ptr); + + for (;;) + { + switch(state->readstate) + { + case READ_HELLO: + r= eat_server_hello(state); + if (r == -1) + return; + state->readstate= READ_CERTS; + continue; + + case READ_CERTS: + r= eat_certificate(state); + if (r == -1) + return; + state->readstate= READ_DONE; + continue; + + case READ_DONE: + msgbuf_cleanup(&state->msginbuf); + buf_cleanup(&state->inbuf); + tu_cleanup(&state->tu_env); + state->busy= 0; + if (state->base->done) + state->base->done(state); + return; + + default: + printf("readcb: readstate = %d\n", state->readstate); + return; + } + } +} + +static int eat_server_hello(struct state *state) +{ + int r; + size_t len; + uint8_t *p; + struct msgbuf *msgbuf; + + msgbuf= &state->msginbuf; + + for (;;) + { + if (msgbuf->buffer.size - msgbuf->buffer.offset < 4) + { + r= msgbuf_read(msgbuf, MSG_HANDSHAKE); + if (r < 0) + { + fprintf(stderr, + "eat_server_hello: msgbuf_read failed\n"); + return -1; + + } + continue; + } + p= (uint8_t *)msgbuf->buffer.buf+msgbuf->buffer.offset; + if (p[0] != HS_SERVER_HELLO) + { + fprintf(stderr, "eat_server_hello: got type %d\n", + p[0]); + return -1; + } + len= (p[1] << 16) + (p[2] << 8) + p[3]; + if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len) + { + r= msgbuf_read(msgbuf, MSG_HANDSHAKE); + if (r < 0) + { + fprintf(stderr, + "eat_server_hello: msgbuf_read failed\n"); + return -1; + } + continue; + } + msgbuf->buffer.offset += 4+len; + break; + } + return 0; +} + +static int eat_certificate(struct state *state) +{ + int i, n, r, first, slen; + size_t o, len; + uint8_t *p; + struct msgbuf *msgbuf; + FILE *fh; + double resptime; + struct timeval endtime; + struct buf tmpbuf; + char hostbuf[NI_MAXHOST]; + + msgbuf= &state->msginbuf; + + for (;;) + { + if (msgbuf->buffer.size - msgbuf->buffer.offset < 4) + { + r= msgbuf_read(msgbuf, MSG_HANDSHAKE); + if (r < 0) + { + if (errno != EAGAIN) + { + fprintf(stderr, + "eat_certificate: msgbuf_read failed: %s\n", + strerror(errno)); + } + return -1; + } + continue; + } + p= (uint8_t *)msgbuf->buffer.buf+msgbuf->buffer.offset; + if (p[0] != HS_CERTIFICATE) + { + fprintf(stderr, "eat_certificate: got type %d\n", p[0]); + return -1; + } + len= (p[1] << 16) + (p[2] << 8) + p[3]; + if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len) + { + r= msgbuf_read(msgbuf, MSG_HANDSHAKE); + if (r < 0) + { + fprintf(stderr, + "eat_certificate: msgbuf_read failed\n"); + return -1; + } + continue; + } + p += 4; + n= (p[0] << 16) + (p[1] << 8) + p[2]; + o= 3; + + gettimeofday(&endtime, NULL); + + fh= NULL; + if (state->output_file) + { + fh= fopen(state->output_file, "a"); + if (!fh) + crondlog(DIE9 "unable to append to '%s'", + state->output_file); + } + else + fh= stdout; + + fprintf(fh, "RESULT { "); + if (state->atlas) + { + fprintf(fh, DBQ(id) ":" DBQ(%s) + ", " DBQ(fw) ":%d", + state->atlas, get_atlas_fw_version()); + } + + fprintf(fh, "%s" DBQ(time) ":%ld", + state->atlas ? ", " : "", time(NULL)); + fprintf(fh, ", " DBQ(dst_name) ":" DBQ(%s) ", " + DBQ(dst_port) ":" DBQ(%s), + state->hostname, state->portname); + + fprintf(fh, ", " DBQ(method) ":" DBQ(SSL) ", " + DBQ(ver) ":" DBQ(3.0)); + getnameinfo((struct sockaddr *)&state->sin6, state->socklen, + hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + fprintf(fh, ", " DBQ(dst_addr) ":" DBQ(%s), hostbuf); + fprintf(fh, ", " DBQ(af) ": %d", + state->sin6.sin6_family == AF_INET6 ? 6 : 4); + + getnameinfo((struct sockaddr *)&state->loc_sin6, + state->loc_socklen, hostbuf, sizeof(hostbuf), NULL, 0, + NI_NUMERICHOST); + fprintf(fh, ", " DBQ(src_addr) ":" DBQ(%s), hostbuf); + + resptime= (state->t_connect.tv_sec- state->start.tv_sec)*1e3 + + (state->t_connect.tv_usec-state->start.tv_usec)/1e3; + fprintf(fh, ", " DBQ(ttc) ": %f", resptime); + + resptime= (endtime.tv_sec- state->start.tv_sec)*1e3 + + (endtime.tv_usec-state->start.tv_usec)/1e3; + fprintf(fh, ", " DBQ(rt) ": %f", resptime); + + first= 1; + fprintf(fh, ", " DBQ(cert) ":[ "); + + buf_init(&tmpbuf, NULL); + while (o < 3+n) + { + slen= (p[o] << 16) + (p[o+1] << 8) + p[o+2]; + buf_add_b64(&tmpbuf, p+o+3, slen); + fprintf(fh, "%s\"-----BEGIN CERTIFICATE-----\\n", + !first ? ", " : ""); + for (i= tmpbuf.offset; ibuffer.offset += 4+len; + break; + } + + fprintf(fh, " }\n"); + + if (state->output_file) + fclose(fh); + + return 0; +} + +static void writecb(struct bufferevent *bev, void *ptr) +{ + struct state *state; + struct buf outbuf; + struct msgbuf msgoutbuf; + struct hsbuf hsbuf; + + state= ENV2STATE(ptr); + + for(;;) + { + switch(state->writestate) + { + case WRITE_HELLO: + gettimeofday(&state->t_connect, NULL); + + buf_init(&outbuf, bev); + msgbuf_init(&msgoutbuf, NULL, &outbuf); + hsbuf_init(&hsbuf); + + /* Major/minor */ + hsbuf_add(&hsbuf, "\3", 1); + hsbuf_add(&hsbuf, "\0", 1); + add_random(&hsbuf); + add_sessionid(&hsbuf); + add_ciphers(&hsbuf); + add_compression(&hsbuf); + + hsbuf_final(&hsbuf, HS_CLIENT_HELLO, &msgoutbuf); + msgbuf_final(&msgoutbuf, MSG_HANDSHAKE); + + /* Ignore error */ + (void) buf_write(&outbuf); + + hsbuf_cleanup(&hsbuf); + msgbuf_cleanup(&msgoutbuf); + buf_cleanup(&outbuf); + + /* Done */ + state->writestate= WRITE_DONE; + continue; + + case WRITE_DONE: + return; + + default: + printf("writecb: unknown write state: %d\n", + state->writestate); + return; + } + } + +} + +static void err_reading(struct state *state) +{ + switch(state->readstate) + { + default: + printf("in err_reading, unhandled case\n"); + } +} + +static void dnscount(struct tu_env *env, int count) +{ + struct state *state; + + state= ENV2STATE(env); + state->subid= 0; + state->submax= count; +} + +static void beforeconnect(struct tu_env *env, + struct sockaddr *addr, socklen_t addrlen) +{ + struct state *state; + + state= ENV2STATE(env); + + state->socklen= addrlen; + memcpy(&state->sin6, addr, state->socklen); + + state->connecting= 1; + state->readstate= READ_HELLO; + state->writestate= WRITE_HELLO; + + state->linelen= 0; + state->lineoffset= 0; + state->headers_size= 0; + state->tot_headers= 0; + + /* Clear result */ + //if (!state->do_all || !state->do_combine) + state->reslen= 0; + + gettimeofday(&state->start, NULL); +} + + +static void reporterr(struct tu_env *env, enum tu_err cause, + const char *str) +{ + struct state *state; + char line[80]; + + state= ENV2STATE(env); + + if (env != &state->tu_env) abort(); + + switch(cause) + { + case TU_DNS_ERR: + snprintf(line, sizeof(line), + DBQ(dnserr) ":" DBQ(%s), str); + add_str(state, line); + state->dnserr= 1; + report(state); + break; + + case TU_READ_ERR: + err_reading(state); + break; + + case TU_CONNECT_ERR: + snprintf(line, sizeof(line), + DBQ(err) ":" DBQ(connect: %s), str); + add_str(state, line); + + if (0 /*state->do_all*/) + report(state); + else + tu_restart_connect(&state->tu_env); + break; + + case TU_OUT_OF_ADDRS: + report(state); + break; + + default: + crondlog(DIE9 "reporterr: bad cause %d", cause); + } +} + +static void connected(struct tu_env *env, struct bufferevent *bev) +{ + struct state *state; + + state= ENV2STATE(env); + + if (env != &state->tu_env) abort(); + + state->connecting= 0; + state->bev= bev; + + buf_init(&state->inbuf, bev); + msgbuf_init(&state->msginbuf, &state->inbuf, NULL); + + state->loc_socklen= sizeof(state->loc_sin6); + getsockname(bufferevent_getfd(bev), + &state->loc_sin6, &state->loc_socklen); +} + +static void sslgetcert_start(void *vstate) +{ + struct state *state; + struct evutil_addrinfo hints; + struct timeval interval; + + state= vstate; + + if (state->busy) + { + printf("httget_start: busy\n"); + return; + } + state->busy= 1; + + state->dnserr= 0; + state->connecting= 0; + state->readstate= READ_HELLO; + state->writestate= WRITE_HELLO; + state->gstart= time(NULL); + + memset(&hints, '\0', sizeof(hints)); + hints.ai_socktype= SOCK_STREAM; + if (state->only_v4) + hints.ai_family= AF_INET; + else if (state->only_v6) + hints.ai_family= AF_INET6; + interval.tv_sec= CONN_TO; + interval.tv_usec= 0; + + tu_connect_to_name(&state->tu_env, state->hostname, + state->portname, + &interval, &hints, timeout_callback, + reporterr, dnscount, beforeconnect, + connected, readcb, writecb); +} + +static int sslgetcert_delete(void *vstate) +{ + int ind; + struct state *state; + struct hgbase *base; + + state= vstate; + + printf("sslgetcert_delete: state %p, index %d, busy %d\n", + state, state->index, state->busy); + + if (state->busy) + return 0; + + if (state->line) + crondlog(DIE9 "line is not empty"); + + base= state->base; + ind= state->index; + + if (base->table[ind] != state) + crondlog(DIE9 "strange, state not in table"); + base->table[ind]= NULL; + + //event_del(&state->timer); + + free(state->atlas); + state->atlas= NULL; + free(state->output_file); + state->output_file= NULL; + free(state->hostname); + state->hostname= NULL; + free(state->portname); + state->portname= NULL; + + free(state); + + return 1; +} + +struct testops sslgetcert_ops = { sslgetcert_init, sslgetcert_start, + sslgetcert_delete }; + diff --git a/eperd/tcputil.c b/eperd/tcputil.c index 0e5d221..d3d6134 100644 --- a/eperd/tcputil.c +++ b/eperd/tcputil.c @@ -29,8 +29,6 @@ void tu_connect_to_name(struct tu_env *env, char *host, char *port, void (*readcb)(struct bufferevent *bev, void *ptr), void (*writecb)(struct bufferevent *bev, void *ptr)) { - struct evdns_getaddrinfo_request *evdns_req; - env->interval= *interval; env->reporterr= reporterr; env->reportcount= reportcount; @@ -46,8 +44,7 @@ void tu_connect_to_name(struct tu_env *env, char *host, char *port, env->dnsip= 1; env->connecting= 0; - evdns_req= evdns_getaddrinfo(DnsBase, host, port, - hints, dns_cb, env); + (void) evdns_getaddrinfo(DnsBase, host, port, hints, dns_cb, env); } void tu_restart_connect(struct tu_env *env) diff --git a/eperd/traceroute.c b/eperd/traceroute.c index 014849b..f8fa7f7 100644 --- a/eperd/traceroute.c +++ b/eperd/traceroute.c @@ -2378,6 +2378,15 @@ static void *traceroute_init(int __attribute((unused)) argc, char *argv[], fclose(fh); } + if (str_Atlas) + { + if (!validate_atlas_id(str_Atlas)) + { + crondlog(LVL8 "bad atlas ID '%s'", str_Atlas); + return NULL; + } + } + if (!delay_name_res) { /* Attempt to resolve 'name' */ diff --git a/include/applets.h b/include/applets.h index eb1495e..128d8d9 100644 --- a/include/applets.h +++ b/include/applets.h @@ -149,6 +149,7 @@ USE_EPERD(APPLET(eperd, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) USE_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, _BB_DIR_USR_BIN, _BB_SUID_NEVER, ether_wake)) USE_EVHTTPGET(APPLET(evhttpget, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_EVPING(APPLET(evping, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) +USE_EVSSLGETCERT(APPLET(evsslgetcert, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_EVTDIG(APPLET(evtdig, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_EVTRACEROUTE(APPLET(evtraceroute, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_EXPAND(APPLET(expand, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) @@ -319,6 +320,7 @@ USE_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, USE_ROUTE(APPLET(route, _BB_DIR_SBIN, _BB_SUID_NEVER)) USE_RPM(APPLET(rpm, _BB_DIR_BIN, _BB_SUID_NEVER)) USE_RPTRA6(APPLET(rptra6, _BB_DIR_BIN, _BB_SUID_NEVER)) +USE_RPTADDR6(APPLET(rptaddr6, _BB_DIR_BIN, _BB_SUID_NEVER)) USE_RPM2CPIO(APPLET(rpm2cpio, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_RTCWAKE(APPLET(rtcwake, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) USE_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts)) diff --git a/include/libbb.h b/include/libbb.h index 1231b31..229634d 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -302,7 +302,7 @@ extern char *bb_get_last_path_component_strip(char *path) FAST_FUNC; /* "abc/def/" -> "" and it never modifies 'path' */ extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; -/* What's the best place for this? */ +/* What's the best place for this? AA may be atlas_probe.h */ #define ATLAS_HOME "/home/atlas" #define ATLAS_CRONS ATLAS_HOME "/crons" #define ATLAS_STATUS ATLAS_HOME "/status" @@ -312,6 +312,7 @@ extern char *bb_get_last_path_component_nostrip(const char *path) FAST_FUNC; #define ATLAS_TIMESYNC_FILE ATLAS_STATUS "/timesync.vol" extern int validate_filename(const char *path, const char *prefix); +extern int validate_atlas_id(const char *atlas_id); int ndelay_on(int fd) FAST_FUNC; int ndelay_off(int fd) FAST_FUNC; diff --git a/include/usage.h b/include/usage.h index 2fd5953..5870924 100644 --- a/include/usage.h +++ b/include/usage.h @@ -1055,6 +1055,11 @@ "\n -6 IPv6" \ "todo" +#define evsslgetcert_trivial_usage \ + "todo" +#define evsslgetcert_full_usage "\n\n" \ + "todo" + #define evtraceroute_trivial_usage \ "todo" #define evtraceroute_full_usage "\n\n" \ @@ -3546,6 +3551,10 @@ #define rptra6_trivial_usage \ +#define rptaddr6_full_usage "\n\n" \ + +#define rptaddr6_trivial_usage \ + #define rptra6_full_usage "\n\n" \ #define rtcwake_trivial_usage \ diff --git a/libbb/Kbuild b/libbb/Kbuild index dead4bf..178347d 100644 --- a/libbb/Kbuild +++ b/libbb/Kbuild @@ -99,6 +99,7 @@ lib-y += trim.o lib-y += u_signal_names.o lib-y += udp_io.o lib-y += uuencode.o +lib-y += validate_atlas_id.o lib-y += validate_filename.o lib-y += vdprintf.o lib-y += verror_msg.o diff --git a/libbb/validate_atlas_id.c b/libbb/validate_atlas_id.c new file mode 100644 index 0000000..b4458b1 --- /dev/null +++ b/libbb/validate_atlas_id.c @@ -0,0 +1,8 @@ +#include "libbb.h" + +int validate_atlas_id(const char *atlas_id) +{ + if (strspn(atlas_id, "0123456789") == strlen(atlas_id)) + return 1; + return 0; +} diff --git a/networking/Config.in b/networking/Config.in index 7c72b6d..cdf067a 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -705,6 +705,12 @@ config RPTRA6 help Report received IPv6 router advertisements +config RPTADDR6 + bool "rptaddr6" + default n + help + Report IPv6 addresses and routes + config RXTXRPT bool "rxtxrpt" default n diff --git a/networking/Kbuild b/networking/Kbuild index ff7dfb3..ddb143a 100644 --- a/networking/Kbuild +++ b/networking/Kbuild @@ -32,6 +32,7 @@ lib-$(CONFIG_PING6) += ping.o lib-$(CONFIG_PSCAN) += pscan.o lib-$(CONFIG_ROUTE) += route.o lib-$(CONFIG_RPTRA6) += rptra6.o +lib-$(CONFIG_RPTADDR6) += rptaddr6.o lib-$(CONFIG_RXTXRPT) += rxtxrpt.o lib-$(CONFIG_SLATTACH) += slattach.o lib-$(CONFIG_SSLGETCERT) += sslgetcert.o diff --git a/networking/httppost.c b/networking/httppost.c index 26934ec..6b8b533 100644 --- a/networking/httppost.c +++ b/networking/httppost.c @@ -150,6 +150,15 @@ int httppost_main(int argc, char *argv[]) } url= argv[optind]; + if (atlas_id) + { + if (!validate_atlas_id(atlas_id)) + { + fprintf(stderr, "bad atlas ID '%s'", atlas_id); + return 1; + } + } + if (maxpostsizestr) { maxpostsize= strtoul(maxpostsizestr, &check, 0); diff --git a/networking/route.c b/networking/route.c index 8778ecd..b040539 100644 --- a/networking/route.c +++ b/networking/route.c @@ -462,7 +462,7 @@ static const char flagchars[] ALIGN1 = #endif ; -static void set_flags(char *flagstr, int flags) +void set_flags(char *flagstr, int flags) { int i; diff --git a/networking/rptaddr6.c b/networking/rptaddr6.c new file mode 100644 index 0000000..9c60425 --- /dev/null +++ b/networking/rptaddr6.c @@ -0,0 +1,493 @@ +/* + * rptaddr6.c + * Copyright (c) 2013 RIPE NCC + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include +#include +#include +#include +#include +#include "../eperd/eperd.h" + +#include "libbb.h" + +#define IF_INET6_FILE "/proc/net/if_inet6" +#define IPV6_ROUTE_FILE "/proc/net/ipv6_route" +#define SUFFIX ".new" + +#define SAFE_PREFIX_O ATLAS_DATA_OUT +#define SAFE_PREFIX_N ATLAS_DATA_NEW + +#define OPT_STRING "A:O:c:" + +#define DBQ(str) "\"" #str "\"" +#define JS(key, val) fprintf(fh, "\"" #key"\" : \"%s\" , ", val); +#define JS1(key, fmt, val) fprintf(fh, "\"" #key"\" : "#fmt" , ", val); + +#ifndef IPV6_MASK +#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE) +#endif + +#define IPV6_ADDR_LOOPBACK 0x0010U +#define IPV6_ADDR_LINKLOCAL 0x0020U +#define IPV6_ADDR_SITELOCAL 0x0040U + +#define IPV6_ADDR_COMPATv4 0x0080U + +#define IPV6_ADDR_SCOPE_MASK 0x00f0U + +enum { + OPT_a = (1 << 0), +}; + +static int setup_ipv6_rpt(char *cache_name, int *need_report); +static int rpt_ipv6(char *cache_name, char *out_name, char *opt_atlas, int opt_append); +static void report(const char *fmt, ...); +static void report_err(const char *fmt, ...); + +int rptaddr6_main(int argc, char *argv[]) +{ + int r, need_report; + unsigned opt; + char *opt_atlas; + char *cache_name; /* temp file in an intermediate format */ + char *out_name; /* output file in json: timestamp opt_atlas */ + int opt_append; + + opt_atlas= NULL; + out_name = NULL; + cache_name = NULL; + opt_atlas = NULL; + opt_complementary= NULL; + opt_append = FALSE; + + opt= getopt32(argv, OPT_STRING, &opt_atlas, &out_name, &cache_name); + + if (out_name && !validate_filename(out_name, SAFE_PREFIX_O)) + { + crondlog(LVL8 "insecure file '%s' : allowed '%s'", out_name, + SAFE_PREFIX_O); + return 1; + } + if (cache_name && !validate_filename(cache_name, SAFE_PREFIX_N)) + { + crondlog(LVL8 "insecure file '%s' allowed %s", cache_name, + SAFE_PREFIX_N); + return 1; + } + + if (!cache_name) { + crondlog(LVL8 "missing requried option, -c "); + return 1; + } + + if (opt & OPT_a) + opt_append = TRUE; + + r= setup_ipv6_rpt(cache_name, &need_report); + if (r != 0) + return r; + if (need_report) + { + r = rpt_ipv6(cache_name, out_name, opt_atlas, opt_append); + if (r != 0) + return r; + } + + return 0; +} +static int setup_ipv6_rpt(char *cache_name, int *need_report) +{ + int i, r, n; + char *cp, *cp1; + char filename[80]; + char dst6in[INET6_ADDRSTRLEN]; + char nh6in[INET6_ADDRSTRLEN]; /* next hop */ + char *dst6out = NULL; + char *nh6out = NULL; + char dst6p[8][5]; + char nh6p[8][5]; + char iface[16], flags[16]; + char Scope[32]; + int scope, dad_status, if_idx; + int iflags, metric, refcnt, use, prefix_len, slen; + struct sockaddr_in6 sdst6, snh6; + + char buf1[1024]; + char buf2[1024]; + FILE *in_file, *out_file, *cache_file; + + *need_report= 0; + + if (strlen(cache_name) + strlen(SUFFIX) + 1 > sizeof(filename)) + { + report("cache name '%s' too long", cache_name); + return 1; + } + + strlcpy(filename, cache_name, sizeof(filename)); + strlcat(filename, SUFFIX, sizeof(filename)); + + out_file= fopen(filename, "w"); + if (out_file == NULL) + { + report_err("unable to create '%s'", filename); + return 1; + } + + /* Copy IF_INET6_FILE */ + in_file= fopen(IF_INET6_FILE, "r"); + if (in_file == NULL) + { + report_err("unable to open '%s'", IF_INET6_FILE); + fclose(out_file); + return 1; + } + n = 0; + while ((r = fscanf(in_file, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n", + dst6p[0], dst6p[1], dst6p[2] + , dst6p[3], dst6p[4], dst6p[5] + , dst6p[6], dst6p[7], &if_idx, &prefix_len + , &scope, &dad_status, iface)) != EOF) { + + snprintf(dst6in, sizeof(dst6in), "%s:%s:%s:%s:%s:%s:%s:%s", + dst6p[0], dst6p[1], dst6p[2], dst6p[3], + dst6p[4], dst6p[5], dst6p[6], dst6p[7]); + + inet_pton(AF_INET6, dst6in, (struct sockaddr *) &sdst6.sin6_addr); + sdst6.sin6_family = AF_INET6; + dst6out = INET6_rresolve((struct sockaddr_in6 *) &sdst6, 0x0fff); + + switch (scope & IPV6_ADDR_SCOPE_MASK) { + case 0: + snprintf(Scope, sizeof(Scope), "Global"); + break; + case IPV6_ADDR_LINKLOCAL: + snprintf(Scope, sizeof(Scope), "Link"); + break; + case IPV6_ADDR_SITELOCAL: + snprintf(Scope, sizeof(Scope), "Site"); + break; + case IPV6_ADDR_COMPATv4: + snprintf(Scope, sizeof(Scope), "Compat"); + break; + case IPV6_ADDR_LOOPBACK: + snprintf(Scope, sizeof(Scope), "Host"); + break; + default: + snprintf(Scope, sizeof(Scope), "Unknown %d", scope); + } + r = snprintf(buf2, sizeof(buf2), "%s %s{" DBQ(inet6 addr) " : " + DBQ(%s) ", " DBQ(prefix length) " : %d," + DBQ(scope) " : " DBQ(%s) ", " DBQ(interface) + " : " DBQ(%s) "}", + n ? "" : "\"inet6 addresses\" : [", n ? ", " : "" + , dst6out, prefix_len, Scope, iface); + + /* printf("%s\n", buf2); */ + + if(dst6out) { + free(dst6out); + dst6out=NULL; + } + n++; + if (fwrite(buf2, 1, r, out_file) != r) + { + report_err("error writing to '%s'", filename); + fclose(in_file); + fclose(out_file); + return 1; + } + + if (ferror(in_file)) + { + report_err("error reading from '%s'", IF_INET6_FILE); + fclose(in_file); + fclose(out_file); + return 1; + } + } + if ( n > 0 ) { + r = snprintf(buf2, 2, "]"); + } + if (fwrite(buf2, 1, r, out_file) != r) + { + report_err("error writing to '%s'", filename); + fclose(in_file); + fclose(out_file); + return 1; + } + + fclose(in_file); + + /* Copy IPV6_ROUTE_FILE */ + in_file= fopen(IPV6_ROUTE_FILE, "r"); + if (in_file == NULL) + { + report_err("unable to open '%s'", IPV6_ROUTE_FILE); + fclose(out_file); + return 1; + } + + n = 0; + while ((r = fscanf (in_file, "%4s%4s%4s%4s%4s%4s%4s%4s%x%*s%x%4s%4s%4s%4s%4s%4s%4s%4s%x%x%x%x%s\n", + dst6p[0], dst6p[1], dst6p[2], dst6p[3], dst6p[4], + dst6p[5], dst6p[6], dst6p[7], &prefix_len, &slen, + nh6p[0], nh6p[1], nh6p[2], nh6p[3], nh6p[4], + nh6p[5], nh6p[6], nh6p[7], &metric, &use, &refcnt, &iflags, iface)) != EOF) { + + if (r != 23) { + if ((r < 0) && feof(in_file)) { /* EOF with no (nonspace) chars read. */ + break; + } + report_err("reading '%s'", IF_INET6_FILE); + fclose(in_file); + fclose(out_file); + return 1; + } + + /* skip some the stuff we don't want to report */ + if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */ + continue; + } + if ((iflags & RTF_ADDRCONF) && (iflags & RTF_CACHE)) { /* Skip interfaces that are down. */ + continue; + } + + if ( strncmp (dst6p[0], "ff02", strlen("ff02")) == 0 ) { + continue; + } + if ( strncmp (dst6p[0], "ff00", strlen("ff00")) == 0 ) { + continue; + } + + snprintf(dst6in, sizeof(dst6in), "%s:%s:%s:%s:%s:%s:%s:%s", + dst6p[0], dst6p[1], dst6p[2], dst6p[3], + dst6p[4], dst6p[5], dst6p[6], dst6p[7]); + + snprintf(nh6in, sizeof(nh6in), "%s:%s:%s:%s:%s:%s:%s:%s", + nh6p[0], nh6p[1], nh6p[2], nh6p[3], + nh6p[4], nh6p[5], nh6p[6], nh6p[7]); + + + set_flags(flags, (iflags & IPV6_MASK)); + inet_pton(AF_INET6, dst6in, (struct sockaddr *) &sdst6.sin6_addr); + sdst6.sin6_family = AF_INET6; + dst6out = INET6_rresolve((struct sockaddr_in6 *) &sdst6, 0x0fff); + + inet_pton(AF_INET6, nh6in, (struct sockaddr *) &snh6.sin6_addr); + snh6.sin6_family = AF_INET6; + nh6out = INET6_rresolve((struct sockaddr_in6 *) &snh6, 0x0fff); + + + r = snprintf(buf2, sizeof(buf2), "%s %s{" DBQ(destination) " : " + DBQ(%s) ", " DBQ(prefix length) " : %d," + DBQ(next hop) " : " DBQ(%s) ", " DBQ(flags) + " : " DBQ(%s) ", " DBQ(metric) " : %d , " + DBQ(interface) " : " DBQ(%s) "}", + n ? "" : ", \"inet6 routes\" : [", n ? ", " : "" + , dst6out, prefix_len, nh6out, flags, metric + , iface); + + /* + r = snprintf(buf2, sizeof(buf2), "%s %s{" DBQ(destination) " : " + DBQ(%s) ", " DBQ(prefix length) " : %d," + DBQ(next hop) " : " DBQ(%s) ", " DBQ(flags) + " : " DBQ(%s) ", " DBQ(metric) " : %d , " + DBQ(interface) " : " DBQ(%s) "}", + n ? " " : '"inet6 routes" [' + , n ? ", " : "" + , dst6out, prefix_len, nh6out, flags, metric + , iface); + */ + + /* printf("%s\n", buf2); */ + + if(dst6out) { + free(dst6out); + dst6out=NULL; + } + if(nh6out) { + free(nh6out); + nh6out=NULL; + } + + if (fwrite(buf2, 1, r, out_file) != r) + { + report_err("error writing to '%s'", filename); + fclose(in_file); + fclose(out_file); + return 1; + } + n++; + } + if ( n > 0 ) { + r = snprintf(buf2, 2, "]"); + } + if (fwrite(buf2, 1, r, out_file) != r) + { + report_err("error writing to '%s'", filename); + fclose(in_file); + fclose(out_file); + return 1; + } + + + if (ferror(in_file)) + { + report_err("error reading from '%s'", IPV6_ROUTE_FILE); + fclose(in_file); + fclose(out_file); + return 1; + } + fclose(in_file); + + /* Now check if the new file is different from the cache one */ + fclose(out_file); + cache_file= fopen(cache_name, "r"); + if (cache_file == NULL) + { + /* Assume that any kind of error here calls for reporting */ + *need_report= 1; + } + + if (cache_file) + { + in_file= fopen(filename, "r"); + if (in_file == NULL) + { + report_err("unable to open '%s'", filename); + fclose(cache_file); + return 1; + } + + /* Compare them */ + while (r= fread(buf1, 1, sizeof(buf1), cache_file), r > 0) + { + if (fread(buf2, 1, sizeof(buf2), in_file) != r) + { + /* Ignore errors, just report */ + *need_report= 1; + break; + } + + if (memcmp(buf1, buf2, r) != 0) + { + /* Something changed, report */ + *need_report= 1; + break; + } + } + + /* Maybe something got added */ + if (!*need_report) + { + if (fread(buf2, 1, sizeof(buf2), in_file) != 0) + { + *need_report= 1; + } + } + fclose(cache_file); + fclose(in_file); + } + + if (*need_report) + { + if (rename(filename, cache_name) == -1) + { + report_err("renaming '%s' to '%s' failed", + filename, cache_name); + return 1; + } + } + else + { + if (unlink(filename) == -1) + { + report_err("unlinking '%s' failed", + filename); + } + } + + return 0; +} + + +static int rpt_ipv6(char *cache_name, char *out_name, char *opt_atlas, int opt_append) +{ + FILE *file; + FILE *fh; + char *cp; + char buf[256]; + struct timeval now; + + file= fopen(cache_name, "r"); + if (!file) + { + report_err("unable to open cache file '%s'", cache_name); + return 1; + } + + if (out_name) { + if(opt_append) + fh= fopen(out_name, "w"); + else + fh= fopen(out_name, "w"); + + if (!fh) + crondlog(DIE9 "unable to append to '%s'", out_name); + } + else + fh = stdout; + + fprintf(fh, "RESULT { "); + if(opt_atlas) + { + JS(id, opt_atlas); + } + gettimeofday(&now, NULL); + JS1(time, %ld, now.tv_sec); + + /* Copy all lines */ + while (fgets(buf, sizeof(buf), file) != NULL) + { + fputs(buf, fh); + } + fprintf(fh, "}\n"); + fclose(file); + fclose(fh); + + return 0; +} + +static void report(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + fprintf(stderr, "rptaddr6: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + + va_end(ap); +} + +static void report_err(const char *fmt, ...) +{ + int t_errno; + va_list ap; + + t_errno= errno; + + va_start(ap, fmt); + + fprintf(stderr, "rptaddr6: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": %s\n", strerror(t_errno)); + + va_end(ap); +} diff --git a/networking/rptra6.c b/networking/rptra6.c index b481252..8b45e72 100644 --- a/networking/rptra6.c +++ b/networking/rptra6.c @@ -35,7 +35,7 @@ static void usage(void) exit(1); } -int rptra6_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; +int rptaddr6_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; int rptra6_main(int argc, char *argv[]) { int i, r, first, sock, on, nrecv, rcvd_ttl, olen; diff --git a/shell/hush.c b/shell/hush.c index 9b0d850..13aceb3 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -770,6 +770,7 @@ static int builtin_epoch(char **argv); static int builtin_condmv(char **argv); static int builtin_dfrm(char **argv); static int builtin_rxtxrpt(char **argv); +static int builtin_rptaddr6(char **argv); static int builtin_true(char **argv); static int builtin_set(char **argv); static int builtin_shift(char **argv); @@ -829,6 +830,7 @@ static const struct built_in_command bltins[] = { BLTIN("condmv" , builtin_condmv, "conditional move"), BLTIN("dfrm" , builtin_dfrm, "cleanup if free space gets too low"), BLTIN("rxtxrpt" , builtin_rxtxrpt, "report RX and TX"), + BLTIN("rptaddr6" , builtin_rptaddr6, "report ipv6 address(es) and route(s)"), BLTIN("echo" , builtin_echo, "Write to stdout"), BLTIN("eval" , builtin_eval, "Construct and run shell command"), BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"), @@ -4680,6 +4682,17 @@ static int builtin_rxtxrpt(char **argv) return rxtxrpt_main(argc, argv); } +int builitin_rptaddr6(int argc, char *argv[]); + +static int builtin_rptaddr6(char **argv) +{ + int argc; + + for (argc= 0; argv[argc] != 0; argc++) + ; + return rptaddr6_main(argc, argv); +} + static int builtin_rchoose(char **argv) { -- cgit v1.2.3