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