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 --- 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 + 13 files changed, 1665 insertions(+), 76 deletions(-) create mode 100644 eperd/evsslgetcert.c create mode 100644 eperd/sslgetcert.c (limited to 'eperd') 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' */ -- cgit v1.2.3