From 729eb50738777d6dbc27f81d854c824b32bebb05 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 6 May 2016 15:14:16 +0200 Subject: ripe-atlas-fw: imported version 4730 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- eperd/eperd.c | 2 +- eperd/eperd.h | 2 +- eperd/evtdig.c | 283 +++++++++++++++++++++++------ eperd/httpget.c | 123 +++++++++++-- eperd/ntp.c | 147 +++++++++++++-- eperd/ping.c | 233 +++++++++++++++++++++--- eperd/sslgetcert.c | 174 +++++++++++++++--- eperd/traceroute.c | 524 ++++++++++++++++++++++++++++++++++++++++++++++------- 8 files changed, 1292 insertions(+), 196 deletions(-) (limited to 'eperd') diff --git a/eperd/eperd.c b/eperd/eperd.c index 464cd6c..9c602c6 100644 --- a/eperd/eperd.c +++ b/eperd/eperd.c @@ -269,7 +269,7 @@ int eperd_main(int argc UNUSED_PARAM, char **argv) /* "-b after -f is ignored", and so on for every pair a-b */ opt_complementary = "f-b:b-f:S-L:L-S" USE_FEATURE_PERD_D(":d-l") - "i:+:l+:d+"; /* -i, -l and -d have numeric param */ + ":i+:l+:d+"; /* -i, -l and -d have numeric param */ opt = getopt32(argv, "i:l:L:fc:A:DP:" USE_FEATURE_PERD_D("d:") "O:", &instance_id, &LogLevel, &LogFile, &CDir, &atlas_id, &PidFileName diff --git a/eperd/eperd.h b/eperd/eperd.h index 73b5cc6..2b5df6b 100644 --- a/eperd/eperd.h +++ b/eperd/eperd.h @@ -23,7 +23,7 @@ extern struct globals G; #define LineBase (G.LineBase ) #define FileBase (G.FileBase ) #define oldLine (G.oldLine ) -#define instance_id (G.instance_id ) +#define instance_id (G.instance_id ) #define EventBase (G.EventBase ) #define DnsBase (G.DnsBase ) diff --git a/eperd/evtdig.c b/eperd/evtdig.c index 299a79e..0303cfd 100644 --- a/eperd/evtdig.c +++ b/eperd/evtdig.c @@ -122,9 +122,6 @@ #define T_DNAME ns_t_dname #endif - - - #ifndef ns_t_dlv #define ns_t_dlv 32769 #endif @@ -133,7 +130,6 @@ #define T_DLV ns_t_dlv #endif - #ifndef ns_t_ds #define ns_t_ds 43 #endif @@ -231,8 +227,6 @@ typedef uint32_t counter_t; struct tdig_base { struct event_base *event_base; - struct timeval tv_noreply; /* DNS query Reply timeout */ - /* A circular list of user queries */ struct query_state *qry_head; @@ -264,14 +258,10 @@ struct query_state { struct tdig_base *base; char * name; /* Host identifier as given by the user */ - char * fqname; /* Full qualified hostname */ - char * ipname; /* Remote address in dot notation */ char * infname; /* Bind to this interface (or address) */ u_int16_t qryid; /* query id 16 bit */ struct event event; /* Used to detect read events on udp socket */ - int udp_fd; /* udp_fd and tcp_fd should be merged */ - int tcp_fd; - FILE *tcp_file; + int udp_fd; /* udp_fd */ int wire_size; struct bufferevent *bev_tcp; @@ -294,6 +284,7 @@ struct query_state { int opt_rset; int opt_retry_max; int opt_query_arg; + unsigned opt_timeout; int retry; int resolv_i; @@ -305,6 +296,10 @@ struct query_state { char * server_name; char *out_filename ; + /* For fuzzing */ + char *response_out; + char *response_in; + uint32_t pktsize; /* Packet size in bytes */ struct addrinfo *res, *ressave, *ressent; @@ -338,9 +333,10 @@ struct query_state { unsigned short loc_ai_family ; struct sockaddr_in6 loc_sin6; socklen_t loc_socklen; - u_char *outbuff; + + FILE *resp_file; /* Fuzzing */ }; //DNS header structure struct DNS_HEADER @@ -483,12 +479,16 @@ static struct option longopts[]= { "resolv", no_argument, NULL, O_RESOLV_CONF }, { "qbuf", no_argument, NULL, 1001 }, { "noabuf", no_argument, NULL, 1002 }, + { "timeout", required_argument, NULL, 'T' }, { "evdns", no_argument, NULL, O_EVDNS }, { "out-file", required_argument, NULL, 'O' }, { "p_probe_id", no_argument, NULL, O_PREPEND_PROBE_ID }, { "c_output", no_argument, NULL, O_OUTPUT_COBINED}, - + + { "write-response", required_argument, NULL, 200000 + 'W'}, + { "read-response", required_argument, NULL, 200000 + 'R'}, + { NULL, } }; static char line[(DEFAULT_LINE_LENGTH+1)]; @@ -797,7 +797,7 @@ static void mk_dns_buff(struct query_state *qry, u_char *packet) -/* Attempt to transmit a UDP DNS Request to a serveri. TCP is else where */ +/* Attempt to transmit a UDP DNS Request to a server. TCP is else where */ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event UNUSED_PARAM, void *h) { int r, fd; @@ -807,6 +807,7 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event uint32_t nsent = 0; u_char *outbuff= NULL; int err = 0; + struct timeval tv_noreply; /* Clean the no reply timer (if any was previously set) */ evtimer_del(&qry->noreply_timer); @@ -828,7 +829,16 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event af = ((struct sockaddr *)(qry->res->ai_addr))->sa_family; - if ((fd = socket(af, SOCK_DGRAM, 0) ) < 0 ) + if (qry->response_in) + { + fd= open(qry->response_in, O_RDONLY); + if (fd == -1) + { + crondlog(DIE9 "unable to open '%s': %s", + qry->response_in, strerror(errno)); + } + } + else if ((fd = socket(af, SOCK_DGRAM, 0) ) < 0 ) { snprintf(line, DEFAULT_LINE_LENGTH, "%s \"socket\" : \"socket failed %s\"", qry->err.size ? ", " : "", strerror(errno)); buf_add(&qry->err, line, strlen(line)); @@ -878,7 +888,9 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event } } qry->loc_socklen = sizeof(qry->loc_sin6); - if (connect(qry->udp_fd, qry->res->ai_addr, qry->res->ai_addrlen) == -1) + if (qry->response_in) + ; /* No need to connect */ + else if (connect(qry->udp_fd, qry->res->ai_addr, qry->res->ai_addrlen) == -1) { snprintf(line, DEFAULT_LINE_LENGTH, "%s \"socket\" : \"connect failed %s\"", @@ -891,12 +903,18 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event return; } - nsent = send(qry->udp_fd, outbuff,qry->pktsize, MSG_DONTWAIT); + if (qry->response_in) + nsent= qry->pktsize; + else + { + nsent = send(qry->udp_fd, outbuff,qry->pktsize, + MSG_DONTWAIT); + } qry->ressent = qry->res; if (nsent == qry->pktsize) { - if (getsockname(qry->udp_fd, (struct sockaddr *)&qry->loc_sin6, &qry->loc_socklen) == -1) { - snprintf(line, DEFAULT_LINE_LENGTH, "%s \"getscokname\" : \"%s\"", qry->err.size ? ", " : "", strerror(errno)); + if (!qry->response_in && getsockname(qry->udp_fd, (struct sockaddr *)&qry->loc_sin6, &qry->loc_socklen) == -1) { + snprintf(line, DEFAULT_LINE_LENGTH, "%s \"getsockname\" : \"%s\"", qry->err.size ? ", " : "", strerror(errno)); buf_add(&qry->err, line, strlen(line)); } @@ -905,7 +923,8 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event base->sentbytes += nsent; err = 0; /* Add the timer to handle no reply condition in the given timeout */ - evtimer_add(&qry->noreply_timer, &base->tv_noreply); + msecstotv(qry->opt_timeout, &tv_noreply); + evtimer_add(&qry->noreply_timer, &tv_noreply); if(qry->opt_qbuf) { buf_init(&qry->qbuf, -1); buf_add_b64(&qry->qbuf, outbuff, qry->pktsize, 0); @@ -919,6 +938,8 @@ static void tdig_send_query_callback(int unused UNUSED_PARAM, const short event , strerror(errno) , qry->res->ai_family == AF_INET ? "AF_INET" :"NOT AF_INET"); buf_add(&qry->err, line, strlen(line)); } + if (qry->response_in) + ready_callback(0, 0, qry); } while ((qry->res = qry->res->ai_next) != NULL); free (outbuff); outbuff = NULL; @@ -946,8 +967,9 @@ static void noreply_callback(int unused UNUSED_PARAM, const short event UNUSED_ { struct timeval asap = { 1, 1 }; struct query_state *qry = h; + qry->base->timeout++; - snprintf(line, DEFAULT_LINE_LENGTH, "%s \"timeout\" : %d", qry->err.size ? ", " : "", DEFAULT_NOREPLY_TIMEOUT); + snprintf(line, DEFAULT_LINE_LENGTH, "%s \"timeout\" : %d", qry->err.size ? ", " : "", qry->opt_timeout); buf_add(&qry->err, line, strlen(line)); BLURT(LVL5 "AAA timeout for %s retry %d/%d ", qry->server_name, qry->retry, qry->opt_retry_max); @@ -1062,7 +1084,8 @@ static void tcp_connected(struct tu_env *env, struct bufferevent *bev) qry = ENV2QRY(env); qry->loc_socklen= sizeof(qry->loc_sin6); - getsockname(bufferevent_getfd(bev), &qry->loc_sin6, &qry->loc_socklen); + if (!qry->response_in) + getsockname(bufferevent_getfd(bev), &qry->loc_sin6, &qry->loc_socklen); qry->bev_tcp = bev; outbuff = xzalloc(MAX_DNS_BUF_SIZE); @@ -1072,7 +1095,11 @@ static void tcp_connected(struct tu_env *env, struct bufferevent *bev) wire = xzalloc (payload_len + 4); ldns_write_uint16(wire, qry->pktsize); memcpy(wire + 2, outbuff, qry->pktsize); - evbuffer_add(bufferevent_get_output(qry->bev_tcp), wire, (qry->pktsize +2)); + if (!qry->response_in) + { + evbuffer_add(bufferevent_get_output(qry->bev_tcp), wire, + (qry->pktsize +2)); + } qry->base->sentok++; qry->base->sentbytes+= (qry->pktsize +2); BLURT(LVL5 "send %u bytes", payload_len ); @@ -1111,10 +1138,17 @@ static void tcp_readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) clock_gettime(CLOCK_MONOTONIC_RAW, &rectime); bzero(qry->base->packet, MAX_DNS_BUF_SIZE); - input = bufferevent_get_input(bev); + if (!qry->response_in) + input = bufferevent_get_input(bev); if(qry->wire_size == 0) { - n = evbuffer_remove(input, b2, 2 ); + if (qry->response_in) + n= fread(b2, 1, 2, qry->resp_file); + else + n = evbuffer_remove(input, b2, 2 ); + printf("got %d bytes for response size\n", n); if(n == 2){ + if (qry->response_out) + fwrite(b2, 2, 1, qry->resp_file); qry->wire_size = ldns_read_uint16(b2); buf_init(&qry->packet, -1); } @@ -1124,7 +1158,20 @@ static void tcp_readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) buf_add(&qry->err, line, strlen(line)); } } - while ((n = evbuffer_remove(input,line , DEFAULT_LINE_LENGTH )) > 0) { + for (;;) { + if (qry->response_in) + n= fread(line, 1, 1, qry->resp_file); + else + n = evbuffer_remove(input,line , DEFAULT_LINE_LENGTH ); + printf("got %d bytes for data size\n", n); + if (n <= 0) + { + if (qry->response_in) + noreply_callback(0,0,qry); + break; + } + if (qry->response_out) + fwrite(line, n, 1, qry->resp_file); buf_add(&qry->packet, line, n); if(qry->wire_size == qry->packet.size) { crondlog(LVL5 "in readcb %s %s red %d bytes ", qry->str_Atlas, qry->server_name, qry->wire_size); @@ -1213,6 +1260,7 @@ static void ready_callback (int unused UNUSED_PARAM, const short event UNUSED_PA struct query_state * qry; int nrecv; struct timespec rectime; + FILE *fh; // printf("in ready_callback\n"); @@ -1223,13 +1271,30 @@ static void ready_callback (int unused UNUSED_PARAM, const short event UNUSED_PA clock_gettime(CLOCK_MONOTONIC_RAW, &rectime); /* Receive data from the network */ - nrecv = recv(qry->udp_fd, qry->base->packet, - sizeof(qry->base->packet), MSG_DONTWAIT); + if (qry->response_in) + { + nrecv= read(qry->udp_fd, qry->base->packet, + sizeof(qry->base->packet)); + } + else + { + nrecv = recv(qry->udp_fd, qry->base->packet, + sizeof(qry->base->packet), MSG_DONTWAIT); + } if (nrecv < 0) { /* One more failure */ qry->base->recvfail++; return ; } + if (qry->response_out) + { + fh= fopen(qry->response_out, "w"); + if (fh) + { + fwrite(qry->base->packet, nrecv, 1, fh); + fclose(fh); + } + } process_reply(arg, nrecv, rectime); return; } @@ -1309,6 +1374,25 @@ static bool argProcess (int argc, char *argv[], struct query_state *qry ) tdig_delete(qry); return TRUE; } + if (qry->response_in) + { + if (!validate_filename(qry->response_in, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", qry->response_in); + tdig_delete(qry); + return TRUE; + } + } + if (qry->response_out) + { + if (!validate_filename(qry->response_out, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", qry->response_out); + tdig_delete(qry); + return TRUE; + } + } + if(qry->opt_v6_only == 0) @@ -1346,9 +1430,7 @@ static void *tdig_init(int argc, char *argv[], void (*done)(void *state)) qry->str_Atlas = NULL; qry->out_filename = NULL; qry->opt_proto = 17; - qry->tcp_file = NULL; qry->udp_fd = -1; - qry->tcp_fd = -1; qry->server_name = NULL; qry->str_Atlas = NULL; qry->infname = NULL; @@ -1381,6 +1463,7 @@ static void *tdig_init(int argc, char *argv[], void (*done)(void *state)) qry->result.offset = qry->result.size = qry->result.maxsize= 0; qry->result.buf = NULL; qry->opt_query_arg = 0; + qry->opt_timeout= DEFAULT_NOREPLY_TIMEOUT; /* initialize callbacks : */ /* sendpacket called by UDP send */ @@ -1471,6 +1554,10 @@ static void *tdig_init(int argc, char *argv[], void (*done)(void *state)) qry->opt_proto = 6; break; + case 'T' : + qry->opt_timeout = strtoul(optarg, NULL, 10); + break; + case 1001: qry->opt_qbuf = 1; break; @@ -1733,6 +1820,18 @@ static void *tdig_init(int argc, char *argv[], void (*done)(void *state)) qry->lookupname = strdup(optarg); break; + case 200000 + 'W': + printf("write-response: to %s\n", optarg); + if (qry->response_out) free(qry->response_out); + qry->response_out= strdup(optarg); + break; + + case 200000 + 'R': + printf("read-response: from %s\n", optarg); + if (qry->response_in) free(qry->response_in); + qry->response_in= strdup(optarg); + break; + default: fprintf(stderr, "ERROR unknown option %d ??\n", c); tdig_delete(qry); @@ -1744,6 +1843,10 @@ static void *tdig_init(int argc, char *argv[], void (*done)(void *state)) if(argProcess(argc, argv, qry)) return NULL; + qry->opt_evdns = 1; /* Always enabled, leave the old code in + * place for now. + */ + qry->base = tdig_base; /* insert this qry into the list of queries */ @@ -1805,8 +1908,6 @@ struct tdig_base * tdig_base_new(struct event_base *event_base) evtimer_assign(&tdig_base->statsReportEvent, tdig_base->event_base, tdig_stats, tdig_base); - msecstotv(DEFAULT_NOREPLY_TIMEOUT, &tdig_base->tv_noreply); - // Define the callback to handle UDP Reply // add the raw file descriptor to those monitored for read events @@ -1933,15 +2034,44 @@ void tdig_start (void *arg) } else { // TCP Query + if (qry->response_out) + { + qry->resp_file= fopen(qry->response_out, "w"); + if (!qry->resp_file) + { + crondlog(DIE9 "unable to write to '%s'", + qry->response_out); + } + } + qry->wire_size = 0; crondlog(LVL5 "TCP QUERY %s", qry->server_name); interval.tv_sec = CONN_TO; interval.tv_usec= 0; - tu_connect_to_name (&qry->tu_env, qry->server_name, port_as_char, - &interval, &hints, qry->infname, - tcp_timeout_callback, tcp_reporterr, - tcp_dnscount, tcp_beforeconnect, - tcp_connected, tcp_readcb, tcp_writecb); + + if (qry->response_in) + { + qry->resp_file= fopen(qry->response_in, "r"); + if (!qry->resp_file) + { + crondlog(DIE9 "unable to read from '%s'", + qry->response_in); + } + tcp_connected(&qry->tu_env, NULL); + tcp_writecb(NULL, &qry->tu_env); + while(qry->resp_file != NULL) + tcp_readcb(NULL, &qry->tu_env); + // report(qry); + } + else + { + tu_connect_to_name (&qry->tu_env, qry->server_name, + port_as_char, + &interval, &hints, qry->infname, + tcp_timeout_callback, tcp_reporterr, + tcp_dnscount, tcp_beforeconnect, + tcp_connected, tcp_readcb, tcp_writecb); + } } return ; @@ -2061,6 +2191,12 @@ static void free_qry_inst(struct query_state *qry) struct timeval asap = { 1, 0 }; BLURT(LVL5 "freeing instance of %s ", qry->server_name); + if (qry->response_in) + { + asap.tv_sec= 0; + asap.tv_usec= 1; + } + if(qry->err.size) { buf_cleanup(&qry->err); @@ -2120,6 +2256,11 @@ static void free_qry_inst(struct query_state *qry) if(qry->base->done) { evtimer_add(&qry->done_qry_timer, &asap); } + if (qry->response_in && qry->resp_file) + { + fclose(qry->resp_file); + qry->resp_file= NULL; + } break; } } @@ -2254,7 +2395,7 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) u_int32_t serial; int iMax ; int flagAnswer = 0; - int data_len; + int data_len, offset; int write_out = FALSE; int fw = get_atlas_fw_version(); @@ -2380,12 +2521,21 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) { iMax = MIN(2, ntohs(dnsR->ans_count)); + memset(answers, '\0', sizeof(answers)); + for(i=0;i + result + wire_size) + { + /* Report error? */ + break; + } + answers[i].resource = (struct R_DATA*)(reader); reader = reader + sizeof(struct R_DATA); @@ -2404,7 +2554,11 @@ void printReply(struct query_state *qry, int wire_size, unsigned char *result) flagAnswer++; JS (TYPE, "TXT"); JS (NAME, answers[i].name); - print_txt_json(&result[reader-result], data_len, qry); + offset= reader-result; + if (offset+data_len > wire_size) + data_len= wire_size-offset; + print_txt_json(reader, + data_len, qry); reader = reader + ntohs(answers[i].resource->data_len); AS("}"); @@ -2514,7 +2668,8 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, int* count) { unsigned char *name; - unsigned int p=0,jumped=0, len; + unsigned int p=0,jumped=0, jump_count=0, len; + size_t noffset; *count = 0; name = (unsigned char*)malloc(256); @@ -2522,28 +2677,41 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, name[0]= '\0'; //read the names in 3www6google3com format - while(len= base[offset], len !=0) + while(offset < size && (len= base[offset], len !=0)) { if (len & 0xc0) { if ((len & 0xc0) != 0xc0) { /* Bad format */ - strcpy((char *)name, "format-error"); - printf("format-error: len = %d\n", - len); - abort(); + snprintf((char *)name, sizeof(name), + "format-error at %lu: value 0x%x", + offset, len); + //abort(); return name; } - offset= ((len & ~0xc0) << 8) | base[offset+1]; - if (offset >= size) + noffset= ((len & ~0xc0) << 8) | base[offset+1]; + if (noffset >= size) { - strcpy((char *)name, "offset-error"); - printf("offset-error\n"); - abort(); + snprintf((char *)name, sizeof(name), + "offset-error at %lu: offset %lu", + offset, noffset); + //abort(); return name; } + + if (jump_count > 256) + { + /* Too many */ + snprintf((char *)name, sizeof(name), + "too many redirects at %lu", + offset); + //abort(); + return name; + } + + offset= noffset; if(jumped==0) { /* if we havent jumped to another location @@ -2552,21 +2720,24 @@ unsigned char* ReadName(unsigned char *base, size_t size, size_t offset, *count += 2; } jumped= 1; + jump_count++; continue; } if (offset+len+1 > size) { - strcpy((char *)name, "buf-bounds-error"); - printf("buf-bounds-error\n"); - abort(); + snprintf((char *)name, sizeof(name), + "buf-bounds-error at %lu: len %d", + offset, len); + //abort(); return name; } if (p+len+1 > 255) { - strcpy((char *)name, "name-length-error"); - printf("name-length-error\n"); - abort(); + snprintf((char *)name, sizeof(name), + "name-length-error at %lu: len %d", + offset, p+len+1); + //abort(); return name; } memcpy(name+p, base+offset+1, len); diff --git a/eperd/httpget.c b/eperd/httpget.c index 92ce453..aed596e 100644 --- a/eperd/httpget.c +++ b/eperd/httpget.c @@ -92,6 +92,8 @@ struct hgstate size_t read_limit; unsigned timeout; char *infname; + char *response_in; /* Fuzzing */ + char *response_out; /* State */ char busy; @@ -150,6 +152,8 @@ struct hgstate char *result2; size_t reslen2; size_t resmax2; + + FILE *resp_file; /* Fuzzing */ }; static struct hgbase *hg_base; @@ -332,7 +336,7 @@ static void timeout_callback(int __attribute((unused)) unused, break; case READ_HEADER: if (state->max_headers) - add_str(s, " ], "); + add_str(state, " ], "); add_str(state, ", " DBQ(err) ":" DBQ(timeout reading headers)); report(state); break; @@ -381,7 +385,7 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], char *url, *check; char *post_file, *output_file, *post_footer, *post_header, *A_arg, *store_headers, *store_body, *read_limit_str, - *timeout_str, *infname; + *timeout_str, *infname, *response_in, *response_out; const char *user_agent; char *host, *port, *hostport, *path; struct hgstate *state; @@ -404,6 +408,8 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], timeout_str= NULL; A_arg= NULL; infname= NULL; + response_in= NULL; + response_out= NULL; only_v4= 0; only_v6= 0; do_etim= 0; @@ -420,7 +426,8 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], /* Allow us to be called directly by another program in busybox */ optind= 0; - while (c= getopt_long(argc, argv, "01aA:cI:O:46", longopts, NULL), c != -1) + while (c= getopt_long(argc, argv, "01aA:cI:O:R:W:46", longopts, NULL), + c != -1) { switch(c) { @@ -441,6 +448,12 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], case 'A': A_arg= optarg; break; + case 'R': + response_in= optarg; + break; + case 'W': + response_out= optarg; + break; case 'a': /* --all */ do_all= 1; break; @@ -511,6 +524,23 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], } url= argv[optind]; + if (response_in) + { + if (!validate_filename(response_in, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_in); + return NULL; + } + } + if (response_out) + { + if (!validate_filename(response_out, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_out); + return NULL; + } + } + if (output_file) { if (!validate_filename(output_file, SAFE_PREFIX_OUT)) @@ -621,6 +651,8 @@ static void *httpget_init(int __attribute((unused)) argc, char *argv[], state->base= hg_base; state->atlas= A_arg ? strdup(A_arg) : NULL; state->output_file= output_file ? strdup(output_file) : NULL; + state->response_in= response_in ? strdup(response_in) : NULL; + state->response_out= response_out ? strdup(response_out) : NULL; state->host= host; state->port= port; state->hostport= hostport; @@ -859,6 +891,12 @@ static void report(struct hgstate *state) tu_cleanup(&state->tu_env); + if (state->resp_file) + { + fclose(state->resp_file); + state->resp_file= NULL; + } + state->busy= 0; if (state->base->done) state->base->done(state); @@ -914,11 +952,33 @@ static int get_input(struct hgstate *state) state->report_roffset= 0; } - n= bufferevent_read(state->bev, - &state->line[state->linelen], - state->linemax-state->linelen); + if (state->response_in) + { + if (!state->resp_file) + abort(); + n= fread(&state->line[state->linelen], 1, 1, state->resp_file); + if (n == -1 || n == 0) + { + fclose(state->resp_file); + state->resp_file= NULL; + timeout_callback(0, 0, &state->tu_env); + report(state); + return -1; + } + } + else + { + n= bufferevent_read(state->bev, + &state->line[state->linelen], + state->linemax-state->linelen); + } if (n < 0) return -1; + if (state->response_out) + { + fwrite(&state->line[state->linelen], n, 1, + state->resp_file); + } state->linelen += n; state->roffset += n; return 0; @@ -1556,7 +1616,7 @@ static void readcb(struct bufferevent *bev UNUSED_PARAM, void *ptr) continue; case READ_DONE: - if (state->bev) + if (state->bev || state->response_in) { state->bev= NULL; clock_gettime(CLOCK_MONOTONIC_RAW, &endtime); @@ -1637,6 +1697,11 @@ static void writecb(struct bufferevent *bev, void *ptr) state->writestate= WRITE_HEADER; continue; case WRITE_HEADER: + if (state->response_in) + { + state->writestate = WRITE_DONE; + continue; + } output= bufferevent_get_output(bev); evbuffer_add_printf(output, "%s %s HTTP/1.%c\r\n", state->do_get ? "GET" : @@ -1892,8 +1957,11 @@ static void connected(struct tu_env *env, struct bufferevent *bev) state->bev= bev; state->loc_socklen= sizeof(state->loc_sin6); - getsockname(bufferevent_getfd(bev), - &state->loc_sin6, &state->loc_socklen); + if (!state->response_in) + { + getsockname(bufferevent_getfd(bev), + &state->loc_sin6, &state->loc_socklen); + } } static void httpget_start(void *state) @@ -1919,6 +1987,16 @@ static void httpget_start(void *state) clock_gettime(CLOCK_MONOTONIC_RAW, &hgstate->start); hgstate->first_connect= 1; + if (hgstate->response_out) + { + hgstate->resp_file= fopen(hgstate->response_out, "w"); + if (!hgstate->resp_file) + { + crondlog(DIE9 "unable to write to '%s'", + hgstate->response_out); + } + } + memset(&hints, '\0', sizeof(hints)); hints.ai_socktype= SOCK_STREAM; if (hgstate->only_v4) @@ -1927,10 +2005,29 @@ static void httpget_start(void *state) hints.ai_family= AF_INET6; 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, hgstate->infname, timeout_callback, - reporterr, dnscount, beforeconnect, - connected, readcb, writecb); + + if (hgstate->response_in) + { + hgstate->resp_file= fopen(hgstate->response_in, "r"); + if (!hgstate->resp_file) + { + crondlog(DIE9 "unable to read from '%s'", + hgstate->response_in); + } + connected(&hgstate->tu_env, NULL); + writecb(NULL, &hgstate->tu_env); + while(hgstate->resp_file != NULL) + readcb(NULL, &hgstate->tu_env); + report(hgstate); + } + else + { + tu_connect_to_name(&hgstate->tu_env, hgstate->host, + hgstate->port, + &interval, &hints, hgstate->infname, timeout_callback, + reporterr, dnscount, beforeconnect, + connected, readcb, writecb); + } } static int httpget_delete(void *state) diff --git a/eperd/ntp.c b/eperd/ntp.c index 7dc1109..0289f35 100644 --- a/eperd/ntp.c +++ b/eperd/ntp.c @@ -31,15 +31,10 @@ #define NTP_PORT 123 -#define NTP_OPT_STRING ("!46c:i:w:A:O:") +#define NTP_OPT_STRING ("!46c:i:w:A:O:R:W:") #define OPT_4 (1 << 0) #define OPT_6 (1 << 1) -#define OPT_I (1 << 2) -#define OPT_U (1 << 3) -#define OPT_F (1 << 4) -#define OPT_r (1 << 5) -#define OPT_T (1 << 6) #define IPHDR 20 @@ -83,6 +78,8 @@ struct ntpstate char do_v6; char count; unsigned timeout; + char *response_in; /* Fuzzing */ + char *response_out; /* Base and index in table */ struct ntpbase *base; @@ -135,6 +132,8 @@ struct ntpstate size_t reslen; size_t resmax; char open_result; + + FILE *resp_file_out; /* Fuzzing */ }; static struct ntpbase *ntp_base; @@ -182,6 +181,8 @@ struct ntphdr #define NTP_4G 4294967296.0 +static void ready_callback(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s); static int create_socket(struct ntpstate *state); static void add_str(struct ntpstate *state, const char *str) @@ -523,9 +524,14 @@ static void send_pkt(struct ntpstate *state) ((struct sockaddr_in *)&state->sin6)->sin_port= htons(NTP_PORT); - r= sendto(state->socket, base->packet, len, 0, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* No need to send */ + else + { + r= sendto(state->socket, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { static int doit=0; if (doit && r != -1) @@ -563,6 +569,13 @@ static void send_pkt(struct ntpstate *state) interval.tv_usec= state->timeout % 1000000; evtimer_add(&state->timer, &interval); + if (state->response_in) + { + if (state->sin6.sin6_family == AF_INET6) + ready_callback(0, 0, state); + else + ready_callback(0, 0, state); + } } static void ready_callback(int __attribute((unused)) unused, @@ -586,8 +599,49 @@ static void ready_callback(int __attribute((unused)) unused, base= state->base; slen= sizeof(remote); - nrecv= recvfrom(state->socket, base->packet, sizeof(base->packet), - MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + if (state->response_in) + { + uint32_t len; + if (read(state->socket, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_callback4: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_callback4: bad value for len: %u", + len); + } + if (read(state->socket, base->packet, len) != len) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (read(state->socket, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + nrecv= len; + } + else + { + nrecv= recvfrom(state->socket, base->packet, + sizeof(base->packet), + MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + } if (nrecv == -1) { /* Strange, read error */ @@ -596,6 +650,16 @@ static void ready_callback(int __attribute((unused)) unused, } // printf("ready_callback: got packet\n"); + if (state->resp_file_out) + { + uint32_t len= nrecv; + + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + + if (nrecv < sizeof(*ntphdr)) { /* Short packet */ @@ -1622,6 +1686,7 @@ static void *ntp_init(int __attribute((unused)) argc, char *argv[], char *out_filename; const char *destportstr; char *interface; + char *response_in, *response_out; struct ntpstate *state; FILE *fh; @@ -1638,10 +1703,13 @@ static void *ntp_init(int __attribute((unused)) argc, char *argv[], timeout= 1000; str_Atlas= NULL; out_filename= NULL; + response_in= NULL; + response_out= NULL; opt_complementary = "=1:4--6:i--u:c+:w+:"; opt = getopt32(argv, NTP_OPT_STRING, &count, - &interface, &timeout, &str_Atlas, &out_filename); + &interface, &timeout, &str_Atlas, &out_filename, + &response_in, &response_out); hostname = argv[optind]; if (opt == 0xffffffff) @@ -1652,6 +1720,23 @@ static void *ntp_init(int __attribute((unused)) argc, char *argv[], do_v6= !!(opt & OPT_6); + if (response_in) + { + if (!validate_filename(response_in, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_in); + return NULL; + } + } + if (response_out) + { + if (!validate_filename(response_out, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_out); + return NULL; + } + } + if (out_filename) { if (!validate_filename(out_filename, SAFE_PREFIX)) @@ -1689,6 +1774,8 @@ static void *ntp_init(int __attribute((unused)) argc, char *argv[], state->hostname= strdup(hostname); state->do_v6= do_v6; state->out_filename= out_filename ? strdup(out_filename) : NULL; + state->response_in= response_in ? strdup(response_in) : NULL; + state->response_out= response_out ? strdup(response_out) : NULL; state->base= ntp_base; state->busy= 0; state->result= NULL; @@ -1785,7 +1872,17 @@ static int create_socket(struct ntpstate *state) type= SOCK_DGRAM; protocol= 0; - state->socket= xsocket(af, type, protocol); + if (state->response_in) + { + state->socket= open(state->response_in, O_RDONLY); + if (state->socket == -1) + { + crondlog(DIE9 "unable to open '%s'", + state->response_in); + } + } + else + state->socket= xsocket(af, type, protocol); #if 0 { errno= ENOSYS; state->socket= -1; } #endif @@ -1814,9 +1911,14 @@ static int create_socket(struct ntpstate *state) } } - r= connect(state->socket, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* No need to connect */ + else + { + r= connect(state->socket, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { errno= ENOSYS; r= -1; } #endif @@ -1832,7 +1934,7 @@ static int create_socket(struct ntpstate *state) return -1; } state->loc_socklen= sizeof(state->loc_sin6); - if (getsockname(state->socket, + if (!state->response_in && getsockname(state->socket, &state->loc_sin6, &state->loc_socklen) == -1) { @@ -1954,6 +2056,17 @@ static void ntp_start(void *state) ntpstate= state; + if (ntpstate->response_out) + { + ntpstate->resp_file_out= fopen(ntpstate->response_out, "w"); + if (!ntpstate->resp_file_out) + { + crondlog(DIE9 "unable to write to '%s'", + ntpstate->response_out); + } + } + + memset(&hints, '\0', sizeof(hints)); hints.ai_socktype= SOCK_DGRAM; hints.ai_family= ntpstate->do_v6 ? AF_INET6 : AF_INET; diff --git a/eperd/ping.c b/eperd/ping.c index 0b8d1b2..3f03bf8 100644 --- a/eperd/ping.c +++ b/eperd/ping.c @@ -26,7 +26,7 @@ #define DBQ(str) "\"" #str "\"" -#define PING_OPT_STRING ("!46rc:s:A:O:i:I:") +#define PING_OPT_STRING ("!46rc:s:A:O:i:I:R:W:") enum { @@ -148,6 +148,11 @@ struct pingstate /* Packets Counters */ size_t cursize; counter_t sentpkts; /* Total # of ICMP Echo Requests sent */ + + /* For fuzzing */ + char *response_in; + char *response_out; + FILE *resp_file_out; }; /* User Data added to the ICMP header @@ -165,6 +170,10 @@ struct evdata { }; +static void ready_callback4(int __attribute((unused)) unused, + const short __attribute((unused)) event, void * arg); +static void ready_callback6(int __attribute((unused)) unused, + const short __attribute((unused)) event, void * arg); /* Initialize a struct timeval by converting milliseconds */ static void @@ -587,10 +596,18 @@ static void ping_xmit(struct pingstate *host) host->loc_socklen= sizeof(host->loc_sin6); getsockname(host->socket, &host->loc_sin6, &host->loc_socklen); - nsent = sendto(host->socket, base->packet, - host->cursize+ICMP6_HDRSIZE, - MSG_DONTWAIT, (struct sockaddr *)&host->sin6, - host->socklen); + if (host->response_in) + { + /* Assume the send succeeded */ + nsent= host->cursize+ICMP6_HDRSIZE; + } + else + { + nsent = sendto(host->socket, base->packet, + host->cursize+ICMP6_HDRSIZE, + MSG_DONTWAIT, (struct sockaddr *)&host->sin6, + host->socklen); + } } else @@ -602,10 +619,18 @@ static void ping_xmit(struct pingstate *host) host->loc_socklen= sizeof(host->loc_sin6); getsockname(host->socket, &host->loc_sin6, &host->loc_socklen); - nsent = sendto(host->socket, base->packet, - host->cursize+ICMP_MINLEN, - MSG_DONTWAIT, (struct sockaddr *)&host->sin6, - host->socklen); + if (host->response_in) + { + /* Assume the send succeeded */ + nsent= host->cursize+ICMP_MINLEN; + } + else + { + nsent = sendto(host->socket, base->packet, + host->cursize+ICMP_MINLEN, + MSG_DONTWAIT, (struct sockaddr *)&host->sin6, + host->socklen); + } } if (nsent > 0) @@ -631,6 +656,14 @@ static void ping_xmit(struct pingstate *host) /* Add the timer to handle no reply condition in the given timeout */ msecstotv(host->interval, &tv_interval); evtimer_add(&host->ping_timer, &tv_interval); + + if (host->response_in) + { + if (host->sin6.sin6_family == AF_INET6) + ready_callback6(0, 0, host); + else + ready_callback4(0, 0, host); + } } @@ -690,12 +723,58 @@ static void ready_callback4 (int __attribute((unused)) unused, clock_gettime(CLOCK_MONOTONIC_RAW, &now); /* Receive data from the network */ - nrecv = recvfrom(state->socket, base->packet, sizeof(base->packet), MSG_DONTWAIT, (struct sockaddr *) &remote, &slen); + if (state->response_in) + { + uint32_t len; + if (read(state->socket, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_callback4: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_callback4: bad value for len: %u", + len); + } + if (read(state->socket, base->packet, len) != len) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (read(state->socket, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + nrecv= len; + } + else nrecv = recvfrom(state->socket, base->packet, sizeof(base->packet), MSG_DONTWAIT, (struct sockaddr *) &remote, &slen); if (nrecv < 0) { goto done; } + if (state->resp_file_out) + { + uint32_t len= nrecv; + + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + #if 0 { int i; printf("received:"); @@ -712,7 +791,6 @@ static void ready_callback4 (int __attribute((unused)) unused, if (nrecv < hlen + ICMP_MINLEN || ip->ip_hl < 5) { /* One more too short packet */ -printf("ready_callback4: too short\n"); goto done; } @@ -731,9 +809,7 @@ printf("ready_callback4: too short\n"); /* Check the ICMP payload for legal values of the 'index' portion */ if (data->index >= base->tabsiz || base->table[data->index] == NULL) - { goto done; - } /* Get the pointer to the host descriptor in our internal table */ if (state != base->table[data->index]) @@ -794,7 +870,6 @@ printf("ready_callback4: too short\n"); } else { -printf("ready_callback4: not an echo reply\n"); /* Handle this condition exactly as the request has expired */ noreply_callback (-1, -1, state); } @@ -857,12 +932,66 @@ static void ready_callback6 (int __attribute((unused)) unused, msg.msg_flags= 0; /* Not really needed */ /* Receive data from the network */ - nrecv= recvmsg(state->socket, &msg, MSG_DONTWAIT); + if (state->response_in) + { + uint32_t len; + if (read(state->socket, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_callback6: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_callback6: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_callback6: bad value for len: %u", + len); + } + if (read(state->socket, base->packet, len) != len) + { + //printf("ready_callback6: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + if (read(state->socket, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_callback6: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + + nrecv= len; + + /* Do not try to fuzz the cmsgbuf. We assume stuff returned by + * the kernel can be trusted. + */ + memset(cmsgbuf, '\0', sizeof(cmsgbuf)); + } + else + nrecv= recvmsg(state->socket, &msg, MSG_DONTWAIT); + if (nrecv < 0) { goto done; } + if (state->resp_file_out) + { + uint32_t len= nrecv; + + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + /* Check the ICMP header to drop unexpected packets due to * unrecognized id */ @@ -970,6 +1099,7 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[], char *str_Atlas; char *out_filename; char *interface; + char *response_in, *response_out; struct pingstate *state; len_and_sockaddr *lsa; FILE *fh; @@ -1016,10 +1146,13 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[], out_filename= NULL; interval= DEFAULT_PING_INTERVAL; interface= NULL; + response_in= NULL; + response_out= NULL; /* exactly one argument needed; -c NUM */ opt_complementary = "=1:c+:s+:i+"; opt = getopt32(argv, PING_OPT_STRING, &pingcount, &size, - &str_Atlas, &out_filename, &interval, &interface); + &str_Atlas, &out_filename, &interval, &interface, + &response_in, &response_out); hostname = argv[optind]; if (opt == 0xffffffff) @@ -1034,6 +1167,23 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[], return NULL; } + if (response_in) + { + if (!validate_filename(response_in, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_in); + return NULL; + } + } + if (response_out) + { + if (!validate_filename(response_out, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_out); + return NULL; + } + } + if (out_filename) { if (!validate_filename(out_filename, SAFE_PREFIX)) @@ -1065,6 +1215,9 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[], else af= AF_INET6; delay_name_res= !!(opt & opt_r); + delay_name_res= 1; /* Always enabled, leave the old code in + * place for now. + */ if (!delay_name_res) { @@ -1103,6 +1256,14 @@ static void *ping_init(int __attribute((unused)) argc, char *argv[], state->interval= interval; state->interface= interface; state->socket= -1; + state->response_in= response_in ? strdup(response_in) : NULL; + state->response_out= response_out ? strdup(response_out) : NULL; + + if (state->response_in || state->response_out) + { + ping_base->pid= 42; + memset(&cookie, 42, sizeof(cookie)); + } state->seq = 1; @@ -1166,9 +1327,18 @@ static void ping_start2(void *state) /* Check if the ICMP protocol is available on this system */ p_proto= IPPROTO_ICMP; - /* Create an endpoint for communication using raw socket for - * ICMP calls */ - if ((fd = socket(AF_INET, SOCK_RAW, p_proto)) == -1) { + if (pingstate->response_in) + { + fd= open(pingstate->response_in, O_RDONLY); + if (fd == -1) + { + crondlog(DIE9 "unable to open '%s'", + pingstate->response_in); + } + } + else if ((fd = socket(AF_INET, SOCK_RAW, p_proto)) == -1) { + /* Create an endpoint for communication using raw + * socket for ICMP calls */ snprintf(line, sizeof(line), "{ " DBQ(error) ":" DBQ(socket failed: %s) " }", strerror(errno)); @@ -1191,7 +1361,16 @@ static void ping_start2(void *state) /* Check if the ICMP6 protocol is available on this system */ p_proto= IPPROTO_ICMPV6; - if ((fd = socket(AF_INET6, SOCK_RAW, p_proto)) == -1) { + if (pingstate->response_in) + { + fd= open(pingstate->response_in, O_RDONLY); + if (fd == -1) + { + crondlog(DIE9 "unable to open '%s'", + pingstate->response_in); + } + } + else if ((fd = socket(AF_INET6, SOCK_RAW, p_proto)) == -1) { snprintf(line, sizeof(line), "{ " DBQ(error) ":" DBQ(socket failed: %s) " }", strerror(errno)); @@ -1238,7 +1417,8 @@ static void ping_start2(void *state) } } - if (connect(pingstate->socket, &pingstate->sin6, + if (!pingstate->response_in && + connect(pingstate->socket, &pingstate->sin6, pingstate->socklen) == -1) { snprintf(line, sizeof(line), @@ -1362,6 +1542,7 @@ static void ping_start(void *state) pingstate->resmax= 80; pingstate->result= xmalloc(pingstate->resmax); pingstate->reslen= 0; + pingstate->resp_file_out= NULL; pingstate->first= 1; pingstate->got_reply= 0; @@ -1370,6 +1551,16 @@ static void ping_start(void *state) pingstate->maxpkts= pingstate->pingcount; + if (pingstate->response_out) + { + pingstate->resp_file_out= fopen(pingstate->response_out, "w"); + if (!pingstate->resp_file_out) + { + crondlog(DIE9 "unable to write to '%s'", + pingstate->response_out); + } + } + if (!pingstate->delay_name_res) { ping_start2(state); diff --git a/eperd/sslgetcert.c b/eperd/sslgetcert.c index 91dea01..e457106 100644 --- a/eperd/sslgetcert.c +++ b/eperd/sslgetcert.c @@ -74,6 +74,8 @@ struct state char *output_file; char *atlas; char *infname; + char *response_in; /* Fuzzing */ + char *response_out; char only_v4; char only_v6; char major_version; @@ -129,6 +131,8 @@ struct state char *result; size_t reslen; size_t resmax; + + FILE *resp_file; /* Fuzzing */ }; #define BUF_CHUNK 4096 @@ -152,6 +156,8 @@ 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 timeout_callback(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s); static void buf_init(struct buf *buf, struct bufferevent *bev) { @@ -253,10 +259,10 @@ static void buf_add_b64(struct buf *buf, void *data, size_t len) } } -static int buf_read(struct buf *buf) +static int buf_read(struct state *state, struct buf *buf) { int r; - size_t maxsize; + size_t len, maxsize; void *newbuf; if (buf->size >= buf->maxsize) @@ -264,9 +270,14 @@ static int buf_read(struct buf *buf) 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; + len= buf->size-buf->offset; + if (len != 0) + { + memmove(buf->buf, &buf->buf[buf->offset], + len); + } + buf->size -= buf->offset; + buf->offset= 0; } else { @@ -297,10 +308,27 @@ static int buf_read(struct buf *buf) } } - r= bufferevent_read(buf->bev, - buf->buf+buf->size, buf->maxsize-buf->size); + if (state->response_in) + { + r= fread(buf->buf+buf->size, 1, 1, state->resp_file); + if (r == -1 || r == 0) + { + timeout_callback(0, 0, &state->tu_env); + return -1; + } + } + else + { + r= bufferevent_read(buf->bev, + buf->buf+buf->size, buf->maxsize-buf->size); + } if (r > 0) { + if (state->response_out) + { + fwrite(buf->buf+buf->size, r, 1, + state->resp_file); + } buf->size += r; return 0; } @@ -356,7 +384,7 @@ 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 *typep, +static int msgbuf_read(struct state *state, struct msgbuf *msgbuf, int *typep, char *majorp, char *minorp) { int r; @@ -367,12 +395,11 @@ static int msgbuf_read(struct msgbuf *msgbuf, int *typep, { if (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5) { - r= buf_read(msgbuf->inbuf); + r= buf_read(state, msgbuf->inbuf); if (r < 0) { fprintf(stderr, - "msgbuf_read: buf_read failed: %s\n", - strerror(errno)); + "msgbuf_read: buf_read failed\n"); return -1; } continue; @@ -384,7 +411,7 @@ static int msgbuf_read(struct msgbuf *msgbuf, int *typep, len= (p[3] << 8) + p[4]; if (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5 + len) { - r= buf_read(msgbuf->inbuf); + r= buf_read(state, msgbuf->inbuf); if (r < 0) { if (errno != EAGAIN) @@ -608,6 +635,7 @@ static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], size_t newsiz; char *hostname, *str_port, *infname, *version_str; char *output_file, *A_arg; + char *response_in, *response_out; struct state *state; FILE *fh; @@ -617,6 +645,8 @@ static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], A_arg= NULL; infname= NULL; str_port= NULL; + response_in= NULL; + response_out= NULL; only_v4= 0; only_v6= 0; @@ -630,7 +660,7 @@ static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], /* Allow us to be called directly by another program in busybox */ optind= 0; - while (c= getopt_long(argc, argv, "A:O:V:i:p:46", longopts, NULL), c != -1) + while (c= getopt_long(argc, argv, "A:O:R:V:W:i:p:46", longopts, NULL), c != -1) { switch(c) { @@ -640,9 +670,15 @@ static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], case 'O': output_file= optarg; break; + case 'R': + response_in= optarg; + break; case 'V': version_str= optarg; break; + case 'W': + response_out= optarg; + break; case 'i': infname= optarg; break; @@ -670,6 +706,23 @@ static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], } hostname= argv[optind]; + if (response_in) + { + if (!validate_filename(response_in, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_in); + return NULL; + } + } + if (response_out) + { + if (!validate_filename(response_out, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_out); + return NULL; + } + } + if (output_file) { if (!validate_filename(output_file, SAFE_PREFIX_OUT)) @@ -726,6 +779,8 @@ static void *sslgetcert_init(int __attribute((unused)) argc, char *argv[], state->base= hg_base; state->atlas= A_arg ? strdup(A_arg) : NULL; state->output_file= output_file ? strdup(output_file) : NULL; + state->response_in= response_in ? strdup(response_in) : NULL; + state->response_out= response_out ? strdup(response_out) : NULL; state->infname= infname ? strdup(infname) : NULL; state->hostname= strdup(hostname); state->major_version= major; @@ -839,6 +894,12 @@ static void report(struct state *state) tu_cleanup(&state->tu_env); + if (state->resp_file) + { + fclose(state->resp_file); + state->resp_file= NULL; + } + state->busy= 0; if (state->base->done) state->base->done(state); @@ -1016,7 +1077,7 @@ static int eat_alert(struct state *state) { if (msgbuf->buffer.size - msgbuf->buffer.offset < 2) { - r= msgbuf_read(msgbuf, &type, + r= msgbuf_read(state, msgbuf, &type, &state->recv_major, &state->recv_minor); if (r < 0) { @@ -1074,7 +1135,7 @@ static int eat_server_hello(struct state *state) { if (msgbuf->buffer.size - msgbuf->buffer.offset < 4) { - r= msgbuf_read(msgbuf, &type, + r= msgbuf_read(state, msgbuf, &type, &state->recv_major, &state->recv_minor); if (r < 0) { @@ -1095,6 +1156,8 @@ static int eat_server_hello(struct state *state) fprintf(stderr, "eat_server_hello: got bad type %d from msgbuf_read\n", type); + add_str(state, DBQ(err) ":" DBQ(bad type)); + report(state); return -1; } continue; @@ -1104,12 +1167,14 @@ static int eat_server_hello(struct state *state) { fprintf(stderr, "eat_server_hello: got type %d\n", p[0]); + add_str(state, DBQ(err) ":" DBQ(bad type)); + report(state); return -1; } len= (p[1] << 16) + (p[2] << 8) + p[3]; if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len) { - r= msgbuf_read(msgbuf, &type, + r= msgbuf_read(state, msgbuf, &type, &state->recv_major, &state->recv_minor); if (r < 0) { @@ -1122,6 +1187,8 @@ static int eat_server_hello(struct state *state) fprintf(stderr, "eat_server_hello: got bad type %d from msgbuf_read\n", type); + add_str(state, DBQ(err) ":" DBQ(bad type)); + report(state); return -1; } continue; @@ -1147,7 +1214,7 @@ static int eat_certificate(struct state *state) { if (msgbuf->buffer.size - msgbuf->buffer.offset < 4) { - r= msgbuf_read(msgbuf, &type, + r= msgbuf_read(state, msgbuf, &type, &state->recv_major, &state->recv_minor); if (r < 0) { @@ -1164,6 +1231,8 @@ static int eat_certificate(struct state *state) fprintf(stderr, "eat_certificate: got bad type %d from msgbuf_read\n", type); + add_str(state, DBQ(err) ":" DBQ(bad type)); + report(state); return -1; } continue; @@ -1172,12 +1241,14 @@ static int eat_certificate(struct state *state) if (p[0] != HS_CERTIFICATE) { fprintf(stderr, "eat_certificate: got type %d\n", p[0]); + add_str(state, DBQ(err) ":" DBQ(bad type)); + report(state); return -1; } len= (p[1] << 16) + (p[2] << 8) + p[3]; if (msgbuf->buffer.size - msgbuf->buffer.offset < 4+len) { - r= msgbuf_read(msgbuf, &type, + r= msgbuf_read(state, msgbuf, &type, &state->recv_major, &state->recv_minor); if (r < 0) { @@ -1190,6 +1261,8 @@ static int eat_certificate(struct state *state) fprintf(stderr, "eat_certificate: got bad type %d from msgbuf_read\n", type); + add_str(state, DBQ(err) ":" DBQ(bad type)); + report(state); return -1; } continue; @@ -1209,6 +1282,17 @@ static int eat_certificate(struct state *state) while (o < 3+n) { slen= (p[o] << 16) + (p[o+1] << 8) + p[o+2]; + if (o+3+slen > len) + { + fprintf(stderr, + "eat_certificate: got bad len %d\n", + slen); + msgbuf->buffer.offset= + msgbuf->buffer.size; + add_str(state, DBQ(err) ":" DBQ(bad len)); + report(state); + return -1; + } buf_add_b64(&tmpbuf, p+o+3, slen); fprintf(fh, "%s\"-----BEGIN CERTIFICATE-----\\n", !first ? ", " : ""); @@ -1239,12 +1323,18 @@ static int eat_certificate(struct state *state) { fprintf(stderr, "do_certificate: bad amount of cert data\n"); + add_str(state, DBQ(err) ":" + DBQ(bad amount of cert data)); + report(state); return -1; } if (o != len) { fprintf(stderr, "do_certificate: bad amount of cert data\n"); + add_str(state, DBQ(err) ":" + DBQ(bad amount of cert data)); + report(state); return -1; } msgbuf->buffer.offset += 4+len; @@ -1295,7 +1385,8 @@ static void writecb(struct bufferevent *bev, void *ptr) msgbuf_final(&msgoutbuf, MSG_HANDSHAKE); /* Ignore error */ - (void) buf_write(&outbuf); + if (!state->response_in) + (void) buf_write(&outbuf); hsbuf_cleanup(&hsbuf); msgbuf_cleanup(&msgoutbuf); @@ -1434,8 +1525,11 @@ static void connected(struct tu_env *env, struct bufferevent *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); + if (!state->response_in) + { + getsockname(bufferevent_getfd(bev), + &state->loc_sin6, &state->loc_socklen); + } } static void sslgetcert_start(void *vstate) @@ -1459,6 +1553,17 @@ static void sslgetcert_start(void *vstate) state->writestate= WRITE_HELLO; state->gstart= time(NULL); + if (state->response_out) + { + state->resp_file= fopen(state->response_out, "w"); + if (!state->resp_file) + { + crondlog(DIE9 "unable to write to '%s'", + state->response_out); + } + } + + memset(&hints, '\0', sizeof(hints)); hints.ai_socktype= SOCK_STREAM; if (state->only_v4) @@ -1468,11 +1573,28 @@ static void sslgetcert_start(void *vstate) interval.tv_sec= CONN_TO; interval.tv_usec= 0; - tu_connect_to_name(&state->tu_env, state->hostname, - state->portname, - &interval, &hints, state->infname, timeout_callback, - reporterr, dnscount, beforeconnect, - connected, readcb, writecb); + if (state->response_in) + { + state->resp_file= fopen(state->response_in, "r"); + if (!state->resp_file) + { + crondlog(DIE9 "unable to read from '%s'", + state->response_in); + } + connected(&state->tu_env, NULL); + writecb(NULL, &state->tu_env); + while(state->resp_file != NULL) + readcb(NULL, &state->tu_env); + report(state); + } + else + { + tu_connect_to_name(&state->tu_env, state->hostname, + state->portname, + &interval, &hints, state->infname, timeout_callback, + reporterr, dnscount, beforeconnect, + connected, readcb, writecb); + } } static int sslgetcert_delete(void *vstate) diff --git a/eperd/traceroute.c b/eperd/traceroute.c index 4875721..c446d8e 100644 --- a/eperd/traceroute.c +++ b/eperd/traceroute.c @@ -28,7 +28,7 @@ #define uh_sum check #endif -#define TRACEROUTE_OPT_STRING ("!46IUFrTa:b:c:f:g:i:m:p:w:z:A:O:S:H:D:") +#define TRACEROUTE_OPT_STRING ("!46IUFrTa:b:c:f:g:i:m:p:w:z:A:O:S:H:D:R:W:") #define OPT_4 (1 << 0) #define OPT_6 (1 << 1) @@ -103,6 +103,9 @@ struct trtstate unsigned duptimeout; unsigned timeout; + char *response_in; /* Fuzzing */ + char *response_out; + /* Base and index in table */ struct trtbase *base; int index; @@ -157,6 +160,8 @@ struct trtstate size_t reslen; size_t resmax; char open_result; + + FILE *resp_file_out; /* Fuzzing */ }; static struct trtbase *trt_base; @@ -189,6 +194,12 @@ struct v6info }; static int create_socket(struct trtstate *state, int do_tcp); +static void ready_callback4(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s); +static void ready_tcp4(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s); +static void ready_callback6(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s); #define OPT_PAD1 0 #define OPT_PADN 1 @@ -528,7 +539,12 @@ static void send_pkt(struct trtstate *state) if (state->do_tcp) { - sock= socket(AF_INET6, SOCK_RAW, IPPROTO_TCP); + if (state->response_in) + sock= open("/dev/null", O_RDWR); + else + { + sock= socket(AF_INET6, SOCK_RAW, IPPROTO_TCP); + } if (sock == -1) { crondlog(DIE9 "socket failed"); @@ -552,9 +568,14 @@ static void send_pkt(struct trtstate *state) #endif /* Bind to source addr/port */ - r= bind(sock, - (struct sockaddr *)&state->loc_sin6, - state->loc_socklen); + if (state->response_in) + r= 0; /* No need to bind */ + else + { + r= bind(sock, + (struct sockaddr *)&state->loc_sin6, + state->loc_socklen); + } if (r == -1) { serrno= errno; @@ -618,9 +639,14 @@ static void send_pkt(struct trtstate *state) sin6copy= state->sin6; sin6copy.sin6_port= 0; - r= sendto(sock, base->packet, len, 0, - (struct sockaddr *)&sin6copy, - state->socklen); + if (state->response_in) + r= 0; /* No need to send anything */ + else + { + r= sendto(sock, base->packet, len, 0, + (struct sockaddr *)&sin6copy, + state->socklen); + } #if 0 { static int doit=1; if (doit && r != -1) @@ -725,9 +751,15 @@ static void send_pkt(struct trtstate *state) memset(&sin6copy, '\0', sizeof(sin6copy)); sin6copy.sin6_family= AF_INET6; sin6copy.sin6_addr= state->sin6.sin6_addr; - r= sendto(state->socket_icmp, base->packet, len, 0, - (struct sockaddr *)&sin6copy, - sizeof(sin6copy)); + + if (state->response_in) + r= 0; + else + { + r= sendto(state->socket_icmp, base->packet, + len, 0, (struct sockaddr *)&sin6copy, + sizeof(sin6copy)); + } #if 0 { static int doit=1; if (doit && r != -1) @@ -752,7 +784,12 @@ static void send_pkt(struct trtstate *state) } else if (state->do_udp) { - sock= socket(AF_INET6, SOCK_DGRAM, 0); + if (state->response_in) + sock= open("/dev/null", O_RDWR); + else + { + sock= socket(AF_INET6, SOCK_DGRAM, 0); + } if (sock == -1) { crondlog(DIE9 "socket failed"); @@ -774,9 +811,14 @@ static void send_pkt(struct trtstate *state) } /* Bind to source addr/port */ - r= bind(sock, - (struct sockaddr *)&state->loc_sin6, - state->loc_socklen); + if (state->response_in) + r= 0; /* No need to bind */ + else + { + r= bind(sock, + (struct sockaddr *)&state->loc_sin6, + state->loc_socklen); + } if (r == -1) { serrno= errno; @@ -840,9 +882,14 @@ static void send_pkt(struct trtstate *state) len= state->curpacksize; } - r= sendto(sock, base->packet, len, 0, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* No need to bind */ + else + { + r= sendto(sock, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { static int doit=1; if (doit && r != -1) @@ -878,7 +925,12 @@ static void send_pkt(struct trtstate *state) if (state->do_tcp) { - sock= socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (state->response_in) + sock= open("/dev/null", O_RDWR); + else + { + sock= socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + } if (sock == -1) { crondlog(DIE9 "socket failed"); @@ -889,9 +941,14 @@ static void send_pkt(struct trtstate *state) sizeof(on)); /* Bind to source addr/port */ - r= bind(sock, - (struct sockaddr *)&state->loc_sin6, - state->loc_socklen); + if (state->response_in) + r= 0; + else + { + r= bind(sock, + (struct sockaddr *)&state->loc_sin6, + state->loc_socklen); + } #if 0 { static int doit=1; if (doit && r != -1) { errno= ENOSYS; r= -1; } doit= !doit; } @@ -986,9 +1043,14 @@ static void send_pkt(struct trtstate *state) setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &on, sizeof(on)); - r= sendto(sock, base->packet, len, 0, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* No need to send */ + else + { + r= sendto(sock, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { static int doit=0; if (doit && r != -1) @@ -1071,9 +1133,14 @@ static void send_pkt(struct trtstate *state) setsockopt(state->socket_icmp, IPPROTO_IP, IP_MTU_DISCOVER, &on, sizeof(on)); - r= sendto(state->socket_icmp, base->packet, len, 0, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* No need to send packet */ + else + { + r= sendto(state->socket_icmp, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { static int doit=1; if (doit && r != -1) @@ -1254,6 +1321,13 @@ static void send_pkt(struct trtstate *state) interval.tv_usec= state->timeout % 1000000; evtimer_add(&state->timer, &interval); + if (state->response_in) + { + if (state->sin6.sin6_family == AF_INET6) + ready_callback6(0, 0, state); + else + ready_callback4(0, 0, state); + } } static void do_mpls(struct trtstate *state, unsigned char *packet, @@ -1373,8 +1447,75 @@ static void ready_callback4(int __attribute((unused)) unused, base= state->base; slen= sizeof(remote); - nrecv= recvfrom(state->socket_icmp, base->packet, sizeof(base->packet), - MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + if (state->response_in) + { + uint8_t proto; + uint32_t len; + + if (read(state->socket_icmp, &proto, sizeof(proto)) != sizeof(proto)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (proto == 0) + { + printf("ready_callback4: proto 0 -> timeout\n"); + return; /* Timeout */ + } + if (proto == 6) + { + printf("ready_callback4: proto 6 -> TCP\n"); + ready_tcp4(0, 0, s); + return; + } + if (proto != 1) + { + printf("ready_callback4: proto != 1\n"); + return; + } + + if (read(state->socket_icmp, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_callback4: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_callback4: bad value for len: %u", + len); + } + if (read(state->socket_icmp, base->packet, len) != len) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + if (read(state->socket_icmp, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_callback4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback4: error reading from '%s'", + state->response_in); + } + nrecv= len; + } + else + { + nrecv= recvfrom(state->socket_icmp, base->packet, sizeof(base->packet), + MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + } if (nrecv == -1) { /* Strange, read error */ @@ -1383,6 +1524,17 @@ static void ready_callback4(int __attribute((unused)) unused, } // printf("ready_callback4: got packet\n"); + if (state->resp_file_out) + { + uint8_t proto= 1; + uint32_t len= nrecv; + + fwrite(&proto, sizeof(proto), 1, state->resp_file_out); + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + ip= (struct ip *)base->packet; hlen= ip->ip_hl*4; @@ -1541,6 +1693,8 @@ static void ready_callback4(int __attribute((unused)) unused, { printf("ready_callback4: changed source %s\n", inet_ntoa(eip->ip_src)); + printf("ready_callback4: expected %s\n", + inet_ntoa(((struct sockaddr_in *)&state->loc_sin6)->sin_addr)); } if (memcmp(&eip->ip_dst, &((struct sockaddr_in *)&state->sin6)-> @@ -1607,7 +1761,6 @@ static void ready_callback4(int __attribute((unused)) unused, nextmtu; } } -printf("curpacksize: %d\n", state->curpacksize); if (!late) state->not_done= 1; break; @@ -1820,7 +1973,6 @@ printf("curpacksize: %d\n", state->curpacksize); nextmtu; } } -printf("curpacksize: %d\n", state->curpacksize); if (!late) state->not_done= 1; break; @@ -1903,7 +2055,6 @@ printf("curpacksize: %d\n", state->curpacksize); } if (!state->busy) { -printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); printf( "ready_callback4: index (%d) is not busy\n", ind); @@ -1983,6 +2134,8 @@ printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family { printf("ready_callback4: changed source %s\n", inet_ntoa(eip->ip_src)); + printf("ready_callback4: expected %s\n", + inet_ntoa(((struct sockaddr_in *)&state->loc_sin6)->sin_addr)); } if (memcmp(&eip->ip_dst, &((struct sockaddr_in *)&state->sin6)-> @@ -2179,7 +2332,6 @@ printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family if (!state->busy) { -printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); printf( "ready_callback4: index (%d) is not busy\n", ind); @@ -2344,8 +2496,51 @@ static void ready_tcp4(int __attribute((unused)) unused, base= state->base; slen= sizeof(remote); - nrecv= recvfrom(state->socket_tcp, base->packet, sizeof(base->packet), - MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + + if (state->response_in) + { + uint32_t len; + + /* Proto is eaten by ready_callback4 */ + if (read(state->socket_icmp, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_tcp4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_tcp4: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_tcp4: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_tcp4: bad value for len: %u", + len); + } + if (read(state->socket_icmp, base->packet, len) != len) + { + //printf("ready_tcp4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_tcp4: error reading from '%s'", + state->response_in); + } + if (read(state->socket_icmp, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_tcp4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_tcp4: error reading from '%s'", + state->response_in); + } + nrecv= len; + } + else + { + nrecv= recvfrom(state->socket_tcp, base->packet, sizeof(base->packet), + MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + } if (nrecv == -1) { /* Strange, read error */ @@ -2353,6 +2548,17 @@ static void ready_tcp4(int __attribute((unused)) unused, return; } + if (state->resp_file_out) + { + uint8_t proto= 6; + uint32_t len= nrecv; + + fwrite(&proto, sizeof(proto), 1, state->resp_file_out); + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + ip= (struct ip *)base->packet; hlen= ip->ip_hl*4; @@ -2399,7 +2605,6 @@ static void ready_tcp4(int __attribute((unused)) unused, if (!state->busy) { -printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); printf( "ready_callback4: index (%d) is not busy\n", ind); @@ -2416,7 +2621,6 @@ printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family seq= ntohl(tcphdr->ack_seq) & 0xffff; if (seq-state->seq > 0x2000) { -printf("got seq %d, expected %d\n", seq, state->seq); if (seq > state->seq) { #if 0 @@ -2537,7 +2741,47 @@ static void ready_tcp6(int __attribute((unused)) unused, msg.msg_controllen= sizeof(cmsgbuf); msg.msg_flags= 0; /* Not really needed */ - nrecv= recvmsg(state->socket_tcp, &msg, MSG_DONTWAIT); + if (state->response_in) + { + uint32_t len; + + /* Proto is eaten by ready_callback4 */ + if (read(state->socket_icmp, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_tcp4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_tcp4: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_tcp4: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_tcp4: bad value for len: %u", + len); + } + if (read(state->socket_icmp, base->packet, len) != len) + { + //printf("ready_tcp4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_tcp4: error reading from '%s'", + state->response_in); + } + if (read(state->socket_icmp, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_tcp4: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_tcp4: error reading from '%s'", + state->response_in); + } + nrecv= len; + } + else + nrecv= recvmsg(state->socket_tcp, &msg, MSG_DONTWAIT); if (nrecv == -1) { /* Strange, read error */ @@ -2545,6 +2789,17 @@ static void ready_tcp6(int __attribute((unused)) unused, return; } + if (state->resp_file_out) + { + uint8_t proto= 6; + uint32_t len= nrecv; + + fwrite(&proto, sizeof(proto), 1, state->resp_file_out); + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + rcvdttl= -42; /* To spot problems */ memset(&dstaddr, '\0', sizeof(dstaddr)); for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr; @@ -2601,7 +2856,6 @@ static void ready_tcp6(int __attribute((unused)) unused, if (!state->busy) { -printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); printf("ready_tcp6: index (%d) is not busy\n", ind); return; } @@ -2616,7 +2870,6 @@ printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family seq= ntohl(tcphdr->ack_seq) & 0xffff; if (seq-state->seq > 0x2000) { -printf("got seq %d, expected %d\n", seq, state->seq); if (seq > state->seq) { #if 0 @@ -2717,7 +2970,7 @@ static void ready_callback6(int __attribute((unused)) unused, struct v6info *v6info; struct cmsghdr *cmsgptr; void *ptr; - double ms; + double ms= -42; /* lint, to spot problems */ struct timespec now; struct sockaddr_in6 remote; struct in6_addr dstaddr; @@ -2743,7 +2996,76 @@ static void ready_callback6(int __attribute((unused)) unused, msg.msg_controllen= sizeof(cmsgbuf); msg.msg_flags= 0; /* Not really needed */ - nrecv= recvmsg(state->socket_icmp, &msg, MSG_DONTWAIT); + /* Receive data from the network */ + if (state->response_in) + { + uint8_t proto; + uint32_t len; + + if (read(state->socket_icmp, &proto, sizeof(proto)) != sizeof(proto)) + { + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + if (proto == 0) + { + printf("ready_callback6: proto 0 -> timeout\n"); + return; /* Timeout */ + } + if (proto == 6) + { + printf("ready_callback6: proto 6 -> TCP\n"); + ready_tcp6(0, 0, s); + return; + } + if (proto != 1) + { + printf("ready_callback6: proto != 1\n"); + return; + } + + if (read(state->socket_icmp, &len, sizeof(len)) != sizeof(len)) + { + //printf("ready_callback6: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + if (len > sizeof(base->packet)) + { + //printf("ready_callback6: bad value for len: %u\n", len); + //abort(); + crondlog(DIE9 "ready_callback6: bad value for len: %u", + len); + } + if (read(state->socket_icmp, base->packet, len) != len) + { + //printf("ready_callback6: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + if (read(state->socket_icmp, &remote, sizeof(remote)) != + sizeof(remote)) + { + //printf("ready_callback6: error reading from '%s'\n", + // state->response_in); + //abort(); + crondlog(DIE9 "ready_callback6: error reading from '%s'", + state->response_in); + } + + nrecv= len; + + /* Do not try to fuzz the cmsgbuf. We assume stuff returned by + * the kernel can be trusted. + */ + memset(cmsgbuf, '\0', sizeof(cmsgbuf)); + } + else + nrecv= recvmsg(state->socket_icmp, &msg, MSG_DONTWAIT); if (nrecv == -1) { /* Strange, read error */ @@ -2751,6 +3073,17 @@ static void ready_callback6(int __attribute((unused)) unused, return; } + if (state->response_out) + { + uint8_t proto= 1; + uint32_t len= nrecv; + + fwrite(&proto, sizeof(proto), 1, state->resp_file_out); + fwrite(&len, sizeof(len), 1, state->resp_file_out); + fwrite(base->packet, len, 1, state->resp_file_out); + fwrite(&remote, sizeof(remote), 1, state->resp_file_out); + } + rcvdttl= -42; /* To spot problems */ memset(&dstaddr, '\0', sizeof(dstaddr)); for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr; @@ -3003,7 +3336,6 @@ static void ready_callback6(int __attribute((unused)) unused, if (!state->busy) { -printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); printf( "ready_callback6: index (%d) is not busy\n", ind); @@ -3316,7 +3648,6 @@ printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family if (!state->busy) { -printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); printf( "ready_callback6: index (%d) is not busy\n", ind); @@ -3452,8 +3783,7 @@ static void noreply_callback(int __attribute((unused)) unused, state= s; #if 0 - printf("noreply_callback: gotresp = %d\n", - state->gotresp); + printf("noreply_callback: gotresp = %d\n", state->gotresp); #endif if (!state->gotresp) @@ -3462,6 +3792,14 @@ static void noreply_callback(int __attribute((unused)) unused, add_str(state, " }, { "); add_str(state, DBQ(x) ":" DBQ(*)); state->open_result= 1; + + if (state->resp_file_out) + { + uint32_t len= 0; + + /* Use a zero length packet to signal a timeout */ + fwrite(&len, sizeof(len), 1, state->resp_file_out); + } } send_pkt(state); @@ -3481,6 +3819,7 @@ static void *traceroute_init(int __attribute((unused)) argc, char *argv[], const char *hostname; char *out_filename; const char *destportstr; + char *response_in, *response_out; char *interface; char *check; struct trtstate *state; @@ -3511,6 +3850,8 @@ static void *traceroute_init(int __attribute((unused)) argc, char *argv[], destoptsize= 0; str_Atlas= NULL; out_filename= NULL; + response_in= NULL; + response_out= NULL; opt_complementary = "=1:4--6:i--u:a+:b+:c+:f+:g+:m+:w+:z+:S+:H+:D+"; for (i= 0; argv[i] != NULL; i++) @@ -3521,7 +3862,7 @@ for (i= 0; argv[i] != NULL; i++) &firsthop, &gaplimit, &interface, &maxhops, &destportstr, &timeout, &duptimeout, &str_Atlas, &out_filename, &maxpacksize, - &hbhoptsize, &destoptsize); + &hbhoptsize, &destoptsize, &response_in, &response_out); hostname = argv[optind]; if (opt == 0xffffffff) @@ -3534,11 +3875,31 @@ for (i= 0; argv[i] != NULL; i++) do_v6= !!(opt & OPT_6); dont_fragment= !!(opt & OPT_F); delay_name_res= !!(opt & OPT_r); + delay_name_res= 1; /* Always enabled, leave the old code in + * place for now. + */ do_tcp= !!(opt & OPT_T); do_udp= !(do_icmp || do_tcp); if (maxpacksize > sizeof(trt_base->packet)) maxpacksize= sizeof(trt_base->packet); + if (response_in) + { + if (!validate_filename(response_in, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_in); + return NULL; + } + } + if (response_out) + { + if (!validate_filename(response_out, ATLAS_FUZZING)) + { + crondlog(LVL8 "insecure fuzzing file '%s'", response_out); + return NULL; + } + } + if (out_filename) { if (!validate_filename(out_filename, SAFE_PREFIX)) @@ -3618,6 +3979,8 @@ for (i= 0; argv[i] != NULL; i++) state->hbhoptsize= hbhoptsize; state->destoptsize= destoptsize; state->out_filename= out_filename ? strdup(out_filename) : NULL; + state->response_in= response_in; + state->response_out= response_out; state->base= trt_base; state->paris= 0; state->busy= 0; @@ -3627,6 +3990,9 @@ for (i= 0; argv[i] != NULL; i++) state->socket_icmp= -1; state->socket_tcp= -1; + if (response_in || response_out) + trt_base->my_pid= 42; + for (i= 0; itabsiz; i++) { if (trt_base->table[i] == NULL) @@ -3772,7 +4138,17 @@ static int create_socket(struct trtstate *state, int do_tcp) type= SOCK_RAW; protocol= (af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP); - state->socket_icmp= xsocket(af, type, protocol); + if (state->response_in) + { + state->socket_icmp= open(state->response_in, O_RDONLY); + if (state->socket_icmp == -1) + { + crondlog(DIE9 "unable to open '%s'", + state->response_in); + } + } + else + state->socket_icmp= xsocket(af, type, protocol); if (state->socket_icmp == -1) { serrno= errno; @@ -3798,9 +4174,14 @@ static int create_socket(struct trtstate *state, int do_tcp) } } - r= connect(state->socket_icmp, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* Don't try to connect when reading from a file */ + else + { + r= connect(state->socket_icmp, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { errno= ENOSYS; r= -1; } #endif @@ -3816,7 +4197,7 @@ static int create_socket(struct trtstate *state, int do_tcp) return -1; } state->loc_socklen= sizeof(state->loc_sin6); - if (getsockname(state->socket_icmp, + if (!state->response_in && getsockname(state->socket_icmp, &state->loc_sin6, &state->loc_socklen) == -1) { @@ -3829,9 +4210,12 @@ static int create_socket(struct trtstate *state, int do_tcp) buf, sizeof(buf))); #endif - close(state->socket_icmp); - state->socket_icmp= xsocket(af, type, - protocol); + if (!state->response_in) + { + close(state->socket_icmp); + state->socket_icmp= xsocket(af, type, + protocol); + } if (state->socket_icmp == -1) { serrno= errno; @@ -3877,8 +4261,10 @@ static int create_socket(struct trtstate *state, int do_tcp) if (do_tcp) { - state->socket_tcp= xsocket(af, SOCK_RAW, - IPPROTO_TCP); + if (state->response_in) + state->socket_tcp= open("/dev/null", O_RDWR); + else + state->socket_tcp= xsocket(af, SOCK_RAW, IPPROTO_TCP); if (state->socket_tcp == -1) { serrno= errno; @@ -3911,9 +4297,14 @@ static int create_socket(struct trtstate *state, int do_tcp) } } - r= connect(state->socket_tcp, - (struct sockaddr *)&state->sin6, - state->socklen); + if (state->response_in) + r= 0; /* No need to connect */ + else + { + r= connect(state->socket_tcp, + (struct sockaddr *)&state->sin6, + state->socklen); + } #if 0 { errno= ENOSYS; r= -1; } #endif @@ -4038,6 +4429,17 @@ static void traceroute_start(void *state) trtstate= state; + if (trtstate->response_out) + { + trtstate->resp_file_out= fopen(trtstate->response_out, "w"); + if (!trtstate->resp_file_out) + { + crondlog(DIE9 "unable to write to '%s'", + trtstate->response_out); + } + } + + if (!trtstate->delay_name_res) { traceroute_start2(state); -- cgit v1.2.3