From 73e699faf130d0fc0f2f076d95db9dbd7f42a8b6 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 15 May 2015 10:25:25 +0200 Subject: ripe-atlas-fw: imported version 4660 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- eperd/ntp.c | 1955 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1955 insertions(+) create mode 100644 eperd/ntp.c (limited to 'eperd/ntp.c') diff --git a/eperd/ntp.c b/eperd/ntp.c new file mode 100644 index 0000000..251510d --- /dev/null +++ b/eperd/ntp.c @@ -0,0 +1,1955 @@ +/* + * Copyright (c) 2013 RIPE NCC + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * traceroute.c + */ + +#include "libbb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "eperd.h" + +#define SAFE_PREFIX ATLAS_DATA_NEW + +#define DBQ(str) "\"" #str "\"" + +#ifndef STANDALONE_BUSYBOX +#define uh_sport source +#define uh_dport dest +#define uh_ulen len +#define uh_sum check +#endif + +#define NTP_PORT 123 + +#define NTP_OPT_STRING ("!46c:i:w:A:O:") + +#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 + +#define SRC_BASE_PORT (20480) +#define MAX_DATA_SIZE (4096) + +#define DBQ(str) "\"" #str "\"" + +struct ntp_ts +{ + uint32_t ntp_seconds; + uint32_t ntp_fraction; +}; + +struct ntpbase +{ + struct event_base *event_base; + + int my_pid; + + struct ntpstate **table; + int tabsiz; + + /* For standalone traceroute. Called when a traceroute instance is + * done. Just one pointer for all instances. It is up to the caller + * to keep it consistent. + */ + void (*done)(void *state); + + u_char packet[MAX_DATA_SIZE]; +}; + +struct ntpstate +{ + /* Parameters */ + char *atlas; + char *hostname; + char *destportstr; + char *out_filename; + char *interface; + char do_v6; + char count; + unsigned timeout; + + /* Base and index in table */ + struct ntpbase *base; + int index; + + struct sockaddr_in6 sin6; + socklen_t socklen; + struct sockaddr_in6 loc_sin6; + socklen_t loc_socklen; + + int sent; + uint16_t seq; + int socket; /* Socket for sending and receiving */ + struct event event_socket; /* Event for this socket */ + unsigned first:1; /* Waiting for first response */ + unsigned done:1; /* We got something from the target + * host or a destination unreachable. + */ + unsigned not_done:1; /* Not got something else */ + unsigned busy:1; /* Busy, do not start another one */ + unsigned gotresp:1; /* Got a response to the last packet + * we sent. For dup detection. + */ + unsigned dnsip:1; /* Busy with dns name resolution */ + struct evutil_addrinfo *dns_res; + struct evutil_addrinfo *dns_curr; + + time_t starttime; + struct timeval xmit_time; + + uint8_t ntp_flags; + uint8_t ntp_stratum; + int8_t ntp_poll; + int8_t ntp_precision; + uint32_t ntp_root_delay; + uint32_t ntp_root_dispersion; + uint32_t ntp_reference_id; + struct ntp_ts ntp_reference_ts; + + struct event timer; + + unsigned long min; + unsigned long max; + unsigned long sum; + int sentpkts; + int rcvdpkts; + int duppkts; + + char *result; + size_t reslen; + size_t resmax; + char open_result; +}; + +static struct ntpbase *ntp_base; + +struct ntphdr +{ + uint8_t ntp_flags; + uint8_t ntp_stratum; + int8_t ntp_poll; + int8_t ntp_precision; + uint32_t ntp_root_delay; + uint32_t ntp_root_dispersion; + uint32_t ntp_reference_id; + struct ntp_ts ntp_reference_ts; + struct ntp_ts ntp_origin_ts; + struct ntp_ts ntp_receive_ts; + struct ntp_ts ntp_transmit_ts; +}; + +#define NTP_LI_MASK 0xC0 +#define NTP_LI_SHIFT 6 +#define LI_NO_WARNING 0 +#define LI_61 1 +#define LI_59 2 +#define LI_UNKNOWN 3 +#define NTP_VERSION 4 +#define NTP_VERSION_MASK 0x38 +#define NTP_VERSION_SHIFT 3 +#define NTP_MODE_CLIENT 3 +#define NTP_MODE_MASK 0x7 +#define MODE_RESERVED 0 +#define MODE_SYM_ACT 1 +#define MODE_SYM_PASS 2 +#define MODE_CLIENT 3 +#define MODE_SERVER 4 +#define MODE_BROADCAST 5 +#define MODE_CONTROL 6 +#define MODE_PRIVATE 7 + +#define STRATUM_INVALID 0 +#define STRATUM_UNSYNCHRONIZED 16 + +#define NTP_1970 2208988800UL /* 1970 - 1900 in seconds */ + +#define NTP_4G 4294967296.0 + + +static int create_socket(struct ntpstate *state); + +static void add_str(struct ntpstate *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 format_li(char *line, size_t size, uint8_t flags) +{ + const char *str; + + switch((flags & NTP_LI_MASK) >> NTP_LI_SHIFT) + { + case LI_NO_WARNING: str= "no"; break; + case LI_61: str= "61"; break; + case LI_59: str= "59"; break; + case LI_UNKNOWN: str= "unknown"; break; + } + snprintf(line, size, DBQ(li) ": " DBQ(%s), str); +} + +static void format_mode(char *line, size_t size, uint8_t flags) +{ + const char *str; + + switch(flags & NTP_MODE_MASK) + { + case MODE_RESERVED: str= "reserved"; break; + case MODE_SYM_ACT: str= "sym. active"; break; + case MODE_SYM_PASS: str= "sym. passive"; break; + case MODE_CLIENT: str= "client"; break; + case MODE_SERVER: str= "server"; break; + case MODE_BROADCAST: str= "broadcast"; break; + case MODE_CONTROL: str= "control"; break; + case MODE_PRIVATE: str= "private"; break; + } + snprintf(line, size, DBQ(mode) ": " DBQ(%s), str); +} + +static void format_stratum(char *line, size_t size, uint8_t stratum) +{ + const char *str; + + str= NULL; + switch(stratum) + { + case STRATUM_INVALID: str= "invalid"; break; + case STRATUM_UNSYNCHRONIZED: str= "unsynchronized"; break; + } + if (str) + { + snprintf(line, size, DBQ(stratum) ": " DBQ(%s), + str); + } + else if (stratum < STRATUM_UNSYNCHRONIZED) + { + snprintf(line, size, DBQ(stratum) ": %d", + stratum); + } + else + { + snprintf(line, size, DBQ(stratum) ": " DBQ(reserved (%d)), + stratum); + } +} + +static void format_8bit(char *line, size_t size, const char *label, + int8_t value) +{ + if (value >= 0) + { + snprintf(line, size, DBQ(%s) ": %d", label, 1 << value); + } + else + { + snprintf(line, size, DBQ(%s) ": %g", label, + 1.0 / (1 << -value)); + } +} + +static void format_short_ts(char *line, size_t size, const char *label, + uint32_t value) +{ + snprintf(line, size, DBQ(%s) ": %g", label, value/65536.0); +} + +static void format_ref_id(char *line, size_t size, uint32_t value, + uint8_t stratum) +{ + if (stratum == 0 || stratum == 1) + { + snprintf(line, size, DBQ(ref-id) ": " DBQ(%.4s), + (char *)&value); + } + else + { + snprintf(line, size, DBQ(ref-id) ": " DBQ(%08x), + ntohl(value)); + } +} + +static void format_ts(char *line, size_t size, const char *label, + struct ntp_ts *ts) +{ + double d; + + d= ntohl(ts->ntp_seconds) + ntohl(ts->ntp_fraction)/NTP_4G; + snprintf(line, size, DBQ(%s) ": %.9f", label, d); +} + +static void report(struct ntpstate *state) +{ + FILE *fh; + const char *proto; + char namebuf[NI_MAXHOST]; + char line[80]; + + event_del(&state->timer); + + if (state->out_filename) + { + fh= fopen(state->out_filename, "a"); + if (!fh) + crondlog(DIE9 "unable to append to '%s'", + state->out_filename); + } + else + fh= stdout; + + fprintf(fh, "RESULT { "); + if (state->atlas) + { + fprintf(fh, DBQ(id) ":" DBQ(%s) + ", " DBQ(fw) ":%d" + ", " DBQ(lts) ":%d" + ", " DBQ(time) ":%ld, ", + state->atlas, get_atlas_fw_version(), + get_timesync(), + state->starttime); + } + + fprintf(fh, DBQ(dst_name) ":" DBQ(%s), + state->hostname); + + if (!state->dnsip) + { + getnameinfo((struct sockaddr *)&state->sin6, state->socklen, + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + + fprintf(fh, ", " DBQ(dst_addr) ":" DBQ(%s), namebuf); + + namebuf[0]= '\0'; + getnameinfo((struct sockaddr *)&state->loc_sin6, + state->loc_socklen, + namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST); + + fprintf(fh, ", " DBQ(src_addr) ":" DBQ(%s), namebuf); + } + + proto= "UDP"; + fprintf(fh, ", " DBQ(proto) ":" DBQ(%s) ", " DBQ(af) ": %d", + proto, + state->dnsip ? (state->do_v6 ? 6 : 4) : + (state->sin6.sin6_family == AF_INET6 ? 6 : 4)); + + if (!state->first) + { + format_li(line, sizeof(line), state->ntp_flags); + fprintf(fh, ", %s", line); + fprintf(fh, ", " DBQ(version) ": %d", + ((state->ntp_flags & NTP_VERSION_MASK) >> + NTP_VERSION_SHIFT)); + + format_mode(line, sizeof(line), state->ntp_flags); + fprintf(fh, ", %s", line); + + format_stratum(line, sizeof(line), state->ntp_stratum); + fprintf(fh, ", %s", line); + + format_8bit(line, sizeof(line), "poll", state->ntp_poll); + fprintf(fh, ", %s", line); + + format_8bit(line, sizeof(line), "precision", + state->ntp_precision); + fprintf(fh, ", %s", line); + + format_short_ts(line, sizeof(line), "root-delay", + ntohl(state->ntp_root_delay)); + fprintf(fh, ", %s", line); + + format_short_ts(line, sizeof(line), "root-dispersion", + ntohl(state->ntp_root_dispersion)); + fprintf(fh, ", %s", line); + + format_ref_id(line, sizeof(line), state->ntp_reference_id, + state->ntp_stratum); + fprintf(fh, ", %s", line); + + format_ts(line, sizeof(line), "ref-ts", + &state->ntp_reference_ts); + fprintf(fh, ", %s", line); + } + + fprintf(fh, ", " DBQ(result) ": [ %s ] }\n", state->result); + + free(state->result); + state->result= NULL; + + if (state->out_filename) + fclose(fh); + + /* Kill the event and close socket */ + if (state->socket != -1) + { + event_del(&state->event_socket); + close(state->socket); + state->socket= -1; + } + + state->busy= 0; + + if (state->base->done) + state->base->done(state); +} + +static void send_pkt(struct ntpstate *state) +{ + int r, len, serrno; + struct ntpbase *base; + struct ntphdr *ntphdr; + double d; + struct timeval interval; + char line[80]; + + state->gotresp= 0; + + base= state->base; + + if (state->sent >= state->count) + { + add_str(state, " }"); + + /* We are done */ + report(state); + return; + } + state->seq++; + + ntphdr= (struct ntphdr *)base->packet; + len= sizeof(*ntphdr); + + memset(ntphdr, '\0', len); + ntphdr->ntp_flags= (NTP_VERSION << NTP_VERSION_SHIFT) | NTP_MODE_CLIENT; + + gettimeofday(&state->xmit_time, NULL); + + ntphdr->ntp_transmit_ts.ntp_seconds= + htonl(state->xmit_time.tv_sec + NTP_1970); + d= state->xmit_time.tv_usec / 1e6; + d *= NTP_4G; + ntphdr->ntp_transmit_ts.ntp_fraction= htonl((uint32_t)d); + + if (state->sin6.sin6_family == AF_INET6) + { + /* Set port */ + state->sin6.sin6_port= htons(NTP_PORT); + + r= sendto(state->socket, base->packet, len, 0, + (struct sockaddr *)&state->sin6, + state->socklen); + +#if 0 + { static int doit=1; if (doit && r != -1) + { errno= ENOSYS; r= -1; } doit= !doit; } +#endif + serrno= errno; + + if (r == -1) + { + if (serrno != EACCES && + serrno != ECONNREFUSED && + serrno != EMSGSIZE) + { + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + return; + } + } + } + else + { +#if 0 + printf( +"send_pkt: sending IPv4 packet, do_icmp %d, parismod %d, index %d, state %p\n", + state->do_icmp, state->parismod, state->index, state); +#endif + + /* Set port */ + ((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 0 +{ static int doit=0; if (doit && r != -1) +{ errno= ENOSYS; r= -1; } doit= !doit; } +#endif + + serrno= errno; + if (r == -1) + { + if (serrno != EMSGSIZE) + { + serrno= errno; + + snprintf(line, sizeof(line), + "%s{ " DBQ(error) ":" DBQ(sendto failed: %s) " } ] }", + state->sent ? " }, " : "", + strerror(serrno)); + add_str(state, line); + report(state); + return; + } + } + } + + if (state->open_result) + add_str(state, " }, "); + add_str(state, "{ "); + state->open_result= 0; + + /* Increment packets sent */ + state->sent++; + + /* Set timer */ + interval.tv_sec= state->timeout/1000000; + interval.tv_usec= state->timeout % 1000000; + evtimer_add(&state->timer, &interval); + +} + +static void ready_callback(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + struct ntpbase *base; + struct ntpstate *state; + int head; + ssize_t nrecv; + socklen_t slen; + double d; + struct ntphdr *ntphdr; + struct timeval now; + struct ntp_ts final_ts; + struct sockaddr_in remote; + char line[80]; + + gettimeofday(&now, NULL); + + state= s; + base= state->base; + + slen= sizeof(remote); + nrecv= recvfrom(state->socket, base->packet, sizeof(base->packet), + MSG_DONTWAIT, (struct sockaddr *)&remote, &slen); + if (nrecv == -1) + { + /* Strange, read error */ + printf("ready_callback: read error '%s'\n", strerror(errno)); + return; + } + // printf("ready_callback: got packet\n"); + + if (nrecv < sizeof(*ntphdr)) + { + /* Short packet */ + printf("ready_callback: too short %d\n", (int)nrecv); + return; + } + + if (!state->busy) + { +printf("%s, %d: sin6_family = %d\n", __FILE__, __LINE__, state->sin6.sin6_family); + return; + } + + if (state->open_result) + add_str(state, " }, { "); + + head= 1; + + ntphdr= (struct ntphdr *)base->packet; + + if (state->first) + { + /* Copy mostly static fields */ + state->ntp_flags= ntphdr->ntp_flags; + state->ntp_stratum= ntphdr->ntp_stratum; + state->ntp_poll= ntphdr->ntp_poll; + state->ntp_precision= ntphdr->ntp_precision; + state->ntp_root_delay= ntphdr->ntp_root_delay; + state->ntp_root_dispersion= ntphdr->ntp_root_dispersion; + state->ntp_reference_id= ntphdr->ntp_reference_id; + state->ntp_reference_ts= ntphdr->ntp_reference_ts; + + state->first= 0; + } + else + { + if ((ntphdr->ntp_flags & NTP_LI_MASK) != + (state->ntp_flags & NTP_LI_MASK)) + { + format_li(line, sizeof(line), ntphdr->ntp_flags); + add_str(state, line); + head= 0; + } + + if ((ntphdr->ntp_flags & NTP_VERSION_MASK) != + (state->ntp_flags & NTP_VERSION_MASK)) + { + snprintf(line, sizeof(line), ", " DBQ(version) ": %d", + ((ntphdr->ntp_flags & NTP_VERSION_MASK) >> + NTP_VERSION_SHIFT)); + add_str(state, line); + head= 0; + } + + if ((ntphdr->ntp_flags & NTP_MODE_MASK) != + (state->ntp_flags & NTP_MODE_MASK)) + { + if (!head) + add_str(state, ", "); + format_mode(line, sizeof(line), ntphdr->ntp_flags); + add_str(state, line); + head= 0; + } + + if (ntphdr->ntp_stratum != state->ntp_stratum) + { + if (!head) + add_str(state, ", "); + format_stratum(line, sizeof(line), ntphdr->ntp_stratum); + add_str(state, line); + head= 0; + } + + if (ntphdr->ntp_poll != state->ntp_poll) + { + if (!head) + add_str(state, ", "); + format_8bit(line, sizeof(line), "poll", + ntphdr->ntp_poll); + add_str(state, line); + head= 0; + } + + if (ntphdr->ntp_precision != state->ntp_precision) + { + if (!head) + add_str(state, ", "); + format_8bit(line, sizeof(line), "precision", + ntphdr->ntp_precision); + add_str(state, line); + head= 0; + } + + if (ntphdr->ntp_root_delay != state->ntp_root_delay) + { + if (!head) + add_str(state, ", "); + format_short_ts(line, sizeof(line), "root-delay", + ntohl(ntphdr->ntp_root_delay)); + add_str(state, line); + head= 0; + } + + if (ntphdr->ntp_root_dispersion != state->ntp_root_dispersion) + { + if (!head) + add_str(state, ", "); + format_short_ts(line, sizeof(line), "root-dispersion", + ntohl(ntphdr->ntp_root_dispersion)); + add_str(state, line); + head= 0; + } + + if (ntphdr->ntp_reference_id != state->ntp_reference_id) + { + if (!head) + add_str(state, ", "); + format_ref_id(line, sizeof(line), + ntphdr->ntp_reference_id, ntphdr->ntp_stratum); + add_str(state, line); + head= 0; + } + + if (memcmp(&ntphdr->ntp_reference_ts, &state->ntp_reference_ts, + sizeof(ntphdr->ntp_reference_ts)) != 0) + { + if (!head) + add_str(state, ", "); + format_ts(line, sizeof(line), "ref-ts", + &ntphdr->ntp_reference_ts); + add_str(state, line); + head= 0; + } + } + + d= ntohl(ntphdr->ntp_origin_ts.ntp_seconds) + + ntohl(ntphdr->ntp_origin_ts.ntp_fraction)/NTP_4G; + snprintf(line, sizeof(line), "%s" DBQ(origin-ts) ": %.9f", + head ? "" : ", ", d); + head= 0; + add_str(state, line); + + d= ntohl(ntphdr->ntp_receive_ts.ntp_seconds) + + ntohl(ntphdr->ntp_receive_ts.ntp_fraction)/NTP_4G; + snprintf(line, sizeof(line), ", " DBQ(receive-ts) ": %.9f", d); + add_str(state, line); + + d= ntohl(ntphdr->ntp_transmit_ts.ntp_seconds) + + ntohl(ntphdr->ntp_transmit_ts.ntp_fraction)/NTP_4G; + snprintf(line, sizeof(line), ", " DBQ(transmit-ts) ": %.9f", d); + add_str(state, line); + + final_ts.ntp_seconds= now.tv_sec + NTP_1970; + d= now.tv_usec / 1e6; + d *= 4294967296.0; + final_ts.ntp_fraction= d; + + d= final_ts.ntp_seconds + final_ts.ntp_fraction/NTP_4G; + snprintf(line, sizeof(line), ", " DBQ(final-ts) ": %.9f", d); + add_str(state, line); + + /* Compute rtt */ + d= final_ts.ntp_seconds - ntohl(ntphdr->ntp_origin_ts.ntp_seconds) - + (ntohl(ntphdr->ntp_transmit_ts.ntp_seconds) - + ntohl(ntphdr->ntp_receive_ts.ntp_seconds)) + + final_ts.ntp_fraction/NTP_4G - + ntohl(ntphdr->ntp_origin_ts.ntp_fraction)/NTP_4G - + (ntohl(ntphdr->ntp_transmit_ts.ntp_fraction)/NTP_4G - + ntohl(ntphdr->ntp_receive_ts.ntp_fraction)/NTP_4G); + snprintf(line, sizeof(line), ", " DBQ(rtt) ": %f", d); + add_str(state, line); + + d= (ntohl(ntphdr->ntp_origin_ts.ntp_seconds) + + final_ts.ntp_seconds)/2.0 - + (ntohl(ntphdr->ntp_receive_ts.ntp_seconds) + + ntohl(ntphdr->ntp_transmit_ts.ntp_seconds))/2.0 + + (ntohl(ntphdr->ntp_origin_ts.ntp_fraction)/NTP_4G + + final_ts.ntp_fraction/NTP_4G)/2.0 - + (ntohl(ntphdr->ntp_receive_ts.ntp_fraction)/NTP_4G + + ntohl(ntphdr->ntp_transmit_ts.ntp_fraction)/NTP_4G)/2.0; + snprintf(line, sizeof(line), ", " DBQ(offset) ": %f", d); + add_str(state, line); + + state->open_result= 1; + + send_pkt(state); +#if 0 + if (memcmp(&ip->ip_dst, + &((struct sockaddr_in *)&state->loc_sin6)-> + sin_addr, sizeof(eip->ip_src)) != 0) + { + printf("ready_callback4: weird destination %s\n", + inet_ntoa(ip->ip_dst)); + } + + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/1e3; + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntoa(remote.sin_addr)); + add_str(state, line); + snprintf(line, sizeof(line), ", \"ttl\":%d, \"size\":%d", + ip->ip_ttl, (int)nrecv - IPHDR - ICMP_MINLEN); + add_str(state, line); + if (!late) + { + snprintf(line, sizeof(line), ", \"rtt\":%.3f", ms); + add_str(state, line); + } + +#if 0 + printf("ready_callback4: from %s, ttl %d", + inet_ntoa(remote.sin_addr), ip->ip_ttl); + printf(" for %s hop %d\n", + inet_ntoa(((struct sockaddr_in *) + &state->sin6)->sin_addr), state->hop); +#endif + + /* Done */ + state->done= 1; + + state->open_result= 1; + + if (!late && !isDup) + { + if (state->duptimeout) + { + state->gotresp= 1; + interval.tv_sec= state->duptimeout/1000000; + interval.tv_usec= state->duptimeout % 1000000; + evtimer_add(&state->timer, &interval); + } + else + send_pkt(state); + } + + return; + } + else if (icmp->icmp_type == ICMP_ECHO || + icmp->icmp_type == ICMP_ROUTERADVERT) + { + /* No need to do anything */ + } + else + { + printf("ready_callback4: got type %d\n", icmp->icmp_type); + return; + } +#endif +} + +#if 0 +static void ready_callback6(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + crondlog(DIE9 "ready_callback6"); abort(); +#if 0 + ssize_t nrecv; + int ind, rcvdttl, nxt, icmp_prefixlen, offset; + unsigned nextmtu, seq, optlen, hbhoptsize, dstoptsize; + size_t ehdrsiz, v6info_siz, siz; + struct trtbase *base; + struct trtstate *state; + struct ip6_hdr *eip; + struct ip6_frag *frag; + struct ip6_ext *opthdr; + struct icmp6_hdr *icmp, *eicmp; + struct tcphdr *etcp; + struct udphdr *eudp; + struct v6info *v6info; + struct cmsghdr *cmsgptr; + void *ptr; + double ms; + struct timeval now; + struct sockaddr_in6 remote; + struct in6_addr dstaddr; + struct msghdr msg; + struct iovec iov[1]; + struct timeval interval; + char buf[INET6_ADDRSTRLEN]; + char line[80]; + char cmsgbuf[256]; + + gettimeofday(&now, NULL); + + state= s; + base= state->base; + + iov[0].iov_base= base->packet; + iov[0].iov_len= sizeof(base->packet); + msg.msg_name= &remote; + msg.msg_namelen= sizeof(remote); + msg.msg_iov= iov; + msg.msg_iovlen= 1; + msg.msg_control= cmsgbuf; + msg.msg_controllen= sizeof(cmsgbuf); + msg.msg_flags= 0; /* Not really needed */ + + nrecv= recvmsg(state->socket_icmp, &msg, MSG_DONTWAIT); + if (nrecv == -1) + { + /* Strange, read error */ + printf("ready_callback6: read error '%s'\n", strerror(errno)); + return; + } + + rcvdttl= -42; /* To spot problems */ + memset(&dstaddr, '\0', sizeof(dstaddr)); + for (cmsgptr= CMSG_FIRSTHDR(&msg); cmsgptr; + cmsgptr= CMSG_NXTHDR(&msg, cmsgptr)) + { + if (cmsgptr->cmsg_len == 0) + break; /* Can this happen? */ + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_HOPLIMIT) + { + rcvdttl= *(int *)CMSG_DATA(cmsgptr); + } + if (cmsgptr->cmsg_level == IPPROTO_IPV6 && + cmsgptr->cmsg_type == IPV6_PKTINFO) + { + dstaddr= ((struct in6_pktinfo *) + CMSG_DATA(cmsgptr))->ipi6_addr; + } + } + + if (nrecv < sizeof(*icmp)) + { + /* Short packet */ +#if 0 + printf("ready_callback6: too short %d (icmp)\n", (int)nrecv); +#endif + return; + } + + icmp= (struct icmp6_hdr *)&base->packet; + + hbhoptsize= 0; + dstoptsize= 0; + if (icmp->icmp6_type == ICMP6_DST_UNREACH || + icmp->icmp6_type == ICMP6_PACKET_TOO_BIG || + icmp->icmp6_type == ICMP6_TIME_EXCEEDED) + { + eip= (struct ip6_hdr *)&icmp[1]; + + /* Make sure the packet we have is big enough */ + if (nrecv < sizeof(*icmp) + sizeof(*eip)) + { +#if 0 + printf("ready_callback6: too short %d (icmp_ip)\n", + (int)nrecv); +#endif + return; + } + + /* Make sure we have TCP, UDP, ICMP or a fragment header */ + if (eip->ip6_nxt == IPPROTO_FRAGMENT || + eip->ip6_nxt == IPPROTO_HOPOPTS || + eip->ip6_nxt == IPPROTO_DSTOPTS || + eip->ip6_nxt == IPPROTO_TCP || + eip->ip6_nxt == IPPROTO_UDP || + eip->ip6_nxt == IPPROTO_ICMPV6) + { + ehdrsiz= 0; + frag= NULL; + nxt= eip->ip6_nxt; + ptr= &eip[1]; + if (nxt == IPPROTO_HOPOPTS) + { + /* Make sure the options header is completely + * there. + */ + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + sizeof(*opthdr)) + { +#if 0 + printf( + "ready_callback6: too short %d (icmp+ip+opt)\n", + (int)nrecv); +#endif + return; + } + opthdr= (struct ip6_ext *)ptr; + hbhoptsize= 8*opthdr->ip6e_len; + optlen= hbhoptsize+8; + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + optlen) + { + /* Does not contain the full header */ + return; + } + ehdrsiz += optlen; + nxt= opthdr->ip6e_nxt; + ptr= ((char *)opthdr)+optlen; + } + if (nxt == IPPROTO_FRAGMENT) + { + /* Make sure the fragment header is completely + * there. + */ + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + sizeof(*frag)) + { +#if 0 + printf( + "ready_callback6: too short %d (icmp+ip+frag)\n", + (int)nrecv); +#endif + return; + } + frag= (struct ip6_frag *)ptr; + if ((ntohs(frag->ip6f_offlg) & ~3) != 0) + { + /* Not first fragment, just ignore + * it. + */ + return; + } + ehdrsiz += sizeof(*frag); + nxt= frag->ip6f_nxt; + ptr= &frag[1]; + } + if (nxt == IPPROTO_DSTOPTS) + { + /* Make sure the options header is completely + * there. + */ + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + sizeof(*opthdr)) + { +#if 0 + printf( + "ready_callback6: too short %d (icmp+ip+opt)\n", + (int)nrecv); +#endif + return; + } + opthdr= (struct ip6_ext *)ptr; + dstoptsize= 8*opthdr->ip6e_len; + optlen= dstoptsize+8; + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + optlen) + { + /* Does not contain the full header */ + return; + } + ehdrsiz += optlen; + nxt= opthdr->ip6e_nxt; + ptr= ((char *)opthdr)+optlen; + } + + v6info_siz= sizeof(*v6info); + if (nxt == IPPROTO_TCP) + { + ehdrsiz += sizeof(*etcp); + v6info_siz= 0; + } + else if (nxt == IPPROTO_UDP) + ehdrsiz += sizeof(*eudp); + else + ehdrsiz += sizeof(*eicmp); + + /* Now check if there is also a header in the + * packet. + */ + if (nrecv < sizeof(*icmp) + sizeof(*eip) + + ehdrsiz + v6info_siz) + { +#if 0 + printf( + "ready_callback6: too short %d (all) from %s\n", + (int)nrecv, inet_ntop(AF_INET6, + &remote.sin6_addr, buf, sizeof(buf))); +#endif + return; + } + + etcp= NULL; + eudp= NULL; + eicmp= NULL; + v6info= NULL; + if (nxt == IPPROTO_TCP) + { + etcp= (struct tcphdr *)ptr; + } + else if (nxt == IPPROTO_UDP) + { + eudp= (struct udphdr *)ptr; + v6info= (struct v6info *)&eudp[1]; + } + else + { + eicmp= (struct icmp6_hdr *)ptr; + v6info= (struct v6info *)&eicmp[1]; + } + +#if 0 + if (v6info) + { + printf( +"ready_callback6: pid = htonl(%d), id = htonl(%d), seq = htonl(%d)\n", + ntohl(v6info->pid), + ntohl(v6info->id), + ntohl(v6info->seq)); + } +#endif + + if (etcp) + { + /* We store the id in high order 16 bits of the + * sequence number + */ + ind= ntohl(etcp->seq) >> 16; + } + else + { + if (ntohl(v6info->pid) != base->my_pid) + { + /* From a different process */ + return; + } + + ind= ntohl(v6info->id); + } + + if (ind != state->index) + state= NULL; + + if (state && state->sin6.sin6_family != AF_INET6) + state= NULL; + + if (state) + { + if ((etcp && !state->do_tcp) || + (eudp && !state->do_udp) || + (eicmp && !state->do_icmp)) + { + state= NULL; + } + } + + if (!state) + { + /* Nothing here */ + return; + } + +#if 0 + printf("ready_callback6: from %s", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, + buf, sizeof(buf)), state->hop); +#endif + + 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); + return; + } + + late= 0; + isDup= 0; + if (etcp) + { + /* Sequence number is in seq field */ + seq= ntohl(etcp->seq) & 0xffff; + } + else + seq= ntohl(v6info->seq); + + if (state->open_result) + add_str(state, " }, { "); + + if (seq != state->seq) + { + if (seq > state->seq) + { +#if 0 + printf( + "ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n", + ntohl(v6info->seq), + state->seq); +#endif + return; + } + late= 1; + + snprintf(line, sizeof(line), DBQ(late) ":%d", + state->seq-seq); + add_str(state, line); + } else if (state->gotresp) + { + isDup= 1; + add_str(state, DBQ(dup) ":true"); + } + + if (!late && !isDup) + state->last_response_hop= state->hop; + + if (memcmp(&eip->ip6_src, + &state->loc_sin6.sin6_addr, + sizeof(eip->ip6_src)) != 0) + { + printf("ready_callback6: changed source %s\n", + inet_ntop(AF_INET6, &eip->ip6_src, + buf, sizeof(buf))); + } + if (memcmp(&eip->ip6_dst, + &state->sin6.sin6_addr, + sizeof(eip->ip6_dst)) != 0) + { + printf( + "ready_callback6: changed destination %s for %s\n", + inet_ntop(AF_INET6, &eip->ip6_dst, + buf, sizeof(buf)), + state->hostname); + } + if (memcmp(&dstaddr, + &state->loc_sin6.sin6_addr, + sizeof(dstaddr)) != 0) + { + printf("ready_callback6: weird destination %s\n", + inet_ntop(AF_INET6, &dstaddr, + buf, sizeof(buf))); + } + + if (eicmp && state->parismod && + ntohs(eicmp->icmp6_cksum) != + state->paris % state->parismod + 1) + { + printf( + "ready_callback6: got checksum 0x%x, expected 0x%x\n", + ntohs(eicmp->icmp6_cksum), + state->paris % state->parismod + 1); + } + + if (!late) + { + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/ + 1e3; + } + else if (v6info) + { + ms= (now.tv_sec-v6info->tv.tv_sec)*1000 + + (now.tv_usec-v6info->tv.tv_usec)/ + 1e3; + } + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + add_str(state, line); + snprintf(line, sizeof(line), + ", \"ttl\":%d, \"rtt\":%.3f, \"size\":%d", + rcvdttl, ms, (int)(nrecv-ICMP6_HDR)); + add_str(state, line); + if (eip->ip6_hops != 1) + { + snprintf(line, sizeof(line), ", \"ittl\":%d", + eip->ip6_hops); + add_str(state, line); + } + if (hbhoptsize) + { + snprintf(line, sizeof(line), + ", \"hbhoptsize\":%d", hbhoptsize); + add_str(state, line); + } + if (dstoptsize) + { + snprintf(line, sizeof(line), + ", \"dstoptsize\":%d", dstoptsize); + add_str(state, line); + } + +#if 0 + printf("ready_callback6: from %s, ttl %d", + inet_ntop(AF_INET6, &remote.sin6_addr, buf, + sizeof(buf)), rcvdttl); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, + sizeof(buf)), state->hop); +#endif + + if (icmp->icmp6_type == ICMP6_TIME_EXCEEDED) + { + if (!late && !isDup) + state->not_done= 1; + } + else if (icmp->icmp6_type == ICMP6_PACKET_TOO_BIG) + { + nextmtu= ntohl(icmp->icmp6_mtu); + snprintf(line, sizeof(line), ", \"mtu\":%d", + nextmtu); + add_str(state, line); + siz= sizeof(*eip); + if (eudp) + siz += sizeof(*eudp); + else if (eicmp) + siz += sizeof(*eicmp); + else if (etcp) + siz += sizeof(*etcp); + if (nextmtu < 1200) + { + /* This is IPv6, no need to go + * below 1280. Use 1200 to deal with + * off by one error or weird tunnels. + */ + nextmtu= 1200; + } + if (!late && nextmtu >= siz) + { + nextmtu -= siz; + if (nextmtu < state->curpacksize) + state->curpacksize= nextmtu; + } + if (!late) + state->not_done= 1; + } + else if (icmp->icmp6_type == ICMP6_DST_UNREACH) + { + if (!late) + state->done= 1; + switch(icmp->icmp6_code) + { + case ICMP6_DST_UNREACH_NOROUTE: /* 0 */ + add_str(state, ", \"err\":\"N\""); + break; + case ICMP6_DST_UNREACH_ADMIN: /* 1 */ + add_str(state, ", \"err\":\"A\""); + break; + case ICMP6_DST_UNREACH_BEYONDSCOPE: /* 2 */ + add_str(state, ", \"err\":\"h\""); + break; + case ICMP6_DST_UNREACH_ADDR: /* 3 */ + add_str(state, ", \"err\":\"H\""); + break; + case ICMP6_DST_UNREACH_NOPORT: /* 4 */ + break; + default: + snprintf(line, sizeof(line), + ", \"err\":%d", + icmp->icmp6_code); + add_str(state, line); + break; + } + } + } + else + { + printf( + "ready_callback6: not UDP or ICMP (ip6_nxt = %d)\n", + eip->ip6_nxt); + return; + } + + /* RFC-4884, Multi-Part ICMP messages */ + icmp_prefixlen= icmp->icmp6_data8[0] * 8; + if (icmp_prefixlen != 0) + { + + printf("icmp6_data8[0]: 0x%x for %s\n", icmp->icmp6_data8[0], state->hostname); + printf("icmp_prefixlen: 0x%x for %s\n", icmp_prefixlen, inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, sizeof(buf))); + offset= sizeof(*icmp) + icmp_prefixlen; + if (nrecv > offset) + { + do_icmp_multi(state, base->packet+offset, + nrecv-offset, 0 /*!pre_rfc4884*/); + } + else + { +#if 0 + printf( + "ready_callback6: too short %d (Multi-Part ICMP)\n", + (int)nrecv); +#endif + } + } + else if (nrecv > 128) + { + /* Try old style extensions */ + icmp_prefixlen= 128; + offset= sizeof(*icmp) + icmp_prefixlen; + if (nrecv > offset) + { + do_icmp_multi(state, base->packet+offset, + nrecv-offset, 1 /*pre_rfc4884*/); + } + else + { + printf( + "ready_callback6: too short %d (Multi-Part ICMP)\n", + (int)nrecv); + } + } + + state->open_result= 1; + + if (!late && !isDup) + { + if (state->duptimeout) + { + state->gotresp= 1; + interval.tv_sec= state->duptimeout/1000000; + interval.tv_usec= state->duptimeout % 1000000; + evtimer_add(&state->timer, &interval); + } + else + send_pkt(state); + } + } + else if (icmp->icmp6_type == ICMP6_ECHO_REPLY) + { + eip= NULL; + + /* Now check if there is also a header in the packet */ + if (nrecv < sizeof(*icmp) + sizeof(*v6info)) + { +#if 0 + printf("ready_callback6: too short %d (echo reply)\n", + (int)nrecv); +#endif + return; + } + + eudp= NULL; + eicmp= NULL; + + v6info= (struct v6info *)&icmp[1]; + + if (ntohl(v6info->pid) != base->my_pid) + { + /* From a different process */ + return; + } + + ind= ntohl(v6info->id); + + if (ind != state->index) + state= NULL; + if (state && state->sin6.sin6_family != AF_INET6) + state= NULL; + + if (state && !state->do_icmp) + { + state= NULL; + } + + if (!state) + { + /* Nothing here */ + return; + } + +#if 0 + printf("ready_callback6: from %s", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, + buf, sizeof(buf)), state->hop); +#endif + + 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); + return; + } + + if (state->open_result) + add_str(state, " }, { "); + + late= 0; + isDup= 0; + seq= ntohl(v6info->seq); + if (seq != state->seq) + { + if (seq > state->seq) + { + printf( +"ready_callback6: mismatch for seq, got 0x%x, expected 0x%x\n", + ntohl(v6info->seq), + state->seq); + return; + } + late= 1; + + snprintf(line, sizeof(line), DBQ(late) ":%d", + state->seq-seq); + add_str(state, line); + } + else if (state->gotresp) + { + isDup= 1; + add_str(state, DBQ(dup) ":true"); + } + + state->done= 1; + + if (memcmp(&dstaddr, &state->loc_sin6.sin6_addr, + sizeof(dstaddr)) != 0) + { + printf("ready_callback6: weird destination %s\n", + inet_ntop(AF_INET6, &dstaddr, + buf, sizeof(buf))); + } + + if (!late) + { + ms= (now.tv_sec-state->xmit_time.tv_sec)*1000 + + (now.tv_usec-state->xmit_time.tv_usec)/ + 1e3; + } + else + { + ms= (now.tv_sec-v6info->tv.tv_sec)*1000 + + (now.tv_usec-v6info->tv.tv_usec)/ + 1e3; + } + + snprintf(line, sizeof(line), "%s\"from\":\"%s\"", + (late || isDup) ? ", " : "", + inet_ntop(AF_INET6, &remote.sin6_addr, + buf, sizeof(buf))); + add_str(state, line); + snprintf(line, sizeof(line), + ", \"ttl\":%d, \"rtt\":%.3f, \"size\":%d", + rcvdttl, ms, (int)(nrecv - ICMP6_HDR)); + add_str(state, line); + +#if 0 + printf("ready_callback6: from %s, ttl %d", + inet_ntop(AF_INET6, &remote.sin6_addr, buf, + sizeof(buf)), rcvdttl); + printf(" for %s hop %d\n", + inet_ntop(AF_INET6, &state->sin6.sin6_addr, buf, + sizeof(buf)), state->hop); +#endif + + state->open_result= 1; + + send_pkt(state); + } + else if (icmp->icmp6_type == ICMP6_ECHO_REQUEST /* 128 */ || + icmp->icmp6_type == MLD_LISTENER_QUERY /* 130 */ || + icmp->icmp6_type == MLD_LISTENER_REPORT /* 131 */ || + icmp->icmp6_type == ND_ROUTER_ADVERT /* 134 */ || + icmp->icmp6_type == ND_NEIGHBOR_SOLICIT /* 135 */ || + icmp->icmp6_type == ND_NEIGHBOR_ADVERT /* 136 */ || + icmp->icmp6_type == ND_REDIRECT /* 137 */) + { + /* No need to do anything */ + } + else + { + printf("ready_callback6: got type %d\n", icmp->icmp6_type); + return; + } +#endif +} +#endif + +static struct ntpbase *ntp_base_new(struct event_base + *event_base) +{ + struct ntpbase *base; + + base= xzalloc(sizeof(*base)); + + base->event_base= event_base; + + base->tabsiz= 10; + base->table= xzalloc(base->tabsiz * sizeof(*base->table)); + + base->my_pid= getpid(); + + return base; +} + +static void noreply_callback(int __attribute((unused)) unused, + const short __attribute((unused)) event, void *s) +{ + struct ntpstate *state; + + state= s; + +#if 0 + printf("noreply_callback: gotresp = %d\n", + state->gotresp); +#endif + + if (!state->gotresp) + { + if (state->open_result) + add_str(state, " }, { "); + add_str(state, DBQ(x) ":" DBQ(*)); + state->open_result= 1; + } + + send_pkt(state); +} + +static void *ntp_init(int __attribute((unused)) argc, char *argv[], + void (*done)(void *state)) +{ + uint32_t opt; + int i, do_v6; + unsigned count, timeout; + /* must be int-sized */ + size_t newsiz; + char *str_Atlas; + const char *hostname; + char *out_filename; + const char *destportstr; + char *interface; + struct ntpstate *state; + FILE *fh; + + if (!ntp_base) + { + ntp_base= ntp_base_new(EventBase); + if (!ntp_base) + crondlog(DIE9 "ntp_base_new failed"); + } + + /* Parse arguments */ + count= 3; + interface= NULL; + timeout= 1000; + str_Atlas= NULL; + out_filename= NULL; + opt_complementary = "=1:4--6:i--u:c+:w+:"; + + opt = getopt32(argv, NTP_OPT_STRING, &count, + &interface, &timeout, &str_Atlas, &out_filename); + hostname = argv[optind]; + + if (opt == 0xffffffff) + { + crondlog(LVL8 "bad options"); + return NULL; + } + + do_v6= !!(opt & OPT_6); + + if (out_filename) + { + if (!validate_filename(out_filename, SAFE_PREFIX)) + { + crondlog(LVL8 "insecure file '%s'", out_filename); + return NULL; + } + fh= fopen(out_filename, "a"); + if (!fh) + { + crondlog(LVL8 "unable to append to '%s'", + out_filename); + return NULL; + } + fclose(fh); + } + + if (str_Atlas) + { + if (!validate_atlas_id(str_Atlas)) + { + crondlog(LVL8 "bad atlas ID '%s'", str_Atlas); + return NULL; + } + } + + destportstr= "123"; + + state= xzalloc(sizeof(*state)); + state->count= count; + state->interface= interface; + state->destportstr= strdup(destportstr); + state->timeout= timeout*1000; + state->atlas= str_Atlas ? strdup(str_Atlas) : NULL; + state->hostname= strdup(hostname); + state->do_v6= do_v6; + state->out_filename= out_filename ? strdup(out_filename) : NULL; + state->base= ntp_base; + state->busy= 0; + state->result= NULL; + state->reslen= 0; + state->resmax= 0; + + for (i= 0; itabsiz; i++) + { + if (ntp_base->table[i] == NULL) + break; + } + if (i >= ntp_base->tabsiz) + { + newsiz= 2*ntp_base->tabsiz; + ntp_base->table= xrealloc(ntp_base->table, + newsiz*sizeof(*ntp_base->table)); + for (i= ntp_base->tabsiz; itable[i]= NULL; + i= ntp_base->tabsiz; + ntp_base->tabsiz= newsiz; + } + state->index= i; + ntp_base->table[i]= state; + ntp_base->done= done; + + memset(&state->loc_sin6, '\0', sizeof(state->loc_sin6)); + state->loc_socklen= 0; + + evtimer_assign(&state->timer, state->base->event_base, + noreply_callback, state); + + return state; +} + +static void traceroute_start2(void *state) +{ + struct ntpstate *ntpstate; + + ntpstate= state; + + if (ntpstate->busy) + { + printf("ntp_start: busy, can't start\n"); + return; + } + ntpstate->busy= 1; + + ntpstate->min= ULONG_MAX; + ntpstate->max= 0; + ntpstate->sum= 0; + ntpstate->sentpkts= 0; + ntpstate->rcvdpkts= 0; + ntpstate->duppkts= 0; + + ntpstate->sent= 0; + ntpstate->seq= 0; + ntpstate->first= 1; + ntpstate->done= 0; + ntpstate->not_done= 0; + + if (ntpstate->result) free(ntpstate->result); + ntpstate->resmax= 80; + ntpstate->result= xmalloc(ntpstate->resmax); + ntpstate->reslen= 0; + ntpstate->open_result= 0; + ntpstate->starttime= time(NULL); + + ntpstate->socket= -1; + + if (create_socket(ntpstate) == -1) + return; + if (ntpstate->do_v6) + { + ntpstate->loc_sin6.sin6_port= htons(SRC_BASE_PORT + + ntpstate->index); + } + else + { + ((struct sockaddr_in *)(&ntpstate->loc_sin6))-> + sin_port= htons(SRC_BASE_PORT + + ntpstate->index); + } + + send_pkt(ntpstate); +} + +static int create_socket(struct ntpstate *state) +{ + int af, type, protocol; + int r, serrno; + char line[80]; + + af= (state->do_v6 ? AF_INET6 : AF_INET); + type= SOCK_DGRAM; + protocol= 0; + + state->socket= xsocket(af, type, protocol); + if (state->socket == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(socket failed: %s) " }", + strerror(serrno)); + add_str(state, line); + report(state); + return -1; + } + + if (state->interface) + { + if (bind_interface(state->socket, + af, state->interface) == -1) + { + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(bind_interface failed) " }"); + add_str(state, line); + report(state); + return -1; + } + } + + r= connect(state->socket, + (struct sockaddr *)&state->sin6, + state->socklen); +#if 0 + { errno= ENOSYS; r= -1; } +#endif + if (r == -1) + { + serrno= errno; + + snprintf(line, sizeof(line), + ", " DBQ(error) ":" DBQ(connect failed: %s) " }", + strerror(serrno)); + add_str(state, line); + report(state); + return -1; + } + state->loc_socklen= sizeof(state->loc_sin6); + if (getsockname(state->socket, + &state->loc_sin6, + &state->loc_socklen) == -1) + { + crondlog(DIE9 "getsockname failed"); + } +#if 0 + printf("Got localname: %s\n", + inet_ntop(AF_INET6, + &state->loc_sin6.sin6_addr, + buf, sizeof(buf))); +#endif + + + event_assign(&state->event_socket, state->base->event_base, + state->socket, + EV_READ | EV_PERSIST, + (af == AF_INET6 ? ready_callback : ready_callback), + state); + event_add(&state->event_socket, NULL); + + return 0; +} + +static void dns_cb(int result, struct evutil_addrinfo *res, void *ctx) +{ + int count; + struct ntpstate *env; + struct evutil_addrinfo *cur; + char line[160]; + + env= ctx; + + if (!env->dnsip) + { + crondlog(LVL7 + "dns_cb: in dns_cb but not doing dns at this time"); + if (res) + evutil_freeaddrinfo(res); + return; + } + + if (result != 0) + { + /* Hmm, great. Where do we put this init code */ + if (env->result) free(env->result); + env->resmax= 80; + env->result= xmalloc(env->resmax); + env->reslen= 0; + + env->starttime= time(NULL); + snprintf(line, sizeof(line), + "{ " DBQ(error) ":" DBQ(name resolution failed: %s) " }", + evutil_gai_strerror(result)); + add_str(env, line); + report(env); + return; + } + + env->dnsip= 0; + + env->dns_res= res; + env->dns_curr= res; + + count= 0; + for (cur= res; cur; cur= cur->ai_next) + count++; + + // env->reportcount(env, count); + + while (env->dns_curr) + { + env->socklen= env->dns_curr->ai_addrlen; + if (env->socklen > sizeof(env->sin6)) + continue; /* Weird */ + memcpy(&env->sin6, env->dns_curr->ai_addr, + env->socklen); + + traceroute_start2(env); + + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + return; + } + + /* Something went wrong */ + evutil_freeaddrinfo(env->dns_res); + env->dns_res= NULL; + env->dns_curr= NULL; + snprintf(line, sizeof(line), +"%s{ " DBQ(error) ":" DBQ(name resolution failed: out of addresses) " } ] }", + env->sent ? " }, " : ""); + add_str(env, line); + report(env); +} + +static void ntp_start(void *state) +{ + struct ntpstate *ntpstate; + struct evutil_addrinfo hints; + + ntpstate= state; + + memset(&hints, '\0', sizeof(hints)); + hints.ai_socktype= SOCK_DGRAM; + hints.ai_family= ntpstate->do_v6 ? AF_INET6 : AF_INET; + ntpstate->dnsip= 1; + (void) evdns_getaddrinfo(DnsBase, ntpstate->hostname, + ntpstate->destportstr, &hints, dns_cb, ntpstate); +} + +static int ntp_delete(void *state) +{ + int ind; + struct ntpstate *ntpstate; + struct ntpbase *base; + + ntpstate= state; + + printf("ntp_delete: state %p, index %d, busy %d\n", + state, ntpstate->index, ntpstate->busy); + + if (ntpstate->busy) + return 0; + + base= ntpstate->base; + ind= ntpstate->index; + + if (base->table[ind] != ntpstate) + crondlog(DIE9 "strange, state not in table"); + base->table[ind]= NULL; + + event_del(&ntpstate->timer); + + free(ntpstate->atlas); + ntpstate->atlas= NULL; + free(ntpstate->hostname); + ntpstate->hostname= NULL; + free(ntpstate->destportstr); + ntpstate->destportstr= NULL; + free(ntpstate->out_filename); + ntpstate->out_filename= NULL; + + free(ntpstate); + + return 1; +} + +struct testops ntp_ops = { ntp_init, ntp_start, ntp_delete }; + -- cgit v1.2.3