aboutsummaryrefslogtreecommitdiff
path: root/networking
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2015-05-15 10:23:51 +0200
committerBjørn Mork <bjorn@mork.no>2015-05-15 10:23:51 +0200
commit02013228914a1d17e8df15d4e2b7950469395a5c (patch)
tree48d2fbe2f5a5adb60cbeabc26fadaec8e0fa82ed /networking
parent9b3dbb454e8f8a463d5fe4541ee2001585527bc6 (diff)
ripe-atlas-fw: imported version 45204520
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Diffstat (limited to 'networking')
-rw-r--r--networking/Config.in48
-rw-r--r--networking/Kbuild5
-rw-r--r--networking/atlasinit.h33
-rw-r--r--networking/httpget.c1458
-rw-r--r--networking/httppost.c1283
-rw-r--r--networking/rptra6.c353
-rw-r--r--networking/rxtxrpt.c368
-rw-r--r--networking/sslgetcert.c762
-rw-r--r--networking/telnetd.c805
-rw-r--r--networking/tftp.c2
10 files changed, 5106 insertions, 11 deletions
diff --git a/networking/Config.in b/networking/Config.in
index 95f8942..7c72b6d 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -47,6 +47,13 @@ config ARPING
help
Ping hosts by ARP packets.
+config AUTOSSH
+ bool "autossh"
+ default n
+ help
+ Monitor the ssh port. Added by AA Antony. 2010 Oct
+
+
config BRCTL
bool "brctl"
default n
@@ -199,8 +206,7 @@ config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
This option enables support for running scripts through an
interpreter. Turn this on if you want PHP scripts to work
properly. You need to supply an additional line in your httpd
- config file:
- *.php:/path/to/your/php
+ config file: *.php:/path/to/your/php
config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
bool "Set REMOTE_PORT environment variable for CGI"
@@ -245,6 +251,18 @@ config FEATURE_HTTPD_PROXY
Then a request to /url/myfile will be forwarded to
http://hostname[:port]/new/path/myfile.
+config HTTPGET
+ bool "httpget"
+ default y
+ help
+ simple http GET. RIPE NCC 2011
+
+config HTTPPOST
+ bool "httppost"
+ default y
+ help
+ simple httppost --post-file with IPv4. RIPE NCC 2011
+
config IFCONFIG
bool "ifconfig"
default n
@@ -681,6 +699,19 @@ config ROUTE
help
Route displays or manipulates the kernel's IP routing tables.
+config RPTRA6
+ bool "rptra6"
+ default n
+ help
+ Report received IPv6 router advertisements
+
+config RXTXRPT
+ bool "rxtxrpt"
+ default n
+ help
+ rxtxrpt report RX and TX statistics as well as IPv6 addresses and
+ routes
+
config SLATTACH
bool "slattach"
default n
@@ -688,6 +719,12 @@ config SLATTACH
slattach is a small utility to attach network interfaces to serial
lines.
+config SSLGETCERT
+ bool "sslgetcert"
+ default n
+ help
+ simple simple program to get certificates from an ssl server
+
#config TC
# bool "tc"
# default n
@@ -833,6 +870,13 @@ config TRACEROUTE
help
Utility to trace the route of IP packets
+config FEATURE_TRACEROUTE_IPV6
+ bool "Enable IPv6 Support"
+ default n
+ depends on FEATURE_IPV6 && TRACEROUTE
+ help
+ Add IPv6 support for the traceroute
+
config FEATURE_TRACEROUTE_VERBOSE
bool "Enable verbose output"
default n
diff --git a/networking/Kbuild b/networking/Kbuild
index 63d0745..ff7dfb3 100644
--- a/networking/Kbuild
+++ b/networking/Kbuild
@@ -15,6 +15,8 @@ lib-$(CONFIG_FTPGET) += ftpgetput.o
lib-$(CONFIG_FTPPUT) += ftpgetput.o
lib-$(CONFIG_HOSTNAME) += hostname.o
lib-$(CONFIG_HTTPD) += httpd.o
+lib-$(CONFIG_HTTPGET) += httpget.o
+lib-$(CONFIG_HTTPPOST) += httppost.o
lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
lib-$(CONFIG_IFUPDOWN) += ifupdown.o
@@ -29,7 +31,10 @@ lib-$(CONFIG_PING) += ping.o
lib-$(CONFIG_PING6) += ping.o
lib-$(CONFIG_PSCAN) += pscan.o
lib-$(CONFIG_ROUTE) += route.o
+lib-$(CONFIG_RPTRA6) += rptra6.o
+lib-$(CONFIG_RXTXRPT) += rxtxrpt.o
lib-$(CONFIG_SLATTACH) += slattach.o
+lib-$(CONFIG_SSLGETCERT) += sslgetcert.o
lib-$(CONFIG_TC) += tc.o
lib-$(CONFIG_TELNET) += telnet.o
lib-$(CONFIG_TELNETD) += telnetd.o
diff --git a/networking/atlasinit.h b/networking/atlasinit.h
new file mode 100644
index 0000000..f7362a6
--- /dev/null
+++ b/networking/atlasinit.h
@@ -0,0 +1,33 @@
+/* RIPEAtlas
+ * All the configurable variables - and some non configurables too
+ * $Id: $
+ */
+
+#ifndef _ATLASINIT_H
+#define _ATLASINIT_H
+
+#define ATLAS_BUF_SIZE 1024
+#define MAX_READ ATLAS_BUF_SIZE-2 /* should be enough to read controller keys */
+
+/*********************************************************************
+ * Set these constants to your liking
+ */
+
+extern const char atlas_log_file[];
+extern const int atlas_log_level;
+
+extern const char atlas_contr_known_hosts[];
+extern const char atlas_rereg_timestamp[];
+
+extern const int max_lines; /* maximum lines we'll process */
+extern const int min_rereg_time; /* 12h */
+extern const int max_rereg_time; /* 28d */
+extern const int default_rereg_time; /* 7d */
+
+/*********************************************************************/
+
+enum { ALL, DEBUG, INFO, WARN, ERROR } error_level;
+
+void atlas_log( int level, const char *msg, ... );
+
+#endif
diff --git a/networking/httpget.c b/networking/httpget.c
new file mode 100644
index 0000000..387a33f
--- /dev/null
+++ b/networking/httpget.c
@@ -0,0 +1,1458 @@
+/*
+ * Copyright (c) 2011-2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * httpget.c -- Simple program that uses the HTTP GET command
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+#define SAFE_PREFIX_OUT ATLAS_DATA_OUT
+#define SAFE_PREFIX_NEW ATLAS_DATA_NEW
+
+#define debug 0
+
+static struct option longopts[]=
+{
+ { "append", no_argument, NULL, 'a' },
+ { "get", no_argument, NULL, 'g' },
+ { "head", no_argument, NULL, 'E' },
+ { "post", no_argument, NULL, 'P' },
+ { "post-file", required_argument, NULL, 'p' },
+ { "post-dir", required_argument, NULL, 'D' },
+ { "post-header", required_argument, NULL, 'h' },
+ { "post-footer", required_argument, NULL, 'f' },
+ { "set-time", required_argument, NULL, 's' },
+ { "store-headers", required_argument, NULL, 'H' },
+ { "store-body", required_argument, NULL, 'B' },
+ { "summary", no_argument, NULL, 'S' },
+ { "user-agent", required_argument, NULL, 'u' },
+ { NULL, }
+};
+
+static char *time_tolerance;
+static char buffer[1024];
+static char host_addr[INET6_ADDRSTRLEN];
+static sa_family_t family;
+static const char *user_agent= "httpget for atlas.ripe.net";
+static int tcp_fd= -1;
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp);
+static int check_result(FILE *tcp_file, int *result);
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length,
+ int *headers_size, FILE *out_file, int max_headers);
+static int connect_to_name(char *host, char *port, int only_v4, int only_v6,
+ struct timeval *start_time, int *gerr);
+static char *do_dir(char *dir_name, off_t *lenp);
+static int copy_chunked(FILE *in_file, FILE *out_file, int *length,
+ int max_body);
+static int copy_bytes(FILE *in_file, FILE *out_file, int *length,
+ int max_body);
+static void got_alarm(int sig);
+static void fatal(const char *fmt, ...);
+static void fatal_err(const char *fmt, ...);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+static int write_to_tcp_fd (int fd, FILE *tcp_file);
+static void skip_spaces(const char *cp, char **ncp);
+
+int httpget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int httpget_main(int argc, char *argv[])
+{
+ int c, i, r, fd, fdF, fdH, fdS, chunked, content_length,
+ result, http_result, do_get, do_head, do_post,
+ max_headers, max_body, do_multiline, only_v4, only_v6,
+ do_summary, headers_size, no_body, do_append, do_http10, gerr,
+ out_file_needs_closing;
+ char *url, *host, *port, *hostport, *path, *filelist, *p, *check;
+ char *post_dir, *post_file, *output_file, *post_footer, *post_header,
+ *A_arg, *store_headers, *store_body;
+ FILE *tcp_file, *out_file;
+ struct timeval tv_start, tv_end;
+ struct stat sbF, sbH, sbS;
+ off_t cLength, dir_length;
+ struct sigaction sa;
+ char rndbuf[16];
+
+ /* Arguments */
+ do_http10= 0;
+ do_append= 0;
+ do_get= 1;
+ do_head= 0;
+ do_post= 0;
+ post_dir= NULL;
+ post_file= NULL;
+ post_footer=NULL;
+ post_header=NULL;
+ output_file= NULL;
+ time_tolerance= NULL;
+ store_headers= NULL;
+ store_body= NULL;
+ A_arg= NULL;
+ only_v4= 0;
+ only_v6= 0;
+ do_summary= 0;
+
+ /* Used in cleanup */
+ fd= -1;
+ fdH= -1;
+ fdF= -1;
+ fdS= -1;
+ tcp_fd= -1;
+ tcp_file= NULL;
+ out_file= NULL;
+ out_file_needs_closing= 0;
+ host= NULL;
+ port= NULL;
+ hostport= NULL;
+ path= NULL;
+ filelist= NULL;
+
+ /* Others */
+ do_multiline= 0;
+ http_result= -1;
+ dir_length= 0;
+ headers_size= 0;
+
+ /* Allow us to be called directly by another program in busybox */
+ optind= 0;
+ while (c= getopt_long(argc, argv, "01A:O:46?", longopts, NULL), c != -1)
+ {
+ switch(c)
+ {
+ case '0':
+ do_http10= 1;
+ break;
+ case '1':
+ do_http10= 0;
+ break;
+ case 'a': /* --append */
+ do_append= 1;
+ break;
+ case 'A':
+ A_arg= optarg;
+ break;
+ case 'O':
+ output_file= optarg;
+ 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;
+ break;
+ case 'P': /* --post */
+ do_get = 0;
+ do_head = 0;
+ do_post = 1;
+ break;
+ case 'D':
+ post_dir = optarg; /* --post-dir */
+ break;
+ case 'h': /* --post-header */
+ post_header= optarg;
+ break;
+ case 'f': /* --post-footer */
+ post_footer= optarg;
+ break;
+
+ case 'p': /* --post-file */
+ post_file= optarg;
+ break;
+ case 's': /* --set-time */
+ time_tolerance= optarg;
+ break;
+ case 'H': /* --store-headers */
+ store_headers= optarg;
+ break;
+ case 'B': /* --store-body */
+ store_body= optarg;
+ break;
+ case 'S': /* --summary */
+ do_summary= 1;
+ break;
+ case '4':
+ only_v4= 1;
+ only_v6= 0;
+ break;
+ case '6':
+ only_v6= 1;
+ only_v4= 0;
+ break;
+ case 'u': /* --user-agent */
+ user_agent= optarg;
+ break;
+ case '?':
+ bb_show_usage();
+ return 1;
+ default:
+ fatal("bad option '%c'", c);
+ }
+ }
+
+ if (optind != argc-1)
+ fatal("exactly one url expected");
+ url= argv[optind];
+
+ max_headers= 0;
+ max_body= UINT_MAX; /* default is to write out the entire body */
+ if (do_summary)
+ max_body= 0; /* default to no body if we want a summary */
+
+ if (store_headers)
+ {
+ max_headers= strtoul(store_headers, &check, 10);
+ if (check[0] != '\0')
+ {
+ report("unable to parse argument '%s'", store_headers);
+ return 1;
+ }
+ }
+
+ if (store_body)
+ {
+ max_body= strtoul(store_body, &check, 10);
+ if (check[0] != '\0')
+ {
+ report("unable to parse argument '%s'", store_body);
+ return 1;
+ }
+ }
+
+ if (!parse_url(url, &host, &port, &hostport, &path))
+ {
+ goto err;
+ }
+
+ //printf("host: %s\n", host);
+ //printf("port: %s\n", port);
+ //printf("hostport: %s\n", hostport);
+ //printf("path: %s\n", path);
+
+ if (post_dir)
+ {
+ filelist= do_dir(post_dir, &dir_length);
+ if (!filelist)
+ {
+ /* Something went wrong. */
+ goto err;
+ }
+ if (debug)
+ {
+ fprintf(stderr, "total size in dir: %ld\n",
+ (long)dir_length);
+ }
+ }
+
+ if(post_header != NULL )
+ {
+ if (!validate_filename(post_header, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", post_header);
+ goto err;
+ }
+ fdH = open(post_header, O_RDONLY);
+ if(fdH == -1 )
+ {
+ report_err("unable to open header '%s'", post_header);
+ goto err;
+ }
+ if (fstat(fdH, &sbH) == -1)
+ {
+ report_err("fstat failed on header file '%s'",
+ post_header);
+ goto err;
+ }
+ if (!S_ISREG(sbH.st_mode))
+ {
+ report("'%s' header is not a regular file",
+ post_header);
+ goto err;
+ }
+ }
+
+ if(post_footer != NULL )
+ {
+ if (!validate_filename(post_footer, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", post_footer);
+ goto err;
+ }
+ fdF = open(post_footer, O_RDONLY);
+ if(fdF == -1 )
+ {
+ report_err("unable to open footer '%s'", post_footer);
+ goto err;
+ }
+ if (fstat(fdF, &sbF) == -1)
+ {
+ report_err("fstat failed on footer file '%s'",
+ post_footer);
+ goto err;
+ }
+ if (!S_ISREG(sbF.st_mode))
+ {
+ report("'%s' footer is not a regular file",
+ post_footer);
+ goto err;
+ }
+ }
+
+ /* Try to open the file before trying to connect */
+ if (post_file != NULL)
+ {
+ if (!validate_filename(post_file, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", post_file);
+ goto err;
+ }
+ fdS= open(post_file, O_RDONLY);
+ if (fdS == -1)
+ {
+ report_err("unable to open '%s'", post_file);
+ goto err;
+ }
+ if (fstat(fdS, &sbS) == -1)
+ {
+ report_err("fstat failed");
+ goto err;
+ }
+ if (!S_ISREG(sbS.st_mode))
+ {
+ report("'%s' is not a regular file", post_file);
+ goto err;
+ }
+ }
+
+ sa.sa_flags= 0;
+ sa.sa_handler= got_alarm;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ if (debug) fprintf(stderr, "setting alarm\n");
+ alarm(10);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (output_file)
+ {
+ if (!validate_filename(output_file, SAFE_PREFIX_NEW))
+ {
+ report("insecure output file '%s'", output_file);
+ goto err;
+ }
+ out_file= fopen(output_file, do_append ? "a" : "w");
+ if (!out_file)
+ {
+ report_err("unable to create '%s'", output_file);
+ goto err;
+ }
+ out_file_needs_closing= 1;
+ }
+ else
+ out_file= stdout;
+
+
+ tcp_fd= connect_to_name(host, port, only_v4, only_v6, &tv_start,
+ &gerr);
+ if (tcp_fd == -1)
+ {
+ int s_errno= errno;
+
+ if (A_arg && do_summary)
+ {
+ fprintf(out_file, "%s %ld ",
+ A_arg, (long)time(NULL));
+ if (gerr != 0)
+ {
+ fprintf(out_file, "bad-hostname %s\n",
+ gai_strerror(gerr));
+ }
+ else
+ {
+ fprintf(out_file, "connect error %d\n",
+ s_errno);
+ }
+ }
+ report("unable to connect to '%s'", host);
+ goto err;
+ }
+
+ /* Stdio makes life easy */
+ tcp_file= fdopen(tcp_fd, "r+");
+ if (tcp_file == NULL)
+ {
+ report("fdopen failed");
+ goto err;
+ }
+ tcp_fd= -1;
+
+ if (debug) fprintf(stderr, "httpget: sending request\n");
+ fprintf(tcp_file, "%s %s HTTP/1.%c\r\n",
+ do_get ? "GET" : do_head ? "HEAD" : "POST", path,
+ do_http10 ? '0' : '1');
+ fprintf(tcp_file, "Host: %s\r\n", host);
+ fprintf(tcp_file, "Connection: close\r\n");
+ fprintf(tcp_file, "User-Agent: %s\r\n", user_agent);
+ if (do_post)
+ {
+ fprintf(tcp_file,
+ "Content-Type: application/x-www-form-urlencoded\r\n");
+ }
+
+ cLength= 0;
+ if( post_header != NULL )
+ cLength += sbH.st_size;
+
+ if (post_file)
+ cLength += sbS.st_size;
+
+ if (post_dir)
+ cLength += dir_length;
+
+ if( post_footer != NULL )
+ cLength += sbF.st_size;
+
+ fprintf(tcp_file, "Content-Length: %lu\r\n", (unsigned long)cLength);
+ fprintf(tcp_file, "\r\n");
+
+ if( post_header != NULL )
+ {
+ if (!write_to_tcp_fd(fdH, tcp_file))
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+
+ if (post_file != NULL)
+ {
+ if (!write_to_tcp_fd(fdS, tcp_file))
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+
+ if (post_dir)
+ {
+ for (p= filelist; p[0] != 0; p += strlen(p)+1)
+ {
+ if (debug) fprintf(stderr, "posting file '%s'\n", p);
+ if (!validate_filename(p, SAFE_PREFIX_OUT))
+ {
+ report("insecure file '%s'", p);
+ goto err;
+ }
+ fd= open(p, O_RDONLY);
+ if (fd == -1)
+ {
+ report_err("unable to open '%s'", p);
+ goto err;
+ }
+ r= write_to_tcp_fd(fd, tcp_file);
+ close(fd);
+ fd= -1;
+ if (!r)
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+ }
+
+ if( post_footer != NULL)
+ {
+ if (!write_to_tcp_fd(fdF, tcp_file))
+ {
+ printf("write_to_tcp_fd failed\n");
+ goto fail;
+ }
+ }
+
+ if (debug) fprintf(stderr, "httpget: writing output\n");
+ do_multiline= (A_arg && (max_headers != 0 || max_body != 0));
+ if (do_multiline)
+ {
+ fd= open("/dev/urandom", O_RDONLY);
+ read(fd, rndbuf, sizeof(rndbuf));
+ close(fd);
+ fprintf(out_file, "BEGINRESULT ");
+ for (i= 0; i<sizeof(rndbuf); i++)
+ fprintf(out_file, "%02x", (unsigned char)rndbuf[i]);
+ fprintf(out_file, " %s %ld\n", A_arg, (long)time(NULL));
+ }
+
+ if (debug) fprintf(stderr, "httpget: getting result\n");
+ if (!check_result(tcp_file, &http_result))
+ {
+ printf("check_result failed\n");
+ goto fail;
+ }
+ if (debug) fprintf(stderr, "httpget: getting reply headers \n");
+ if (!eat_headers(tcp_file, &chunked, &content_length, &headers_size,
+ out_file, max_headers))
+ {
+ printf("eat_headers failed\n");
+ goto fail;
+ }
+
+ no_body= (do_head || http_result == 204 || http_result == 304 ||
+ http_result/100 == 1);
+
+ if (max_headers != 0 && max_body != 0)
+ fprintf(out_file, "\n"); /* separate headers from body */
+
+ if (no_body)
+ {
+ /* This reply will not have a body even if there is a
+ * content-length line.
+ */
+ }
+ else if (chunked)
+ {
+ if (!copy_chunked(tcp_file, out_file, &content_length,
+ max_body))
+ {
+ printf("copy_chunked failed\n");
+ goto fail;
+ }
+ }
+ else
+ {
+ if (!copy_bytes(tcp_file, out_file, &content_length, max_body))
+ {
+ printf("copy_bytes failed\n");
+ goto fail;
+ }
+ }
+
+fail:
+ gettimeofday(&tv_end, NULL);
+
+ tv_end.tv_sec -= tv_start.tv_sec;
+ tv_end.tv_usec -= tv_start.tv_usec;
+ if (tv_end.tv_usec < 0)
+ {
+ tv_end.tv_usec += 1000000;
+ tv_end.tv_sec--;
+ }
+
+ if (do_multiline)
+ {
+ fprintf(out_file, "ENDRESULT ");
+ for (i= 0; i<sizeof(rndbuf); i++)
+ fprintf(out_file, "%02x", (unsigned char)rndbuf[i]);
+ fprintf(out_file, "\n");
+ }
+
+ if (A_arg && do_summary)
+ {
+ fprintf(out_file, "%s %ld ",
+ A_arg, (long)time(NULL));
+ }
+ if (do_summary)
+ {
+ const char *v, *cmd;
+
+ if (do_get)
+ cmd= "GET";
+ else if (do_head)
+ cmd= "HEAD";
+ else
+ cmd= "POST";
+ if (family == AF_INET)
+ v= "4";
+ else if (family == AF_INET6)
+ v= "6";
+ else
+ v= "?";
+
+ fprintf(out_file, "%s%s %s %d.%06d %03u %d %d\n",
+ cmd, v,
+ host_addr, (int)tv_end.tv_sec, (int)tv_end.tv_usec,
+ http_result, headers_size, content_length);
+ }
+
+ if (debug) fprintf(stderr, "httpget: done\n");
+
+ result= 0;
+
+leave:
+ if (fdH != -1) close(fdH);
+ if (fdF != -1) close(fdF);
+ if (fdS != -1) close(fdS);
+ if (fd != -1) close(fd);
+ if (tcp_file) fclose(tcp_file);
+ if (tcp_fd != -1) close(tcp_fd);
+ if (out_file && out_file_needs_closing) fclose(out_file);
+ if (host) free(host);
+ if (port) free(port);
+ if (hostport) free(hostport);
+ if (path) free(path);
+ if (filelist) free(filelist);
+
+ printf("clearing alarm\n");
+ alarm(0);
+ signal(SIGPIPE, SIG_DFL);
+
+ return result;
+
+err:
+ result= 1;
+ goto leave;
+}
+
+static int write_to_tcp_fd (int fd, FILE *tcp_file)
+{
+ int r;
+ /* Copy file */
+ while(r= read(fd, buffer, sizeof(buffer)), r > 0)
+ {
+ if (fwrite(buffer, r, 1, tcp_file) != 1)
+ {
+ report_err("error writing to tcp connection");
+ return 0;
+ }
+ }
+ if (r == -1)
+ fatal_err("error reading from file");
+ return 1;
+}
+
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp)
+{
+ char *item;
+ const char *cp, *np, *prefix;
+ size_t len;
+
+ *hostp= NULL;
+ *portp= NULL;
+ *hostportp= NULL;
+ *pathp= NULL;
+
+ /* the url must start with 'http://' */
+ prefix= "http://";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, url, len) != 0)
+ {
+ report("bad prefix in url '%s'", url);
+ goto fail;
+ }
+
+ cp= url+len;
+
+ /* Get hostport part */
+ np= strchr(cp, '/');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ report("missing host part in url '%s'", url);
+ return 0;
+ }
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *hostportp= item;
+
+ /* The remainder is the path */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "/";
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *pathp= item;
+
+ /* Extract the host name from hostport */
+ cp= *hostportp;
+ np= cp;
+ if (cp[0] == '[')
+ {
+ /* IPv6 address literal */
+ np= strchr(cp, ']');
+ if (np == NULL || np == cp+1)
+ {
+ report("malformed IPv6 address literal in url '%s'",
+ url);
+ goto fail;
+ }
+ }
+
+ np= strchr(np, ':');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ report("missing host part in url '%s'", url);
+ goto fail;
+ }
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ if (cp[0] == '[')
+ {
+ /* Leave out the square brackets */
+ memcpy(item, cp+1, len-2);
+ item[len-2]= '\0';
+ }
+ else
+ {
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ }
+ *hostp= item;
+
+ /* Port */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "80";
+ else
+ cp++;
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *portp= item;
+
+ return 1;
+
+fail:
+ if (*hostp)
+ {
+ free(*hostp);
+ *hostp= NULL;
+ }
+ if (*portp)
+ {
+ free(*portp);
+ *portp= NULL;
+ }
+ if (*hostportp)
+ {
+ free(*hostportp);
+ *hostportp= NULL;
+ }
+ if (*pathp)
+ {
+ free(*pathp);
+ *pathp= NULL;
+ }
+ return 0;
+}
+
+static int check_result(FILE *tcp_file, int *result)
+{
+ int major, minor;
+ size_t len;
+ char *cp, *check, *line;
+ const char *prefix;
+
+ *result= -1; /* Signal error actually getting a result */
+
+ if (fgets(buffer, sizeof(buffer), tcp_file) == NULL)
+ {
+ if (feof(tcp_file))
+ report("got unexpected EOF from server");
+ else
+ report_err("error reading from server");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ /* Check http version */
+ prefix= "http/";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, line, len) != 0)
+ {
+ report("bad prefix in response '%s'", line);
+ return 0;
+ }
+ cp= line+len;
+ major= strtoul(cp, &check, 10);
+ if (check == cp || check[0] != '.')
+ {
+ report("bad major version in response '%s'", line);
+ return 0;
+ }
+ cp= check+1;
+ minor= strtoul(cp, &check, 10);
+ if (check == cp || check[0] == '\0' ||
+ !isspace(*(unsigned char *)check))
+ {
+ report("bad major version in response '%s'", line);
+ return 0;
+ }
+
+ skip_spaces(check, &cp);
+
+ if (!isdigit(*(unsigned char *)cp))
+ {
+ report("bad status code in response '%s'", line);
+ return 0;
+ }
+ *result= strtoul(cp, NULL, 10);
+
+ return 1;
+}
+
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length,
+ int *headers_size, FILE *out_file, int max_headers)
+{
+ int tot_headers;
+ char *line, *cp, *ncp, *check;
+ size_t len;
+ const char *kw;
+
+ *chunked= 0;
+ *content_length= -1;
+ tot_headers= 0;
+ *headers_size= 0;
+ while (fgets(buffer, sizeof(buffer), tcp_file) != NULL)
+ {
+ line= buffer;
+ len=strlen(line);
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (line[0] == '\0')
+ return 1; /* End of headers */
+
+ *headers_size += len;
+
+ if (debug) fprintf(stderr, "httpget: got line '%s'\n", line);
+
+ len= strlen(line);
+ if (tot_headers+len+1 <= max_headers)
+ {
+ fprintf(out_file, "%s\n", line);
+ tot_headers += len+1;
+ } else if (tot_headers <= max_headers && max_headers != 0)
+ {
+ /* Fill up remaining space and report truncation */
+ if (tot_headers < max_headers)
+ {
+ fprintf(out_file, "%.*s\n", max_headers-tot_headers,
+ line);
+ }
+ fprintf(out_file, "[...]\n");
+
+ tot_headers += len+1;
+ }
+
+ if (time_tolerance && strncmp(line, "Date: ", 6) == 0)
+ {
+ /* Try to set time from server */
+ time_t now, tim, tolerance;
+ struct tm tm;
+
+ tolerance= strtoul(time_tolerance, &cp, 10);
+ if (cp[0] != '\0')
+ {
+ fatal("unable to parse tolerance '%s'",
+ time_tolerance);
+ }
+ cp= strptime(line+6, "%a, %d %b %Y %H:%M:%S ", &tm);
+ if (!cp || strcmp(cp, "GMT") != 0)
+ {
+ if (debug)
+ {
+ fprintf(stderr,
+ "unable to parse time '%s'\n",
+ line+6);
+ }
+ }
+ tim= timegm(&tm);
+ now= time(NULL);
+ if (now < tim-tolerance || now > tim+tolerance)
+ {
+ if (debug)
+ { fprintf(stderr,
+ "setting time, time difference is %d\n",
+ (int)(tim-now));
+ }
+ stime(&tim);
+ }
+ }
+
+
+ cp= line;
+ skip_spaces(cp, &ncp);
+ if (ncp != line)
+ continue; /* Continuation line */
+
+ cp= ncp;
+ while (ncp[0] != '\0' && ncp[0] != ':' &&
+ !isspace((unsigned char)ncp[0]))
+ {
+ ncp++;
+ }
+
+ kw= "Transfer-Encoding";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) == 0)
+ {
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ kw= "chunked";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+ /* make sure we have end of line or white space */
+ if (cp[len] != '\0' && isspace((unsigned char)cp[len]))
+ continue;
+ *chunked= 1;
+ continue;
+ }
+
+ kw= "Content-length";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ *content_length= strtoul(cp, &check, 10);
+ if (check == cp)
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+
+ /* And after that we should have just white space */
+ cp= check;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != '\0')
+ {
+ report("malformed content-length header", line);
+ return 0;
+ }
+ }
+ if (feof(tcp_file))
+ report("got unexpected EOF from server");
+ else
+ report_err("error reading from server");
+ return 0;
+}
+
+static int connect_to_name(char *host, char *port, int only_v4, int only_v6,
+ struct timeval *start_time, int *gerr)
+{
+ int r, s, s_errno;
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+
+ if (debug) fprintf(stderr, "httpget: before getaddrinfo\n");
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_STREAM;
+ if (only_v4)
+ hints.ai_family= AF_INET;
+ if (only_v6)
+ hints.ai_family= AF_INET6;
+ r= getaddrinfo(host, port, &hints, &res);
+ *gerr= r;
+ if (r != 0)
+ {
+ report("unable to resolve '%s': %s", host, gai_strerror(r));
+ return -1;
+ }
+
+ s_errno= 0;
+ s= -1;
+ for (aip= res; aip != NULL; aip= aip->ai_next)
+ {
+ family= res->ai_family;
+ getnameinfo(res->ai_addr, res->ai_addrlen, host_addr,
+ sizeof(host_addr), NULL, 0, NI_NUMERICHOST);
+
+ gettimeofday(start_time, NULL);
+ s= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1)
+ {
+ s_errno= errno;
+ continue;
+ }
+
+ if (debug) fprintf(stderr, "httpget: before connect\n");
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+ s_errno= errno;
+ close(s);
+ s= -1;
+ }
+
+ freeaddrinfo(res);
+ if (s == -1)
+ errno= s_errno;
+ return s;
+}
+
+char *do_dir(char *dir_name, off_t *lenp)
+{
+ size_t currsize, allocsize, dirlen, len;
+ char *list, *tmplist, *path;
+ DIR *dir;
+ struct dirent *de;
+ struct stat sb;
+
+ /* Scan a directory for files. Return the filenames asa list of
+ * strings. An empty string terminates the list. Also compute the
+ * total size of the files
+ */
+ *lenp= 0;
+ currsize= 0;
+ allocsize= 0;
+ list= NULL;
+ dir= opendir(dir_name);
+ if (dir == NULL)
+ {
+ report_err("opendir failed for '%s'", dir_name);
+ return NULL;
+ }
+
+ dirlen= strlen(dir_name);
+ while (de= readdir(dir), de != NULL)
+ {
+ /* Concat dir and entry */
+ len= dirlen + 1 + strlen(de->d_name) + 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes",
+ allocsize);
+ closedir(dir);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ strlcpy(path, dir_name, allocsize-currsize);
+ strlcat(path, "/", allocsize-currsize);
+ strlcat(path, de->d_name, allocsize-currsize);
+
+ if (stat(path, &sb) != 0)
+ {
+ report_err("stat '%s' failed", path);
+ free(list);
+ closedir(dir);
+ return NULL;
+ }
+
+ if (!S_ISREG(sb.st_mode))
+ continue; /* Just skip entry */
+
+ currsize += len;
+ *lenp += sb.st_size;
+ }
+ closedir(dir);
+
+ /* Add empty string to terminate the list */
+ len= 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes", allocsize);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ *path= '\0';
+
+ return list;
+}
+
+static int copy_chunked(FILE *in_file, FILE *out_file, int *length, int max_body)
+{
+ int need_nl;
+ size_t len, offset, size, tot_body;
+ char *cp, *line, *check;
+
+ *length= 0;
+ need_nl= 0;
+ tot_body= 0;
+ for (;;)
+ {
+ /* Get a chunk size */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (debug)
+ {
+ fprintf(stderr, "httpget: got chunk line '%s'\n", line);
+ }
+ len= strtoul(line, &check, 16);
+ if (check[0] != '\0' && !isspace(*(unsigned char *)check))
+ {
+ report("bad chunk line '%s'", line);
+ return 0;
+ }
+ if (!len)
+ break;
+
+ *length += len;
+
+ offset= 0;
+
+ while (offset < len)
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ if (tot_body+size <= max_body)
+ {
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ fatal_err("error writing output");
+ need_nl= (buffer[size-1] != '\n');
+ tot_body += len;
+ } else if (tot_body <= max_body && max_body != 0)
+ {
+ /* Fill up remaining space and report truncation */
+ if (tot_body < max_body)
+ {
+ if (fwrite(buffer, max_body-tot_body, 1,
+ out_file) != 1)
+ {
+ fatal_err(
+ "error writing output");
+ }
+ }
+ fprintf(out_file, "\n[...]\n");
+ need_nl= 0;
+ tot_body += len;
+ }
+
+ offset += size;
+ }
+
+ /* Expect empty line after data */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] != '\0')
+ {
+ report("Garbage after chunk data");
+ return 0;
+ }
+ }
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+
+ for (;;)
+ {
+ /* Get an end-of-chunk line */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ report("line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] == '\0')
+ break;
+
+ if (debug)
+ {
+ fprintf(stderr,
+ "httpget: got end-of-chunk line '%s'\n", line);
+ }
+ }
+ return 1;
+}
+
+static int copy_bytes(FILE *in_file, FILE *out_file, int *length,
+ int max_body)
+{
+ int len, need_nl;
+ size_t offset, size;
+
+ offset= 0;
+
+ need_nl= 0;
+ len= *length;
+ while (len == -1 || offset < len)
+ {
+ if (len == -1)
+ {
+ size= sizeof(buffer);
+
+ size= fread(buffer, 1, sizeof(buffer), in_file);
+ if (size == 0)
+ {
+ if (feof(in_file))
+ break; /* Got EOF */
+ report_err("error reading input");
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+ return 0;
+ }
+ }
+ else
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report_err("error reading input");
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+ return 0;
+ }
+ }
+
+ if (offset+size <= max_body)
+ {
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ fatal_err("error writing output");
+ need_nl= (buffer[size-1] != '\n');
+ }
+ else if (offset <= max_body && max_body != 0)
+ {
+ /* Fill up remaining space and report truncation */
+ if (offset < max_body)
+ {
+ if (fwrite(buffer, max_body-offset, 1,
+ out_file) != 1)
+ {
+ fatal_err("error writing output");
+ }
+ }
+ fprintf(out_file, "\n[...]\n");
+ need_nl= 0;
+ }
+
+ offset += size;
+ }
+
+ if (max_body && need_nl)
+ fprintf(out_file, "\n");
+ if (len == -1)
+ *length= offset;
+ return 1;
+}
+
+static void skip_spaces(const char *cp, char **ncp)
+{
+ const unsigned char *ucp;
+
+ ucp= (const unsigned char *)cp;
+ while (ucp[0] != '\0' && isspace(ucp[0]))
+ ucp++;
+ *ncp= (char *)ucp;
+}
+
+static void got_alarm(int sig __attribute__((unused)) )
+{
+ // printf("got alarm\n");
+ // printf("switching tcp_fd to nonblocking\n");
+ if (tcp_fd != -1)
+ fcntl(tcp_fd, F_SETFL, fcntl(tcp_fd, F_GETFL) | O_NONBLOCK);
+ //printf("setting alarm again\n");
+ alarm(1);
+}
+
+static void usage(void)
+{
+ fprintf(stderr,
+"Usage: httpget\n");
+ fprintf(stderr,
+" [--post-header <file-to-post>] [--post-file <file-to-post>]\n");
+ fprintf(stderr,
+" [--post-footer <file-to-post>] \n");
+
+ fprintf(stderr,
+" [--post-footer <file-to-post>] [-O <output-file>] <url>\n");
+ exit(1);
+}
+
+static void fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+static void fatal_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+
+ exit(1);
+}
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+static void report_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httpget: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+}
diff --git a/networking/httppost.c b/networking/httppost.c
new file mode 100644
index 0000000..26934ec
--- /dev/null
+++ b/networking/httppost.c
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (c) 2011-2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ * httppost.c -- Simple program that uses the HTTP POST command
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "libbb.h"
+
+#define SAFE_PREFIX_DATA_OUT ATLAS_DATA_OUT
+#define SAFE_PREFIX_DATA_OOQ_OUT ATLAS_DATA_OOQ_OUT
+#define SAFE_PREFIX_DATA_NEW ATLAS_DATA_NEW
+#define SAFE_PREFIX_STATUS ATLAS_STATUS
+
+struct option longopts[]=
+{
+ { "delete-file", no_argument, NULL, 'd' },
+ { "maxpostsize", required_argument, NULL, 'm' },
+ { "post-file", required_argument, NULL, 'p' },
+ { "post-dir", required_argument, NULL, 'D' },
+ { "post-header", required_argument, NULL, 'h' },
+ { "post-footer", required_argument, NULL, 'f' },
+ { "set-time", required_argument, NULL, 's' },
+ { "timeout", required_argument, NULL, 't' },
+ { NULL, }
+};
+
+static int tcp_fd;
+static struct timeval start_time;
+static time_t timeout = 300;
+
+/* Result sent by controller when input is acceptable. */
+#define OK_STR "OK\n"
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp);
+static int check_result(FILE *tcp_file);
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length, time_t *timep);
+static int connect_to_name(char *host, char *port);
+char *do_dir(char *dir_name, off_t curr_size, off_t max_size, off_t *lenp);
+static int copy_chunked(FILE *in_file, FILE *out_file, int *found_okp);
+static int copy_bytes(FILE *in_file, FILE *out_file, size_t len,
+ int *found_okp);
+static void fatal(const char *fmt, ...);
+// static void fatal_err(const char *fmt, ...);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+static int write_to_tcp_fd (int fd, FILE *tcp_file);
+static void skip_spaces(const char *cp, char **ncp);
+static void got_alarm(int sig);
+static void kick_watchdog(void);
+
+int httppost_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int httppost_main(int argc, char *argv[])
+{
+ int c, r, fd, fdF, fdH, fdS, chunked, content_length, result;
+ int opt_delete_file, found_ok;
+ char *url, *host, *port, *hostport, *path, *filelist, *p, *check;
+ char *post_dir, *post_file, *atlas_id, *output_file,
+ *post_footer, *post_header, *maxpostsizestr, *timeoutstr;
+ char *time_tolerance;
+ FILE *tcp_file, *out_file, *fh;
+ time_t server_time, tolerance;
+ struct stat sbF, sbH, sbS;
+ off_t cLength, dir_length, maxpostsize;
+ struct sigaction sa;
+
+ post_dir= NULL;
+ post_file= NULL;
+ post_footer=NULL;
+ post_header=NULL;
+ atlas_id= NULL;
+ output_file= NULL;
+ opt_delete_file = 0;
+ time_tolerance = NULL;
+ maxpostsizestr= NULL;
+ timeoutstr= NULL;
+
+ fd= -1;
+ fdH= -1;
+ fdF= -1;
+ fdS= -1;
+ tcp_fd= -1;
+ tcp_file= NULL;
+ out_file= NULL;
+ host= NULL;
+ port= NULL;
+ hostport= NULL;
+ path= NULL;
+ filelist= NULL;
+ maxpostsize= 1000000;
+
+ /* Allow us to be called directly by another program in busybox */
+ optind= 0;
+ while (c= getopt_long(argc, argv, "A:O:?", longopts, NULL), c != -1)
+ {
+ switch(c)
+ {
+ case 'A':
+ atlas_id= optarg;
+ break;
+ case 'O':
+ output_file= optarg;
+ break;
+ case 'd':
+ opt_delete_file = 1;
+ break;
+ case 'D':
+ post_dir = optarg; /* --post-dir */
+ break;
+ case 'h': /* --post-header */
+ post_header= optarg;
+ break;
+ case 'f': /* --post-footer */
+ post_footer= optarg;
+ break;
+ case 'm': /* --maxpostsize */
+ maxpostsizestr= optarg;
+ break;
+ case 'p': /* --post-file */
+ post_file= optarg;
+ break;
+ case 's': /* --set-time */
+ time_tolerance= optarg;
+ break;
+ case 't': /* --timeout */
+ timeoutstr= optarg;
+ break;
+ case '?':
+ fprintf(stderr, "bad option\n");
+ return 1;
+ default:
+ fatal("bad option '%c'", c);
+ }
+ }
+
+ if (optind != argc-1)
+ {
+ fprintf(stderr, "exactly one url expected\n");
+ return 1;
+ }
+ url= argv[optind];
+
+ if (maxpostsizestr)
+ {
+ maxpostsize= strtoul(maxpostsizestr, &check, 0);
+ if (check[0] != 0)
+ {
+ report("unable to parse maxpostsize '%s'",
+ maxpostsizestr);
+ goto err;
+ }
+ }
+
+ if (timeoutstr)
+ {
+ timeout= strtoul(timeoutstr, &check, 0);
+ if (check[0] != 0)
+ {
+ report("unable to parse timeout '%s'",
+ timeoutstr);
+ goto err;
+ }
+ }
+
+ tolerance= 0;
+ if (time_tolerance)
+ {
+ tolerance= strtoul(time_tolerance, &p, 10);
+ if (p[0] != '\0')
+ {
+ fprintf(stderr, "unable to parse tolerance '%s'\n",
+ time_tolerance);
+ return 1;
+ }
+ }
+
+ if (parse_url(url, &host, &port, &hostport, &path) == -1)
+ return 1;
+
+ //printf("host: %s\n", host);
+ //printf("port: %s\n", port);
+ //printf("hostport: %s\n", hostport);
+ //printf("path: %s\n", path);
+
+ cLength= 0;
+
+ if(post_header != NULL )
+ {
+ if (!validate_filename(post_header, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(post_header, SAFE_PREFIX_STATUS))
+ {
+ report("protected file (for header) '%s'", post_header);
+ goto err;
+ }
+ fdH = open(post_header, O_RDONLY);
+ if(fdH == -1 )
+ {
+ report_err("unable to open header '%s'", post_header);
+ goto err;
+ }
+ if (fstat(fdH, &sbH) == -1)
+ {
+ report_err("fstat failed on header file '%s'",
+ post_header);
+ goto err;
+ }
+ if (!S_ISREG(sbH.st_mode))
+ {
+ report("'%s' header is not a regular file",
+ post_header);
+ goto err;
+ }
+ cLength += sbH.st_size;
+ }
+
+ if(post_footer != NULL )
+ {
+ if (!validate_filename(post_footer, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(post_footer, SAFE_PREFIX_STATUS))
+ {
+ report("pretected file (for footer) '%s'", post_footer);
+ goto err;
+ }
+ fdF = open(post_footer, O_RDONLY);
+ if(fdF == -1 )
+ {
+ report_err("unable to open footer '%s'", post_footer);
+ goto err;
+ }
+ if (fstat(fdF, &sbF) == -1)
+ {
+ report_err("fstat failed on footer file '%s'",
+ post_footer);
+ goto err;
+ }
+ if (!S_ISREG(sbF.st_mode))
+ {
+ report("'%s' footer is not a regular file",
+ post_footer);
+ goto err;
+ }
+ cLength += sbF.st_size;
+ }
+
+ /* Try to open the file before trying to connect */
+ if (post_file != NULL)
+ {
+ if (!validate_filename(post_file, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(post_file, SAFE_PREFIX_STATUS))
+ {
+ report("protected file (post) '%s'", post_file);
+ goto err;
+ }
+ fdS= open(post_file, O_RDONLY);
+ if (fdS == -1)
+ {
+ report_err("unable to open '%s'", post_file);
+ goto err;
+ }
+ if (fstat(fdS, &sbS) == -1)
+ {
+ report_err("fstat failed");
+ goto err;
+ }
+ if (!S_ISREG(sbS.st_mode))
+ {
+ report("'%s' is not a regular file", post_file);
+ goto err;
+ }
+ cLength += sbS.st_size;
+ }
+
+ if (post_dir)
+ {
+ filelist= do_dir(post_dir, cLength, maxpostsize, &dir_length);
+ if (!filelist)
+ {
+ /* Something went wrong. */
+ goto err;
+ }
+ fprintf(stderr, "total size in dir: %ld\n", (long)dir_length);
+ cLength += dir_length;
+ }
+
+ gettimeofday(&start_time, NULL);
+
+ sa.sa_flags= 0;
+ sa.sa_handler= got_alarm;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGALRM, &sa, NULL);
+ alarm(10);
+ signal(SIGPIPE, SIG_IGN);
+
+ tcp_fd= connect_to_name(host, port);
+ if (tcp_fd == -1)
+ {
+ report_err("unable to connect to '%s'", host);
+ goto err;
+ }
+
+ /* Stdio makes life easy */
+ tcp_file= fdopen(tcp_fd, "r+");
+ if (tcp_file == NULL)
+ {
+ report("fdopen failed");
+ goto err;
+ }
+
+ fprintf(stderr, "httppost: sending request\n");
+ fprintf(tcp_file, "POST %s HTTP/1.1\r\n", path);
+ //fprintf(tcp_file, "GET %s HTTP/1.1\r\n", path);
+ fprintf(tcp_file, "Host: %s\r\n", host);
+ fprintf(tcp_file, "Connection: close\r\n");
+ fprintf(tcp_file, "User-Agent: httppost for atlas.ripe.net\r\n");
+ fprintf(tcp_file,
+ "Content-Type: application/x-www-form-urlencoded\r\n");
+
+ cLength= 0;
+ if( post_header != NULL )
+ cLength += sbH.st_size;
+
+ if (post_file)
+ cLength += sbS.st_size;
+
+ if (post_dir)
+ cLength += dir_length;
+
+ if( post_footer != NULL )
+ cLength += sbF.st_size;
+
+ fprintf(tcp_file, "Content-Length: %lu\r\n", (unsigned long)cLength);
+ fprintf(tcp_file, "\r\n");
+
+ if( post_header != NULL )
+ {
+ if (!write_to_tcp_fd(fdH, tcp_file))
+ goto err;
+ }
+
+ if (post_file != NULL)
+ {
+ if (!write_to_tcp_fd(fdS, tcp_file))
+ goto err;
+ }
+
+ if (post_dir)
+ {
+ for (p= filelist; p[0] != 0; p += strlen(p)+1)
+ {
+ fprintf(stderr, "posting file '%s'\n", p);
+ if (!validate_filename(p, SAFE_PREFIX_DATA_OUT) &&
+ !validate_filename(p, SAFE_PREFIX_DATA_OOQ_OUT))
+ {
+ report("protected file (post dir) '%s'", p);
+ goto err;
+ }
+ fd= open(p, O_RDONLY);
+ if (fd == -1)
+ {
+ report_err("unable to open '%s'", p);
+ goto err;
+ }
+ r= write_to_tcp_fd(fd, tcp_file);
+ close(fd);
+ fd= -1;
+ if (!r)
+ goto err;
+ }
+ }
+
+ if( post_footer != NULL)
+ {
+ if (!write_to_tcp_fd(fdF, tcp_file))
+ goto err;
+ }
+
+ fprintf(stderr, "httppost: getting result\n");
+ if (!check_result(tcp_file))
+ goto err;
+ fprintf(stderr, "httppost: getting reply headers \n");
+ server_time= 0;
+ if (!eat_headers(tcp_file, &chunked, &content_length, &server_time))
+ goto err;
+
+ if (tolerance && server_time > 0)
+ {
+ /* Try to set time from server */
+ struct timeval now;
+ double rtt;
+
+ gettimeofday(&now, NULL);
+ rtt= now.tv_sec-start_time.tv_sec;
+ rtt += (now.tv_usec-start_time.tv_usec)/1e6;
+ if (rtt < 0) rtt= 0;
+ if (now.tv_sec < server_time-tolerance-rtt ||
+ now.tv_sec > server_time+tolerance+rtt)
+ {
+ fprintf(stderr,
+ "setting time, time difference is %ld\n",
+ (long)server_time-now.tv_sec);
+ stime(&server_time);
+ if (atlas_id)
+ {
+ printf(
+ "RESULT %s ongoing %ld httppost setting time, local %ld, remote %ld\n",
+ atlas_id, (long)time(NULL),
+ (long)now.tv_sec,
+ (long)server_time);
+ }
+ }
+ else if (rtt <= 1)
+ {
+ /* Time and network are fine. Record this fact */
+ fh= fopen(ATLAS_TIMESYNC_FILE ".new", "wt");
+ if (fh)
+ {
+ fprintf(fh, "%ld\n", (long)now.tv_sec);
+ fclose(fh);
+ rename(ATLAS_TIMESYNC_FILE ".new",
+ ATLAS_TIMESYNC_FILE);
+ }
+ }
+ else if (atlas_id)
+ {
+ printf("RESULT %s ongoing %ld httppost rtt %g ms\n",
+ atlas_id, (long)time(NULL), rtt*1000);
+ }
+ }
+
+ fprintf(stderr, "httppost: writing output\n");
+ if (output_file)
+ {
+ if (!validate_filename(output_file, SAFE_PREFIX_DATA_NEW))
+ {
+ report("protected file (output) '%s'", output_file);
+ goto err;
+ }
+ out_file= fopen(output_file, "w");
+ if (!out_file)
+ {
+ report_err("unable to create '%s'", output_file);
+ goto err;
+ }
+ }
+ else
+ out_file= stdout;
+
+ if (chunked)
+ {
+ if (!copy_chunked(tcp_file, out_file, &found_ok))
+ goto err;
+ }
+ else if (content_length)
+ {
+ if (!copy_bytes(tcp_file, out_file, content_length, &found_ok))
+ goto err;
+ }
+ if (!found_ok)
+ fprintf(stderr, "httppost: reply text was not equal to OK\n");
+ if ( opt_delete_file == 1 && found_ok)
+ {
+ fprintf(stderr, "httppost: deleting files\n");
+ if (post_file)
+ {
+ if (!validate_filename(post_file, SAFE_PREFIX_DATA_OUT))
+ {
+ report("trying to delete protected file '%s'",
+ post_file);
+ goto err;
+ }
+ unlink (post_file);
+ }
+ if (post_dir)
+ {
+ for (p= filelist; p[0] != 0; p += strlen(p)+1)
+ {
+ fprintf(stderr, "unlinking file '%s'\n", p);
+ if (unlink(p) != 0)
+ report_err("unable to unlink '%s'", p);
+ }
+ }
+ }
+ fprintf(stderr, "httppost: done\n");
+
+ result= 0;
+
+leave:
+ if (fdH != -1) close(fdH);
+ if (fdF != -1) close(fdF);
+ if (fdS != -1) close(fdS);
+ if (fd != -1) close(fd);
+ if (tcp_file)
+ {
+ fclose(tcp_file);
+ tcp_fd= -1;
+ }
+ if (tcp_fd != -1) close(tcp_fd);
+ if (out_file) fclose(out_file);
+ if (host) free(host);
+ if (port) free(port);
+ if (hostport) free(hostport);
+ if (path) free(path);
+ if (filelist) free(filelist);
+
+ alarm(0);
+ signal(SIGPIPE, SIG_DFL);
+
+ return result;
+
+err:
+ fprintf(stderr, "httppost: leaving with error\n");
+ result= 1;
+ goto leave;
+}
+
+static int write_to_tcp_fd (int fd, FILE *tcp_file)
+{
+ int r;
+ char buffer[1024];
+
+ /* Copy file */
+ while(r= read(fd, buffer, sizeof(buffer)), r > 0)
+ {
+ if (fwrite(buffer, r, 1, tcp_file) != 1)
+ {
+ report_err("error writing to tcp connection");
+ return 0;
+ }
+ alarm(10);
+ }
+ if (r == -1)
+ {
+ report_err("error reading from file");
+ return 0;
+ }
+ return 1;
+}
+
+
+static int parse_url(char *url, char **hostp, char **portp, char **hostportp,
+ char **pathp)
+{
+ char *item;
+ const char *cp, *np, *prefix;
+ size_t len;
+
+ *hostportp= NULL;
+ *pathp= NULL;
+ *hostp= NULL;
+ *portp= NULL;
+
+ /* the url must start with 'http://' */
+ prefix= "http://";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, url, len) != 0)
+ {
+ fprintf(stderr, "bad prefix in url '%s'\n", url);
+ return -1;
+ }
+
+ cp= url+len;
+
+ /* Get hostport part */
+ np= strchr(cp, '/');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ fprintf(stderr, "missing host part in url '%s'\n", url);
+ return -1;
+ }
+
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *hostportp= item;
+
+ /* The remainder is the path */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "/";
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *pathp= item;
+
+ /* Extract the host name from hostport */
+ cp= *hostportp;
+ np= cp;
+ if (cp[0] == '[')
+ {
+ /* IPv6 address literal */
+ np= strchr(cp, ']');
+ if (np == NULL || np == cp+1)
+ {
+ fprintf(stderr,
+ "malformed IPv6 address literal in url '%s'\n",
+ url);
+ goto error;
+ }
+ }
+ np= strchr(np, ':');
+ if (np != NULL)
+ len= np-cp;
+ else
+ {
+ len= strlen(cp);
+ np= cp+len;
+ }
+ if (len == 0)
+ {
+ fprintf(stderr, "missing host part in url '%s'\n", url);
+ goto error;
+ }
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ if (cp[0] == '[')
+ {
+ /* Leave out the square brackets */
+ memcpy(item, cp+1, len-2);
+ item[len-2]= '\0';
+ }
+ else
+ {
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ }
+ *hostp= item;
+
+ /* Port */
+ cp= np;
+ if (cp[0] == '\0')
+ cp= "80";
+ else
+ cp++;
+ len= strlen(cp);
+ item= malloc(len+1);
+ if (!item) fatal("out of memory");
+ memcpy(item, cp, len);
+ item[len]= '\0';
+ *portp= item;
+
+ return 0;
+error:
+ free(*hostportp); *hostportp= NULL;
+ free(*pathp); *pathp= NULL;
+ free(*hostp); *hostp= NULL;
+ free(*portp); *portp= NULL;
+
+ return -1;
+}
+
+static int check_result(FILE *tcp_file)
+{
+ int major, minor;
+ size_t len;
+ char *cp, *check, *line;
+ const char *prefix;
+ char buffer[1024];
+
+ while (fgets(buffer, sizeof(buffer), tcp_file) == NULL)
+ {
+ if (feof(tcp_file))
+ {
+ report("got unexpected EOF from server");
+ return 0;
+ }
+ if (errno == EINTR)
+ {
+ report("timeout");
+ sleep(10);
+ }
+ else
+ {
+ report_err("error reading from server");
+ return 0;
+ }
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long\n");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ /* Check http version */
+ prefix= "http/";
+ len= strlen(prefix);
+ if (strncasecmp(prefix, line, len) != 0)
+ {
+ fprintf(stderr, "bad prefix in response '%s'\n", line);
+ return 0;
+ }
+ cp= line+len;
+ major= strtoul(cp, &check, 10);
+ if (check == cp || check[0] != '.')
+ {
+ fprintf(stderr, "bad major version in response '%s'\n", line);
+ return 0;
+ }
+ cp= check+1;
+ minor= strtoul(cp, &check, 10);
+ if (check == cp || check[0] == '\0' ||
+ !isspace(*(unsigned char *)check))
+ {
+ fprintf(stderr, "bad major version in response '%s'\n", line);
+ return 0;
+ }
+
+ skip_spaces(check, &cp);
+
+ if (!isdigit(*(unsigned char *)cp))
+ {
+ fprintf(stderr, "bad status code in response '%s'\n", line);
+ return 0;
+ }
+
+ if (cp[0] != '2')
+ {
+ report("POST command failed: '%s'", cp);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int eat_headers(FILE *tcp_file, int *chunked, int *content_length, time_t *timep)
+{
+ char *line, *cp, *ncp, *check;
+ size_t len;
+ const char *kw;
+ char buffer[1024];
+
+ *chunked= 0;
+ *content_length= 0;
+ while (fgets(buffer, sizeof(buffer), tcp_file) != NULL)
+ {
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long\n");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ if (line[0] == '\0')
+ return 1; /* End of headers */
+
+ fprintf(stderr, "httppost: got line '%s'\n", line);
+
+ if (strncmp(line, "Date: ", 6) == 0)
+ {
+ /* Parse date header */
+ struct tm tm;
+
+ cp= strptime(line+6, "%a, %d %b %Y %H:%M:%S ", &tm);
+ if (!cp || strcmp(cp, "GMT") != 0)
+ {
+ fprintf(stderr, "unable to parse time '%s'\n",
+ line+6);
+ }
+ *timep= timegm(&tm);
+ }
+
+ cp= line;
+ skip_spaces(cp, &ncp);
+ if (ncp != line)
+ continue; /* Continuation line */
+
+ cp= ncp;
+ while (ncp[0] != '\0' && ncp[0] != ':' &&
+ !isspace((unsigned char)ncp[0]))
+ {
+ ncp++;
+ }
+
+ kw= "Transfer-Encoding";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) == 0)
+ {
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ fprintf(stderr,
+ "malformed transfer-encoding header");
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ kw= "chunked";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+ /* make sure we have end of line or white space */
+ if (cp[len] != '\0' && isspace((unsigned char)cp[len]))
+ continue;
+ *chunked= 1;
+ continue;
+ }
+
+ kw= "Content-length";
+ len= strlen(kw);
+ if (strncasecmp(cp, kw, len) != 0)
+ continue;
+
+ /* Skip optional white space */
+ cp= ncp;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != ':')
+ {
+ fprintf(stderr, "malformed content-length header");
+ return 0;
+ }
+ cp++;
+
+ /* Skip more white space */
+ skip_spaces(cp, &cp);
+
+ /* Should have the value by now */
+ *content_length= strtoul(cp, &check, 10);
+ if (check == cp)
+ {
+ fprintf(stderr, "malformed content-length header\n");
+ return 0;
+ }
+
+ /* And after that we should have just white space */
+ cp= check;
+ skip_spaces(cp, &cp);
+
+ if (cp[0] != '\0')
+ {
+ fprintf(stderr, "malformed content-length header\n");
+ return 0;
+ }
+ }
+ if (feof(tcp_file))
+ report("got unexpected EOF from server");
+ else
+ report_err("error reading from server");
+ return 0;
+}
+
+static int connect_to_name(char *host, char *port)
+{
+ int r, s, s_errno;
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+
+ fprintf(stderr, "httppost: before getaddrinfo\n");
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_socktype= SOCK_STREAM;
+ r= getaddrinfo(host, port, &hints, &res);
+ if (r != 0)
+ {
+ fprintf(stderr, "unable to resolve '%s': %s\n",
+ host, gai_strerror(r));
+ errno= ENOENT; /* Need something */
+ return -1;
+ }
+
+ s_errno= 0;
+ s= -1;
+ for (aip= res; aip != NULL; aip= aip->ai_next)
+ {
+ s= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1)
+ {
+ s_errno= errno;
+ continue;
+ }
+
+ fprintf(stderr, "httppost: before connect\n");
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+ s_errno= errno;
+ close(s);
+ s= -1;
+ }
+
+ freeaddrinfo(res);
+ if (s == -1)
+ errno= s_errno;
+ return s;
+}
+
+char *do_dir(char *dir_name, off_t curr_tot_size, off_t max_size, off_t *lenp)
+{
+ size_t currsize, allocsize, dirlen, len;
+ char *list, *tmplist, *path;
+ DIR *dir;
+ struct dirent *de;
+ struct stat sb;
+
+ /* Scan a directory for files. Return the filenames asa list of
+ * strings. An empty string terminates the list. Also compute the
+ * total size of the files
+ */
+ *lenp= 0;
+ currsize= 0;
+ allocsize= 0;
+ list= NULL;
+ dir= opendir(dir_name);
+ if (dir == NULL)
+ {
+ report_err("opendir failed for '%s'", dir_name);
+ return NULL;
+ }
+
+ dirlen= strlen(dir_name);
+ while (de= readdir(dir), de != NULL)
+ {
+ /* Concat dir and entry */
+ len= dirlen + 1 + strlen(de->d_name) + 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes",
+ allocsize);
+ closedir(dir);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ strlcpy(path, dir_name, allocsize-currsize);
+ strlcat(path, "/", allocsize-currsize);
+ strlcat(path, de->d_name, allocsize-currsize);
+
+ if (stat(path, &sb) != 0)
+ {
+ report_err("stat '%s' failed", path);
+ free(list);
+ closedir(dir);
+ return NULL;
+ }
+
+ if (!S_ISREG(sb.st_mode))
+ continue; /* Just skip entry */
+
+ if (curr_tot_size + sb.st_size > max_size)
+ {
+ /* File is too big to fit this time. */
+ if (sb.st_size > max_size/2)
+ {
+ /* File just too big in general */
+ report("deleting file '%s', size %d",
+ path, sb.st_size);
+ unlink(path);
+ }
+ continue;
+ }
+
+ currsize += len;
+ curr_tot_size += sb.st_size;
+ *lenp += sb.st_size;
+ }
+ closedir(dir);
+
+ /* Add empty string to terminate the list */
+ len= 1;
+ if (currsize+len > allocsize)
+ {
+ allocsize += 4096;
+ tmplist= realloc(list, allocsize);
+ if (!tmplist)
+ {
+ free(list);
+ report("realloc failed for %d bytes", allocsize);
+ return NULL;
+ }
+ list= tmplist;
+ }
+ path= list+currsize;
+
+ *path= '\0';
+
+ return list;
+}
+
+static int copy_chunked(FILE *in_file, FILE *out_file, int *found_okp)
+{
+ int i;
+ size_t len, offset, size;
+ char *cp, *line, *check;
+ const char *okp;
+ char buffer[1024];
+
+ okp= OK_STR;
+
+ for (;;)
+ {
+ /* Get a chunk size */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+
+ fprintf(stderr, "httppost: got chunk line '%s'\n", line);
+ len= strtoul(line, &check, 16);
+ if (check[0] != '\0' && !isspace(*(unsigned char *)check))
+ {
+ fprintf(stderr, "bad chunk line '%s'", line);
+ return 0;
+ }
+ if (!len)
+ break;
+
+ offset= 0;
+
+ while (offset < len)
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report_err("error reading input");
+ return 0;
+ }
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ {
+ fprintf(stderr, "error writing output");
+ return 0;
+ }
+ offset += size;
+
+ for (i= 0; i<size; i++)
+ {
+ if (!okp)
+ break;
+ if (*okp != buffer[i] || *okp == '\0')
+ {
+ okp= NULL;
+ break;
+ }
+ okp++;
+ }
+ }
+
+ /* Expect empty line after data */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] != '\0')
+ {
+ fprintf(stderr, "Garbage after chunk data");
+ return 0;
+ }
+ }
+
+ for (;;)
+ {
+ /* Get an end-of-chunk line */
+ if (fgets(buffer, sizeof(buffer), in_file) == NULL)
+ {
+ report("error reading input");
+ return 0;
+ }
+
+ line= buffer;
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ fprintf(stderr, "line too long");
+ return 0;
+ }
+ cp[0]= '\0';
+ if (cp > line && cp[-1] == '\r')
+ cp[-1]= '\0';
+ if (line[0] == '\0')
+ break;
+
+ fprintf(stderr, "httppost: got end-of-chunk line '%s'\n", line);
+ }
+ *found_okp= (okp != NULL && *okp == '\0');
+ return 1;
+}
+
+static int copy_bytes(FILE *in_file, FILE *out_file, size_t len, int *found_okp)
+{
+ int i;
+ size_t offset, size;
+ const char *okp;
+ char buffer[1024];
+
+ okp= OK_STR;
+
+ offset= 0;
+
+ while (offset < len)
+ {
+ size= len-offset;
+ if (size > sizeof(buffer))
+ size= sizeof(buffer);
+ if (fread(buffer, size, 1, in_file) != 1)
+ {
+ report_err("error reading input");
+ return 0;
+ }
+ if (fwrite(buffer, size, 1, out_file) != 1)
+ {
+ report_err("error writing output");
+ return 0;
+ }
+ offset += size;
+
+ for (i= 0; i<size; i++)
+ {
+ if (!okp)
+ break;
+ if (*okp != buffer[i] || *okp == '\0')
+ {
+ okp= NULL;
+ break;
+ }
+ okp++;
+ }
+ }
+ *found_okp= (okp != NULL && *okp == '\0');
+ return 1;
+}
+
+static void skip_spaces(const char *cp, char **ncp)
+{
+ const unsigned char *ucp;
+
+ ucp= (const unsigned char *)cp;
+ while (ucp[0] != '\0' && isspace(ucp[0]))
+ ucp++;
+ *ncp= (char *)ucp;
+}
+
+static void got_alarm(int sig __attribute__((unused)) )
+{
+ if (tcp_fd != -1 && time(NULL) > start_time.tv_sec+timeout)
+ {
+ report("setting tcp_fd to nonblock");
+ fcntl(tcp_fd, F_SETFL, fcntl(tcp_fd, F_GETFL) | O_NONBLOCK);
+ }
+ kick_watchdog();
+ report("got alarm, setting alarm again");
+ alarm(1);
+}
+
+static void kick_watchdog(void)
+{
+ int fdwatchdog = open("/dev/watchdog", O_RDWR);
+ if (fdwatchdog != -1)
+ {
+ write(fdwatchdog, "1", 1);
+ close(fdwatchdog);
+ }
+}
+
+static void fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+#if 0
+static void fatal_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+
+ exit(1);
+}
+#endif
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+
+ va_end(ap);
+}
+
+static void report_err(const char *fmt, ...)
+{
+ int s_errno;
+ va_list ap;
+
+ s_errno= errno;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "httppost: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(s_errno));
+
+ va_end(ap);
+}
diff --git a/networking/rptra6.c b/networking/rptra6.c
new file mode 100644
index 0000000..b481252
--- /dev/null
+++ b/networking/rptra6.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2013 RIPE NCC <atlas@ripe.net>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+
+#define OPT_STRING "P:"
+
+#define DBQ(str) "\"" #str "\""
+
+#define IN6ADDR_ALL_NODES_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+struct in6_addr in6addr_all_nodes = IN6ADDR_ALL_NODES_INIT; /* ff02::1 */
+
+#define OPT_RDNSS 25
+
+#define RA_PREF_MASK 0x18
+#define RA_PREF_HIGH 0x08
+#define RA_PREF_LOW 0x18
+
+struct opt_rdnss /* RDNSS option */
+{
+ uint8_t nd_opt_rdnss_type;
+ uint8_t nd_opt_rdnss_len;
+ uint16_t nd_opt_rdnss_reserved;
+ uint32_t nd_opt_rdnss_lifetime;
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: rptra6 <new> <out>\n");
+ exit(1);
+}
+
+int rptra6_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;
+ uint8_t flags_reserved;
+ size_t o;
+ char *new_name, *out_name;
+ struct nd_router_advert *ra;
+ struct nd_opt_hdr *oh;
+ struct nd_opt_prefix_info *pi;
+ struct nd_opt_mtu *mtup;
+ struct opt_rdnss *rdnssp;
+ struct icmp6_hdr * icmp;
+ struct cmsghdr *cmsgptr;
+ struct sockaddr_in6 *sin6p;
+ FILE *of;
+ char *str_pidfile;
+ struct stat sb;
+ struct sockaddr_in6 remote; /* responding internet address */
+ struct sockaddr_in6 loc_sin6;
+ struct msghdr msg;
+ struct iovec iov[1];
+ char namebuf[NI_MAXHOST];
+ char cmsgbuf[256];
+ char packet[4096];
+
+ str_pidfile= NULL;
+ (void) getopt32(argv, OPT_STRING, &str_pidfile);
+
+ if (argc != optind+2)
+ usage();
+
+ new_name= argv[optind];
+ out_name= argv[optind+1];
+
+ if (str_pidfile)
+ {
+ of= fopen(str_pidfile, "w");
+ if (of)
+ {
+ fprintf(of, "%d\n", getpid());
+ fclose(of);
+ }
+ }
+
+ of= NULL;
+
+ sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ if (sock == -1)
+ {
+ printf("socket failed: %s\n", strerror(errno));
+ return 1;
+ }
+
+ on = 1;
+ setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+
+ on = 1;
+ setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on));
+
+
+ icmp = (struct icmp6_hdr *) packet;
+
+ for(;;)
+ {
+ iov[0].iov_base= packet;
+ iov[0].iov_len= sizeof(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 */
+
+ /* Receive data from the network */
+ nrecv= recvmsg(sock, &msg, 0);
+ if (nrecv < 0)
+ {
+ printf("recvmsg failed: %s\n", strerror(errno));
+ break;
+ }
+
+ /* Check for Destination Host Unreachable */
+ if (icmp->icmp6_type != ND_ROUTER_ADVERT)
+ {
+ switch(icmp->icmp6_type)
+ {
+ case ICMP6_DST_UNREACH: /* 1 */
+ case ICMP6_PACKET_TOO_BIG: /* 2 */
+ case ICMP6_TIME_EXCEEDED: /* 3 */
+ case ICMP6_ECHO_REQUEST: /* 128 */
+ case ICMP6_ECHO_REPLY: /* 129 */
+ case ND_NEIGHBOR_SOLICIT: /* 135 */
+ case ND_NEIGHBOR_ADVERT: /* 136 */
+ case ND_REDIRECT: /* 137 */
+ break; /* Ignore */
+ default:
+ printf("icmp6_type %d\n", icmp->icmp6_type);
+ break;
+ }
+ continue;
+ }
+
+ of= fopen(new_name, "a");
+ if (of == NULL)
+ {
+ fprintf(stderr, "unable to open '%s': %s\n", new_name, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(of, "RESULT { " DBQ(id) ": " DBQ(9019) ", " DBQ(time) ": %ld",
+ (long)time(NULL));
+ getnameinfo((struct sockaddr *)&remote, msg.msg_namelen,
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ fprintf(of, ", " DBQ(src) ": " DBQ(%s), namebuf);
+
+ /* Set destination address of packet as local address */
+ memset(&loc_sin6, '\0', sizeof(loc_sin6));
+ 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_PKTINFO)
+ {
+ sin6p= &loc_sin6;
+ sin6p->sin6_family= AF_INET6;
+ sin6p->sin6_addr= ((struct in6_pktinfo *)
+ CMSG_DATA(cmsgptr))->ipi6_addr;
+ }
+ if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
+ cmsgptr->cmsg_type == IPV6_HOPLIMIT)
+ {
+ rcvd_ttl= *(int *)CMSG_DATA(cmsgptr);
+ }
+ }
+
+ if (memcmp(&loc_sin6.sin6_addr, &in6addr_all_nodes,
+ sizeof(loc_sin6.sin6_addr)) != 0)
+ {
+ getnameinfo((struct sockaddr *)&loc_sin6, sizeof(loc_sin6),
+ namebuf, sizeof(namebuf), NULL, 0, NI_NUMERICHOST);
+ fprintf(of, ", " DBQ(dst) ": " DBQ(%s), namebuf);
+ }
+ if (rcvd_ttl != 255)
+ fprintf(of, ", " DBQ(ttl) ": %d", rcvd_ttl);
+
+ ra= (struct nd_router_advert *)packet;
+ fprintf(of, ", " DBQ(hop_limit) ": %d", ra->nd_ra_curhoplimit);
+ flags_reserved= ra->nd_ra_flags_reserved;
+ if (flags_reserved & ND_RA_FLAG_OTHER)
+ {
+ fprintf(of, ", " DBQ(other_conf) ": true");
+ flags_reserved &= ~ND_RA_FLAG_OTHER;
+ }
+ switch(flags_reserved & RA_PREF_MASK)
+ {
+ case RA_PREF_HIGH:
+ fprintf(of, ", " DBQ(preference) ": " DBQ(high));
+ flags_reserved &= ~RA_PREF_MASK;
+ break;
+ case RA_PREF_LOW:
+ fprintf(of, ", " DBQ(preference) ": " DBQ(low));
+ flags_reserved &= ~RA_PREF_MASK;
+ break;
+ }
+ if (flags_reserved)
+ fprintf(of, ", " DBQ(reserved) ": 0x%x", flags_reserved);
+ fprintf(of, ", " DBQ(lifetime) ": %d", ntohs(ra->nd_ra_router_lifetime));
+ if (ra->nd_ra_reachable)
+ fprintf(of, ", " DBQ(reachable_time) ": %d", ntohl(ra->nd_ra_reachable));
+ if (ra->nd_ra_retransmit)
+ fprintf(of, ", " DBQ(retransmit_time) ": %d", ntohl(ra->nd_ra_retransmit));
+
+ fprintf(of, ", " DBQ(options) ": [ ");
+ first= 1;
+ for (o= sizeof(*ra); o<nrecv;)
+ {
+ if (!first)
+ fprintf(of, ", ");
+ else
+ first= 0;
+
+ if (o+sizeof(*oh) > nrecv)
+ {
+ printf("partial option\n");
+ break;
+ }
+
+ oh= (struct nd_opt_hdr *)&packet[o];
+ if (oh->nd_opt_len == 0)
+ {
+ printf("bad option length (0) at %ld\n",
+ (long)o);
+ break;
+ }
+ olen= oh->nd_opt_len * 8;
+
+ switch(oh->nd_opt_type)
+ {
+ case ND_OPT_SOURCE_LINKADDR: /* 1 */
+ fprintf(of, "{ " DBQ(type) ": " DBQ(link layer address) ", "
+ DBQ(addr) ": \"");
+ for (i= 2; i<olen; i++)
+ {
+ fprintf(of, "%s%02x", i == 2 ? "" : ":",
+ ((uint8_t *)oh)[i]);
+ }
+ fprintf(of, "\" }");
+ break;
+ case ND_OPT_PREFIX_INFORMATION: /* 3 */
+ if (olen < sizeof(*pi))
+ {
+ printf(
+ "bad option length (%d) for prefix info\n",
+ oh->nd_opt_len);
+ break;
+ }
+ pi= (struct nd_opt_prefix_info *)oh;
+ fprintf(of, "{ " DBQ(prefix_len) ": %d",
+ pi->nd_opt_pi_prefix_len);
+ flags_reserved= pi->nd_opt_pi_flags_reserved;
+ if (flags_reserved & ND_OPT_PI_FLAG_ONLINK)
+ {
+ fprintf(of, ", " DBQ(onlink) ": true");
+ flags_reserved &= ~ND_OPT_PI_FLAG_ONLINK;
+ }
+ if (flags_reserved & ND_OPT_PI_FLAG_AUTO)
+ {
+ fprintf(of, ", " DBQ(auto) ": true");
+ flags_reserved &= ~ND_OPT_PI_FLAG_AUTO;
+ }
+
+ if (flags_reserved)
+ {
+ fprintf(of, ", " DBQ(reserved1) ": 0x%x", flags_reserved);
+ }
+ fprintf(of, ", " DBQ(valid_time) ": %d",
+ ntohl(pi-> nd_opt_pi_valid_time));
+ fprintf(of, ", " DBQ(preferred_time) ": %d",
+ ntohl(pi-> nd_opt_pi_preferred_time));
+ if (pi-> nd_opt_pi_reserved2)
+ {
+ fprintf(of, ", " DBQ(reserved2) ": %d",
+ ntohl(pi-> nd_opt_pi_reserved2));
+ }
+
+ fprintf(of, ", " DBQ(prefix) ": " DBQ(%s) " }",
+ inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix,
+ namebuf, sizeof(namebuf)));
+ break;
+
+ case ND_OPT_MTU: /* 5 */
+ fprintf(of, "{ " DBQ(type) ": " DBQ(mtu));
+ mtup= (struct nd_opt_mtu *)oh;
+ if (mtup->nd_opt_mtu_reserved)
+ {
+ fprintf(of, ", " DBQ(reserved) ": 0x%x",
+ ntohs(mtup->nd_opt_mtu_reserved));
+ }
+ fprintf(of, ", " DBQ(mtu) ": %d }",
+ ntohl(mtup->nd_opt_mtu_mtu));
+ break;
+
+ case OPT_RDNSS: /* 25 */
+ fprintf(of, "{ " DBQ(type) ": " DBQ(rdnss));
+ rdnssp= (struct opt_rdnss *)oh;
+ if (rdnssp->nd_opt_rdnss_reserved)
+ {
+ fprintf(of, ", " DBQ(reserved) ": %d",
+ ntohs(rdnssp->nd_opt_rdnss_reserved));
+ }
+ fprintf(of, ", " DBQ(lifetime) ": %d",
+ ntohl(rdnssp->nd_opt_rdnss_lifetime));
+
+ fprintf(of, ", " DBQ(addrs) ": [ ");
+ for (i= 8; i+16 <= olen; i+= 16)
+ {
+ inet_ntop(AF_INET6, ((char *)oh)+i,
+ namebuf, sizeof(namebuf));
+ fprintf(of, "%s" DBQ(%s),
+ i == 8 ? "" : ", ",
+ namebuf);
+ }
+ fprintf(of, " ] }");
+ break;
+
+ default:
+ fprintf(of, "{ " DBQ(type_no) ": %d }", oh->nd_opt_type);
+ break;
+ }
+
+
+ o += olen;
+ }
+ fprintf(of, " ] }\n");
+
+ fclose(of);
+
+ r= stat(out_name, &sb);
+ if (r == 0)
+ continue;
+ if (errno == ENOENT)
+ {
+ rename(new_name, out_name);
+ continue;
+ }
+ fprintf(stderr, "stat '%s' failed: %s\n", out_name, strerror(errno));
+ exit(1);
+ }
+
+ fprintf(stderr, "end of main\n");
+ exit(1);
+}
diff --git a/networking/rxtxrpt.c b/networking/rxtxrpt.c
new file mode 100644
index 0000000..609cedb
--- /dev/null
+++ b/networking/rxtxrpt.c
@@ -0,0 +1,368 @@
+/*
+rxtxrpt.c
+
+Report RX and TX statistics. Also report IPv6 address and the IPv6 routing
+table if it has changed.
+*/
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libbb.h"
+
+#define DEV_FILE "/proc/net/dev"
+#define IF_INET6_FILE "/proc/net/if_inet6"
+#define IPV6_ROUTE_FILE "/proc/net/ipv6_route"
+#define SUFFIX ".new"
+
+int do_atlas= 0;
+
+static int rpt_rxtx(void);
+static int setup_ipv6_rpt(char *cache_name, int *need_report);
+static int rpt_ipv6(char *cache_name);
+static void report(const char *fmt, ...);
+static void report_err(const char *fmt, ...);
+
+int rxtxrpt_main(int argc, char *argv[])
+{
+ int r, need_report;
+ unsigned opt;
+ char *opt_atlas, *cache_name;
+
+ opt_atlas= NULL;
+ opt_complementary= NULL;
+ opt= getopt32(argv, "A:", &opt_atlas);
+
+ do_atlas= (opt_atlas != NULL);
+
+ if (argc > optind+1)
+ bb_show_usage();
+
+ cache_name= NULL;
+ if (argc == optind+1)
+ cache_name= argv[optind];
+
+ if (do_atlas)
+ {
+ printf("%s %lu ", opt_atlas, time(NULL));
+ }
+
+ r= rpt_rxtx();
+ if (r != 0)
+ return r;
+
+ if (cache_name)
+ {
+ r= setup_ipv6_rpt(cache_name, &need_report);
+ if (r != 0)
+ return r;
+ if (need_report)
+ {
+ r= rpt_ipv6(cache_name);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ if (do_atlas)
+ printf("\n");
+
+ return 0;
+}
+
+static int rpt_rxtx(void)
+{
+ int i;
+ char *cp;
+ FILE *file;
+ char buf[256];
+
+ file= fopen(DEV_FILE, "r");
+ if (!file)
+ {
+ report_err("unable to open '%s'", DEV_FILE);
+ return 1;
+ }
+
+ /* Skip two lines */
+ if (fgets(buf, sizeof(buf), file) == NULL ||
+ fgets(buf, sizeof(buf), file) == NULL)
+ {
+ report_err("unable to read from '%s'", DEV_FILE);
+ fclose(file);
+ return 1;
+ }
+
+ /* Copy two line */
+ for (i= 0; i<2; i++)
+ {
+ if (fgets(buf, sizeof(buf), file) == NULL)
+ {
+ report_err("unable to read from '%s'", DEV_FILE);
+ fclose(file);
+ return 1;
+ }
+
+ if (do_atlas)
+ {
+ /* Get rid of newline */
+ cp= strchr(buf, '\n');
+ if (cp) *cp= '\0';
+
+ if (i != 0)
+ printf(" NEWLINE ");
+ }
+ fputs(buf, stdout);
+ }
+ fclose(file);
+
+ return 0;
+}
+
+static int setup_ipv6_rpt(char *cache_name, int *need_report)
+{
+ int i, r;
+ char *cp, *cp1;
+ char filename[80];
+ 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;
+ }
+
+ while (r= fread(buf1, 1, sizeof(buf1), in_file), r > 0)
+ {
+ if (fwrite(buf1, 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;
+ }
+ 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;
+ }
+
+ while (fgets(buf1, sizeof(buf1), in_file) != NULL)
+ {
+ /* Cut out Ref and Use fields */
+ cp= buf1;
+ for (i= 0; i<6; i++)
+ {
+ if (cp && cp[0] != '\0')
+ cp= strchr(cp+1, ' ');
+ }
+ if (!cp && cp[0] == '\0')
+ {
+ report("bad data in '%s'", IPV6_ROUTE_FILE);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ cp++;
+ /* Find the end of the two fields */
+ cp1= cp;
+ for (i= 0; i<2; i++)
+ {
+ if (cp1 && cp1[0] != '\0')
+ cp1= strchr(cp1+1, ' ');
+ }
+ if (!cp1 && cp1[0] == '\0')
+ {
+ report("bad data in '%s'", IPV6_ROUTE_FILE);
+ fclose(in_file);
+ fclose(out_file);
+ return 1;
+ }
+ cp1++;
+ /* And delete the two fields */
+ memmove(cp, cp1, strlen(cp1)+1);
+
+ if (fputs(buf1, out_file) == -1)
+ {
+ 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)
+{
+ FILE *file;
+ char *cp;
+ char buf[256];
+
+ file= fopen(cache_name, "r");
+ if (!file)
+ {
+ report_err("unable to open '%s'", cache_name);
+ return 1;
+ }
+
+ /* Copy all lines */
+ while (fgets(buf, sizeof(buf), file) != NULL)
+ {
+ if (do_atlas)
+ {
+ /* Get rid of newline */
+ cp= strchr(buf, '\n');
+ if (cp) *cp= '\0';
+
+ printf(" NEWLINE ");
+ }
+ fputs(buf, stdout);
+ }
+ fclose(file);
+
+ return 0;
+}
+
+static void report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ fprintf(stderr, "rxtxrpt: ");
+ 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, "rxtxrpt: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, ": %s\n", strerror(t_errno));
+
+ va_end(ap);
+}
diff --git a/networking/sslgetcert.c b/networking/sslgetcert.c
new file mode 100644
index 0000000..a0fbf3c
--- /dev/null
+++ b/networking/sslgetcert.c
@@ -0,0 +1,762 @@
+/* Simple SSL client to get server certificates */
+
+#include "libbb.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#define OPT_4 (1 << 0)
+#define OPT_6 (1 << 1)
+
+#define MSG_HANDSHAKE 22
+#define HS_CLIENT_HELLO 1
+#define HS_SERVER_HELLO 2
+#define HS_CERTIFICATE 11
+
+struct buf
+{
+ size_t offset;
+ size_t size;
+ size_t maxsize;
+ char *buf;
+ int fd;
+};
+
+#define BUF_CHUNK 4096
+
+struct msgbuf
+{
+ struct buf *inbuf;
+ struct buf *outbuf;
+
+ struct buf buffer;
+};
+
+struct hsbuf
+{
+ struct buf buffer;
+};
+
+#define URANDOM_DEV "/dev/urandom"
+
+#define DBQ(str) "\"" #str "\""
+
+static int tcp_fd= -1;
+
+static void fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "ssltestc30: ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+static void got_alarm(int sig __attribute__((unused)) )
+{
+ if (tcp_fd != -1)
+ fcntl(tcp_fd, F_SETFL, fcntl(tcp_fd, F_GETFL) | O_NONBLOCK);
+ alarm(1);
+}
+
+static void buf_init(struct buf *buf, int fd)
+{
+ buf->maxsize= 0;
+ buf->size= 0;
+ buf->offset= 0;
+ buf->buf= NULL;
+ buf->fd= fd;
+}
+
+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:
+ fatal("bad state in buf_add_b64");
+ }
+}
+
+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 */
+ fatal("buf_read: should compact");
+ }
+ else
+ {
+ maxsize= buf->size-buf->offset + BUF_CHUNK;
+ newbuf= malloc(maxsize);
+ if (!newbuf)
+ fatal("unable to allocate %d bytes", maxsize);
+ 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;
+ }
+ }
+
+ for (;;)
+ {
+ r= read(buf->fd, buf->buf+buf->size, buf->maxsize-buf->size);
+ if (r > 0)
+ {
+ buf->size += r;
+ break;
+ }
+ fprintf(stderr, "read error on fd %d: %s",
+ buf->fd, r == 0 ? "eof" : strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int buf_write(struct buf *buf)
+{
+ int r;
+ size_t len;
+
+ while (buf->offset < buf->size)
+ {
+ len= buf->size - buf->offset;
+ r= write(buf->fd, buf->buf+buf->offset, len);
+ if (len > 0)
+ {
+ buf->offset += len;
+ continue;
+ }
+ fprintf(stderr, "write to %d failed: %s\n",
+ buf->fd, 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, -1);
+ 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 Xmsgbuf_read(struct msgbuf *msgbuf, int type)
+{
+ int r;
+ size_t len;
+ uint8_t *p;
+
+ for(;;)
+ {
+ while (msgbuf->inbuf->size - msgbuf->inbuf->offset < 5)
+ {
+ r= buf_read(msgbuf->inbuf);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "msgbuf_read: buf_read failed\n");
+ 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)
+ {
+ fprintf(stderr,
+ "msgbuf_read: buf_read failed\n");
+ 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, -1);
+}
+
+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 int connect_to_name(int af, const char *host, const char *port)
+{
+ int r, s, s_errno;
+ struct addrinfo *res, *aip;
+ struct addrinfo hints;
+
+ memset(&hints, '\0', sizeof(hints));
+ hints.ai_family= af;
+ hints.ai_socktype= SOCK_STREAM;
+ r= getaddrinfo(host, port, &hints, &res);
+ if (r != 0)
+ {
+ fprintf(stderr,
+ "unable to resolve '%s': %s", host, gai_strerror(r));
+ return -1;
+ }
+
+ s_errno= 0;
+ s= -1;
+ for (aip= res; aip != NULL; aip= aip->ai_next)
+ {
+ s= socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1)
+ {
+ s_errno= errno;
+ continue;
+ }
+
+ if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+
+ s_errno= errno;
+ close(s);
+ s= -1;
+ }
+
+ freeaddrinfo(res);
+ if (s == -1)
+ errno= s_errno;
+ return s;
+}
+
+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 int Xeat_server_hello(struct msgbuf *msgbuf)
+{
+ int r;
+ size_t len;
+ uint8_t *p;
+
+ for (;;)
+ {
+ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4)
+ {
+ r= Xmsgbuf_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= Xmsgbuf_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 Xeat_certificate(struct msgbuf *msgbuf)
+{
+ int i, n, r, first, slen;
+ size_t o, len;
+ uint8_t *p;
+ struct buf tmpbuf;
+
+ for (;;)
+ {
+ if (msgbuf->buffer.size - msgbuf->buffer.offset < 4)
+ {
+ r= Xmsgbuf_read(msgbuf, MSG_HANDSHAKE);
+ if (r < 0)
+ {
+ fprintf(stderr,
+ "eat_certificate: msgbuf_read failed\n");
+ 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= Xmsgbuf_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;
+ first= 1;
+ printf(", " DBQ(cert) ":[ ");
+
+ buf_init(&tmpbuf, -1);
+ while (o < 3+n)
+ {
+ slen= (p[o] << 16) + (p[o+1] << 8) + p[o+2];
+ buf_add_b64(&tmpbuf, p+o+3, slen);
+ printf("%s\"-----BEGIN CERTIFICATE-----\\n",
+ !first ? ", " : "");
+ for (i= tmpbuf.offset; i<tmpbuf.size; i++)
+ {
+ if (tmpbuf.buf[i] == '\n')
+ fputs("\\n", stdout);
+ else
+ putchar(tmpbuf.buf[i]);
+ }
+ printf("\\n-----END CERTIFICATE-----\"");
+ tmpbuf.size= tmpbuf.offset;
+ o += 3+slen;
+ first= 0;
+ }
+ buf_cleanup(&tmpbuf);
+ printf(" ]");
+ 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;
+ }
+
+ return 0;
+}
+
+extern int get_atlas_fw_version(void); /* In eperd */
+
+int sslgetcert_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sslgetcert_main(int argc UNUSED_PARAM, char **argv)
+{
+ int r, af;
+ uint32_t opt;
+ socklen_t salen;
+ char *str_Atlas;
+ const char *str_port;
+ char *hostname;
+ struct buf inbuf, outbuf;
+ struct msgbuf msginbuf, msgoutbuf;
+ struct hsbuf hsbuf;
+ struct sockaddr_storage sa;
+ struct sigaction sia;
+ char hostbuf[NI_MAXHOST];
+
+ str_Atlas= NULL;
+ str_port= "https";
+
+ opt_complementary = "=1";
+ opt = getopt32(argv, "!46A:p:", &str_Atlas, &str_port);
+ if (opt == (uint32_t)-1)
+ return 1;
+ hostname = argv[optind];
+
+ af= AF_UNSPEC;
+ if (opt & OPT_4)
+ af= AF_INET;
+ if (opt & OPT_6)
+ af= AF_INET6;
+
+ sia.sa_flags= 0;
+ sia.sa_handler= got_alarm;
+ sigemptyset(&sia.sa_mask);
+ sigaction(SIGALRM, &sia, NULL);
+ alarm(10);
+ signal(SIGPIPE, SIG_IGN);
+
+ tcp_fd= connect_to_name(af, hostname, str_port);
+
+ printf("RESULT { ");
+ if (str_Atlas)
+ {
+ printf(DBQ(id) ":" DBQ(%s)
+ ", " DBQ(fw) ":%d",
+ str_Atlas, get_atlas_fw_version());
+ }
+
+ printf("%s" DBQ(time) ":%ld", str_Atlas ? ", " : "", time(NULL));
+ printf(", " DBQ(dst_name) ":" DBQ(%s) ", " DBQ(dst_port) ":" DBQ(%s),
+ hostname, str_port);
+
+ printf(", " DBQ(method) ":" DBQ(SSL) ", " DBQ(ver) ":" DBQ(3.0));
+ if (af != AF_UNSPEC)
+ printf(", " DBQ(af) ": %d", af == AF_INET6 ? 6 : 4);
+
+ if (tcp_fd == -1)
+ {
+ printf(", " DBQ(err) ":" DBQ(unable to connect) " }\n");
+ return 0;
+ }
+
+ salen= sizeof(sa);
+ if (getpeername(tcp_fd, (struct sockaddr *)&sa, &salen) != -1)
+ {
+ getnameinfo((struct sockaddr *)&sa, salen,
+ hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NUMERICHOST);
+ printf(", " DBQ(dst_addr) ":" DBQ(%s), hostbuf);
+ if (af == AF_UNSPEC)
+ {
+ printf(", " DBQ(af) ": %d",
+ sa.ss_family == AF_INET6 ? 6 : 4);
+ }
+ }
+ salen= sizeof(sa);
+ if (getsockname(tcp_fd, (struct sockaddr *)&sa, &salen) != -1)
+ {
+ getnameinfo((struct sockaddr *)&sa, salen,
+ hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NUMERICHOST);
+ printf(", " DBQ(src_addr) ":" DBQ(%s), hostbuf);
+ }
+
+ buf_init(&outbuf, tcp_fd);
+ 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);
+ r= buf_write(&outbuf);
+ if (r == -1)
+ goto fail;
+
+ buf_init(&inbuf, tcp_fd);
+ msgbuf_init(&msginbuf, &inbuf, NULL);
+
+ if (Xeat_server_hello(&msginbuf) < 0)
+ goto fail;
+
+ if (Xeat_certificate(&msginbuf) < 0)
+ goto fail;
+
+fail:
+ printf(" }\n");
+
+ close(tcp_fd);
+ tcp_fd= -1;
+
+ hsbuf_cleanup(&hsbuf);
+ msgbuf_cleanup(&msginbuf);
+ msgbuf_cleanup(&msgoutbuf);
+ buf_cleanup(&inbuf);
+ buf_cleanup(&outbuf);
+
+ alarm(0);
+ signal(SIGPIPE, SIG_DFL);
+
+ return 0;
+}
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 46dfb31..1855f1b 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -32,12 +32,65 @@
#endif
#include <arpa/telnet.h>
+#define ATLAS 1
+
+#ifdef ATLAS
+#include <string.h>
+#include <unistd.h>
+#include <linux/reboot.h>
+
+#define LOGIN_PREFIX "Atlas probe, see http://atlas.ripe.net/\r\n\r\n"
+#define LOGIN_PROMPT " login: "
+#define PASSWORD_PROMPT "\r\nPassword: "
+
+#define ATLAS_LOGIN "C_TO_P_TEST_V1"
+#define ATLAS_SESSION_FILE "/home/atlas/status/con_session_id.txt"
+#define SESSION_ID_PREFIX "SESSION_ID "
+
+#define CMD_CRONTAB "CRONTAB "
+#define CMD_CRONLINE "CRONLINE "
+#define CMD_ONEOFF "ONEOFF "
+#define CMD_REBOOT "REBOOT"
+
+#define CRLF "\r\n"
+#define RESULT_OK "OK" CRLF CRLF
+#define BAD_PASSWORD "BAD_PASSWORD" CRLF CRLF
+#define BAD_COMMAND "BAD_COMMAND" CRLF CRLF
+#define NAME_TOO_LONG "NAME_TOO_LONG" CRLF CRLF
+#define CRONTAB_BUSY "CRONTAB_BUSY" CRLF CRLF
+#define CREATE_FAILED "UNABLE_TO_CREATE_NEW_CRONTAB" CRLF CRLF
+#define IO_ERROR "IO_ERROR" CRLF CRLF
+#define BAD_PATH "BAD_PATH" CRLF CRLF
+
+#define CRONUSER "root"
+#define CRONTAB_NEW_SUF "/" CRONUSER ".new"
+#define CRONTAB_SUFFIX "/" CRONUSER
+#define CRONUPDATE "/cron.update"
+#define UPDATELINE CRONUSER "\n"
+#define ONEOFF_SUFFIX ".new"
+#define SAFE_PREFIX ATLAS_CRONS
+
+enum state
+{
+ DO_TRADITIONAL,
+ GET_LOGINNAME,
+ GET_PASSWORD,
+ GET_CMD,
+ DO_CRONTAB,
+ EOM_SEEN
+};
+#endif
+
/* Structure that describes a session */
struct tsession {
struct tsession *next;
int sockfd_read, sockfd_write, ptyfd;
int shell_pid;
+#ifdef ATLAS
+ enum state state;
+#endif
+
/* two circular buffers */
/*char *buf1, *buf2;*/
/*#define TS_BUF1 ts->buf1*/
@@ -52,6 +105,19 @@ struct tsession {
* Make whole thing fit in 4k */
enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
+#ifdef ATLAS
+static int equal_sessionid(char *passwd);
+static void add_2sock(struct tsession *ts, const char *str);
+static void pack_4sock(void);
+static char *getline_2pty(struct tsession *ts);
+static void pack_2pty(struct tsession *ts);
+static int start_crontab(struct tsession *ts, char *line);
+static void add_to_crontab(struct tsession *ts, char *line);
+static void end_crontab(struct tsession *ts);
+static void do_oneoff(struct tsession *ts, char *line);
+static int get_probe_id(void);
+int validate_filename(const char *path, const char *prefix);
+#endif
/* Globals */
static int maxfd;
@@ -59,6 +125,16 @@ static struct tsession *sessions;
static const char *loginpath = "/bin/login";
static const char *issuefile = "/etc/issue.net";
+#ifdef ATLAS
+/* Place to store the file handle and directory name for a new crontab */
+static FILE *atlas_crontab;
+static char atlas_dirname[256];
+static struct tsession *atlas_ts; /* Allow only one 'atlas' connection
+ * at a time. The old one
+ * self-destructs when a new one is
+ * started.
+ */
+#endif
/*
Remove all IAC's from buf1 (received IACs are ignored and must be removed
@@ -165,21 +241,32 @@ make_new_session(
USE_FEATURE_TELNETD_STANDALONE(int sock)
SKIP_FEATURE_TELNETD_STANDALONE(void)
) {
+#ifndef ATLAS
const char *login_argv[2];
struct termios termbuf;
int fd, pid;
char tty_name[GETPTY_BUFSIZE];
+#endif
struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+#ifdef ATLAS
+ ts->state= GET_LOGINNAME;
+#endif
+
/*ts->buf1 = (char *)(ts + 1);*/
/*ts->buf2 = ts->buf1 + BUFSIZE;*/
+#ifdef ATLAS
+ ts->ptyfd= 0;
+#else
/* Got a new connection, set up a tty. */
fd = xgetpty(tty_name);
if (fd > maxfd)
maxfd = fd;
ts->ptyfd = fd;
ndelay_on(fd);
+#endif /* ATLAS */
+
#if ENABLE_FEATURE_TELNETD_STANDALONE
ts->sockfd_read = sock;
/* SO_KEEPALIVE by popular demand */
@@ -217,6 +304,25 @@ make_new_session(
ts->size2 = sizeof(iacs_to_send);
}
+#ifdef ATLAS /* Split original function into two */
+ return ts;
+}
+
+static int start_login(struct tsession *ts, char *user)
+{
+ int fd, pid;
+ const char *login_argv[3];
+ struct termios termbuf;
+ char tty_name[GETPTY_BUFSIZE];
+
+ /* Got a new connection, set up a tty. */
+ fd = xgetpty(tty_name);
+ if (fd > maxfd)
+ maxfd = fd;
+ ts->ptyfd = fd;
+ ndelay_on(fd);
+#endif /* ATLAS */
+
fflush(NULL); /* flush all streams */
pid = vfork(); /* NOMMU-friendly */
if (pid < 0) {
@@ -224,12 +330,20 @@ make_new_session(
close(fd);
/* sock will be closed by caller */
bb_perror_msg("vfork");
+#ifdef ATLAS
+ return -1;
+#else
return NULL;
+#endif
}
if (pid > 0) {
/* Parent */
ts->shell_pid = pid;
+#ifdef ATLAS
+ return 0;
+#else
return ts;
+#endif
}
/* Child */
@@ -270,7 +384,12 @@ make_new_session(
/* Exec shell / login / whatever */
login_argv[0] = loginpath;
+#ifdef ATLAS
+ login_argv[1] = user;
+ login_argv[2] = NULL;
+#else
login_argv[1] = NULL;
+#endif
/* exec busybox applet (if PREFER_APPLETS=y), if that fails,
* exec external program */
BB_EXECVP(loginpath, (char **)login_argv);
@@ -281,10 +400,14 @@ make_new_session(
/* Must match getopt32 string */
enum {
+ /* (1 << 0) -f */
+ /* (1 << 1) -l */
OPT_WATCHCHILD = (1 << 2), /* -K */
OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
- OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
- OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
+ /* (1 << 4) -P */
+ OPT_PORT = (1 << 5) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
+ /* (1 << 6) -b */
+ OPT_FOREGROUND = (1 << 7) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
};
#if ENABLE_FEATURE_TELNETD_STANDALONE
@@ -313,7 +436,12 @@ free_session(struct tsession *ts)
kill(ts->shell_pid, SIGKILL);
wait4(ts->shell_pid, NULL, 0, NULL);
#endif
+#ifdef ATLAS
+ if (ts->ptyfd != 0)
+ close(ts->ptyfd);
+#else
close(ts->ptyfd);
+#endif
close(ts->sockfd_read);
/* We do not need to close(ts->sockfd_write), it's the same
* as sockfd_read unless we are in inetd mode. But in inetd mode
@@ -365,19 +493,33 @@ static void handle_sigchld(int sig UNUSED_PARAM)
}
}
+static void kick_watchdog(void)
+{
+ int fdwatchdog = open("/dev/watchdog", O_RDWR);
+ if (fdwatchdog != -1)
+ {
+ write(fdwatchdog, "1", 1);
+ close(fdwatchdog);
+ }
+}
+
int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int telnetd_main(int argc UNUSED_PARAM, char **argv)
{
fd_set rdfdset, wrfdset;
unsigned opt;
- int count;
+ int count, probe_id;
+ int num_totty;
struct tsession *ts;
+ char *line;
#if ENABLE_FEATURE_TELNETD_STANDALONE
#define IS_INETD (opt & OPT_INETD)
int master_fd = master_fd; /* be happy, gcc */
unsigned portnbr = 23;
char *opt_bindaddr = NULL;
char *opt_portnbr;
+ const char *PidFileName = NULL;
+ char buf[80];
#else
enum {
IS_INETD = 1,
@@ -385,10 +527,12 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
portnbr = 23,
};
#endif
+ struct timeval tv;
+
/* Even if !STANDALONE, we accept (and ignore) -i, thus people
* don't need to guess whether it's ok to pass -i to us */
- opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
- &issuefile, &loginpath
+ opt = getopt32(argv, "f:l:KiP:" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
+ &issuefile, &loginpath, &PidFileName
USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
if (!IS_INETD /*&& !re_execed*/) {
/* inform that we start in standalone mode?
@@ -410,6 +554,11 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
portnbr = xatou16(opt_portnbr);
);
+ if(PidFileName)
+ {
+ write_pidfile(PidFileName);
+ }
+
/* Used to check access(loginpath, X_OK) here. Pointless.
* exec will do this for us for free later. */
@@ -458,6 +607,8 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
FD_ZERO(&rdfdset);
FD_ZERO(&wrfdset);
+ kick_watchdog();
+
/* Select on the master socket, all telnet sockets and their
* ptys if there is room in their session buffers.
* NB: scalability problem: we recalculate entire bitmap
@@ -469,13 +620,24 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
/* Child died and we detected that */
free_session(ts);
} else {
+#ifdef ATLAS
+ if (ts->size1 > 0 && ts->state == DO_TRADITIONAL)
+ /* can write to pty */
+#else
if (ts->size1 > 0) /* can write to pty */
+#endif
FD_SET(ts->ptyfd, &wrfdset);
if (ts->size1 < BUFSIZE) /* can read from socket */
FD_SET(ts->sockfd_read, &rdfdset);
if (ts->size2 > 0) /* can write to socket */
FD_SET(ts->sockfd_write, &wrfdset);
+#ifdef ATLAS
+ if (ts->size2 < BUFSIZE &&
+ ts->state == DO_TRADITIONAL)
+ /* can read from pty */
+#else
if (ts->size2 < BUFSIZE) /* can read from pty */
+#endif
FD_SET(ts->ptyfd, &rdfdset);
}
ts = next;
@@ -489,7 +651,9 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
maxfd = master_fd;
}
- count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
+ tv.tv_sec= 10;
+ tv.tv_usec= 0;
+ count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, &tv);
if (count < 0)
goto again; /* EINTR or ENOMEM */
@@ -505,6 +669,18 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
/* Create a new session and link it into our active list */
new_ts = make_new_session(fd);
if (new_ts) {
+#ifdef ATLAS
+ char *hostname;
+
+ hostname= safe_gethostname();
+ probe_id= get_probe_id();
+ add_2sock(new_ts, LOGIN_PREFIX);
+ snprintf(buf, sizeof(buf), "Probe %d (%s)",
+ probe_id, hostname);
+ add_2sock(new_ts, buf);
+ add_2sock(new_ts, LOGIN_PROMPT);
+ free(hostname);
+#endif /* ATLAS */
new_ts->next = sessions;
sessions = new_ts;
} else {
@@ -519,7 +695,6 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
struct tsession *next = ts->next; /* in case we free ts. */
if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
- int num_totty;
unsigned char *ptr;
/* Write to pty from buffer 1. */
ptr = remove_iacs(ts, &num_totty);
@@ -565,10 +740,22 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
ts->wridx2 = 0;
}
- if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
+ if (/*ts->size1 < BUFSIZE &&*/
+ FD_ISSET(ts->sockfd_read, &rdfdset)) {
+#ifdef ATLAS
+ if (ts->size1 < BUFSIZE &&
+ (ts->rdidx1 >= BUFSIZE ||
+ ts->rdidx1 < ts->wridx1))
+ {
+ pack_2pty(ts);
+ }
+#endif
+
/* Read from socket to buffer 1. */
count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
- count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
+
+ count = safe_read(ts->sockfd_read,
+ TS_BUF1 + ts->rdidx1, count);
if (count <= 0) {
if (count < 0 && errno == EAGAIN)
goto skip3;
@@ -584,6 +771,208 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
ts->rdidx1 = 0;
}
skip3:
+#ifdef ATLAS
+ switch(ts->state)
+ {
+ case DO_TRADITIONAL:
+ break; /* Nothing to do */
+ case GET_LOGINNAME:
+ {
+ unsigned char *ptr;
+
+ ptr = remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ if (strcmp(line, ATLAS_LOGIN) == 0)
+ {
+ free(line); line= NULL;
+ add_2sock(ts, PASSWORD_PROMPT);
+ ts->state= GET_PASSWORD;
+ goto skip3;
+ }
+ else
+ {
+ int r;
+
+ /* Echo login name */
+ add_2sock(ts, line);
+
+ r= start_login(ts, line);
+ free(line); line= NULL;
+ if (r == -1)
+ goto kill_session;
+ ts->state= DO_TRADITIONAL;
+ goto skip3a;
+ }
+
+ }
+ case GET_PASSWORD:
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ if (equal_sessionid(line))
+ {
+ free(line); line= NULL;
+
+ if (atlas_ts)
+ {
+ bb_error_msg("found atlas session");
+ /* There is an old session still
+ * around. Take over.
+ */
+ if (atlas_crontab)
+ {
+ fclose(atlas_crontab);
+ atlas_crontab= NULL;
+ }
+ }
+ atlas_ts= ts;
+
+ ts->state= GET_CMD;
+ goto skip3;
+ }
+ else
+ {
+ free(line); line= NULL;
+
+ /* Bad password, the end */
+ add_2sock(ts, BAD_PASSWORD);
+ goto skip3;
+ }
+
+ case GET_CMD:
+ {
+ int r;
+ size_t len;
+
+ if (ts != atlas_ts)
+ goto kill_session; /* Old session */
+
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+do_cmd:
+ len= strlen(CMD_CRONTAB);
+ if (strncmp(line, CMD_CRONTAB, len) == 0)
+ {
+ r= start_crontab(ts, line);
+ free(line); line= NULL;
+ if (r == -1)
+ {
+ /* Assume start_crontab sent an
+ * error response.
+ */
+ goto skip3;
+ }
+
+ ts->state= DO_CRONTAB;
+ goto skip3;
+ }
+
+ len= strlen(CMD_ONEOFF);
+ if (strncmp(line, CMD_ONEOFF, len) == 0)
+ {
+ do_oneoff(ts, line);
+ free(line); line= NULL;
+
+ /* Assume do_oneoff sent an error response
+ * if something was wrong.
+ */
+ goto skip3;
+ }
+ if (strcmp(line, CMD_REBOOT) == 0)
+ {
+ sync();
+ reboot(LINUX_REBOOT_CMD_RESTART);
+ free(line); line= NULL;
+
+ goto skip3;
+ }
+ if (strlen(line) == 0)
+ {
+ free(line); line= NULL;
+
+ /* End of request */
+ add_2sock(ts, RESULT_OK);
+
+ ts->state= EOM_SEEN;
+ goto skip3;
+ }
+
+ free(line); line= NULL;
+
+ /* Bad command */
+ add_2sock(ts, BAD_COMMAND);
+ goto skip3a;
+ }
+
+ case DO_CRONTAB:
+ {
+ size_t len;
+
+ if (ts != atlas_ts)
+ goto kill_session; /* Old session */
+
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ len= strlen(CMD_CRONLINE);
+ if (strncmp(line, CMD_CRONLINE, len) != 0)
+ {
+ end_crontab(ts);
+
+ /* Assume end_crontab sends a response
+ * if there was an error.
+ */
+
+ ts->state= GET_CMD;
+
+ /* Unfortunately, the line that ends the
+ * crontab is the next command.
+ */
+ goto do_cmd;
+ }
+
+ add_to_crontab(ts, line+len);
+ free(line); line= NULL;
+
+ /* And again */
+ goto skip3;
+ }
+
+ case EOM_SEEN:
+ if (ts != atlas_ts)
+ goto kill_session; /* Old session */
+
+ /* Just eat all input and return bad command */
+ remove_iacs(ts, &num_totty);
+
+ line= getline_2pty(ts);
+ if (!line)
+ goto skip3a;
+
+ free(line); line= NULL;
+ add_2sock(ts, BAD_COMMAND);
+ goto skip3;
+
+ default:
+ bb_error_msg("unknown state %d", ts->state);
+ abort();
+ }
+skip3a:
+#endif /* ATLAS */
if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
/* Read from pty to buffer 2. */
count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
@@ -602,9 +991,407 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
ts = next;
continue;
kill_session:
+#ifdef ATLAS
+ if (ts == atlas_ts)
+ {
+ if (atlas_crontab)
+ {
+ fclose(atlas_crontab);
+ atlas_crontab= NULL;
+ }
+ atlas_ts= NULL;
+ }
+#endif /* ATLAS */
free_session(ts);
ts = next;
}
goto again;
}
+
+#ifdef ATLAS
+static int equal_sessionid(char *passwd)
+{
+ size_t len;
+ char *cp;
+ FILE *file;
+ char line[80];
+
+ file= fopen(ATLAS_SESSION_FILE, "r");
+ if (file == NULL)
+ {
+ syslog(LOG_ERR, "unable to open '%s': %m", ATLAS_SESSION_FILE);
+ return 0;
+ }
+
+ if (fgets(line, sizeof(line), file) == NULL)
+ {
+ syslog(LOG_ERR, "unable to read from '%s': %m",
+ ATLAS_SESSION_FILE);
+ fclose(file);
+ return 0;
+ }
+ fclose(file);
+
+ len= strlen(SESSION_ID_PREFIX);
+ if (strlen(line) < len)
+ {
+ syslog(LOG_ERR, "not enough session ID data");
+ return 0;
+ }
+ if (memcmp(line, SESSION_ID_PREFIX, len) != 0)
+ {
+ syslog(LOG_ERR, "missing session ID prefix");
+ return 0;
+ }
+
+ cp= strchr(line, '\n');
+ if (cp == NULL)
+ {
+ syslog(LOG_ERR, "missing newline in session ID file");
+ return 0;
+ }
+ *cp= '\0';
+
+ if (strcmp(line+len, passwd) == 0)
+ return 1;
+
+ /* Wrong password */
+ return 0;
+}
+
+static void add_2sock(struct tsession *ts, const char *str)
+{
+ size_t len;
+
+ len= strlen(str);
+ if (ts->size2 + len > BUFSIZE)
+ {
+ syslog(LOG_ERR, "add_2sock: buffer full");
+ abort();
+ }
+ if (ts->rdidx2 + len > BUFSIZE)
+ pack_4sock();
+
+ memcpy(TS_BUF2+ts->rdidx2, str, len);
+ ts->rdidx2 += len;
+ ts->size2 += len;
+}
+
+static void pack_4sock(void)
+{
+ syslog(LOG_ERR, "pack_4sock: not implemented");
+ abort();
+}
+
+static char *getline_2pty(struct tsession *ts)
+{
+ size_t size1, len;
+ char *cp, *cp2, *line;
+
+ size1= ts->size1;
+
+
+ if (ts->wridx1 + size1 > BUFSIZE)
+ pack_2pty(ts);
+
+ /* remove_iacs converts a CR-LF to a CR */
+ cp= memchr(TS_BUF1+ts->wridx1, '\r', size1);
+ cp2= memchr(TS_BUF1+ts->wridx1, '\n', size1);
+ if (cp2 != NULL && (cp == NULL || cp2 < cp))
+ {
+ /* Use the LF. Patch '\n' to '\r' */
+ *cp2= '\r';
+ cp= cp2;
+ }
+ if (cp == NULL)
+ return NULL;
+
+ len= cp-((char *)TS_BUF1+ts->wridx1)+1;
+ line= xmalloc(len+1);
+ memcpy(line, (char *)TS_BUF1+ts->wridx1, len);
+ line[len]= '\0';
+
+ ts->wridx1 += len;
+ ts->size1 -= len;
+
+ /* Make sure that the line ends in a \r. If not, just ignore the
+ * line. Otherwise, delete the \r.
+ */
+ cp= strchr(line, '\r');
+ if (cp == NULL || cp-line != strlen(line)-1)
+ {
+ bb_error_msg("bad line '%s', cp %p, cp-line %ld, |line| %ld",
+ line, cp, (long)(cp-line), (long)strlen(line));
+
+ /* Bad line, just ignore it */
+ free(line); line= NULL;
+ return NULL;
+ }
+ *cp= '\0';
+
+ return line;
+}
+
+static void pack_2pty(struct tsession *ts)
+{
+ size_t size1, size_lo, size_hi, wridx1;
+
+ size1= ts->size1;
+ wridx1= ts->wridx1;
+
+ size_hi= BUFSIZE-wridx1; /* Amount at the top of the buffer */
+ size_lo= size1-size_hi;
+
+ /* Move the low part up a bit */
+ memmove(TS_BUF1+size_hi, TS_BUF1, size_lo);
+
+ /* Now move the high part down */
+ memmove(TS_BUF1, TS_BUF1+wridx1, size_hi);
+
+ /* Update wridx1 and rdidx1 */
+ ts->wridx1= 0;
+ ts->rdidx1= size1;
+}
+
+static int start_crontab(struct tsession *ts, char *line)
+{
+ size_t len;
+ char *cp;
+ char filename[256];
+
+ if (atlas_crontab)
+ {
+ add_2sock(ts, CRONTAB_BUSY);
+ return -1;
+ }
+
+ cp= line+strlen(CMD_CRONTAB);
+ len= strlen(cp);
+ if (len+1 > sizeof(atlas_dirname))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return -1;
+ }
+ strlcpy(atlas_dirname, cp, sizeof(atlas_dirname));
+
+ if (len + strlen(CRONTAB_NEW_SUF) + 1 > sizeof(filename))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return -1;
+ }
+
+ strlcpy(filename, atlas_dirname, sizeof(filename));
+ strlcat(filename, CRONTAB_NEW_SUF, sizeof(filename));
+
+ if (!validate_filename(filename, SAFE_PREFIX))
+ {
+ add_2sock(ts, BAD_PATH);
+ return -1;
+ }
+
+ atlas_crontab= fopen(filename, "w");
+ if (!atlas_crontab)
+ {
+ add_2sock(ts, CREATE_FAILED);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void add_to_crontab(struct tsession *ts, char *line)
+{
+ if (!atlas_crontab)
+ return; /* Some error occured earlier */
+ // fprintf(stderr, "telnetd: adding '%s' to crontab\n", line);
+ if (fputs(line, atlas_crontab) == -1 ||
+ fputc('\n', atlas_crontab) == -1)
+ {
+ add_2sock(ts, IO_ERROR);
+ fclose(atlas_crontab);
+ atlas_crontab= NULL;
+ return;
+ }
+}
+
+static void end_crontab(struct tsession *ts)
+{
+ int fd;
+ size_t len;
+ struct stat st;
+ char filename1[256];
+ char filename2[256];
+
+ if (!atlas_crontab)
+ return; /* Some error occured earlier */
+ if (fclose(atlas_crontab) == -1)
+ {
+ atlas_crontab= NULL;
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+ atlas_crontab= NULL;
+
+ /* Rename */
+ len= strlen(atlas_dirname);
+ if (len + strlen(CRONTAB_NEW_SUF) + 1 > sizeof(filename1))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ strlcpy(filename1, atlas_dirname, sizeof(filename1));
+ strlcat(filename1, CRONTAB_NEW_SUF, sizeof(filename1));
+ if (len + strlen(CRONTAB_SUFFIX) + 1 > sizeof(filename2))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ strlcpy(filename2, atlas_dirname, sizeof(filename2));
+ strlcat(filename2, CRONTAB_SUFFIX, sizeof(filename2));
+ if (rename(filename1, filename2) == -1)
+ {
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+
+ /* Inspired by the crontab command, tell cron to load the new
+ * crontab.
+ */
+ if (strlen(atlas_dirname) + strlen(CRONUPDATE) + 1 > sizeof(filename1))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+
+ strlcpy(filename1, atlas_dirname, sizeof(filename1));
+ strlcat(filename1, CRONUPDATE, sizeof(filename1));
+
+ while (fd= open(filename1, O_WRONLY|O_CREAT|O_TRUNC, 0600), fd >= 0)
+ {
+ len= strlen(UPDATELINE);
+ if (write(fd, UPDATELINE, len) != len)
+ {
+ close(fd);
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+ if (fstat(fd, &st) != 0)
+ {
+ close(fd);
+ add_2sock(ts, IO_ERROR);
+ return;
+ }
+ close(fd);
+ if (st.st_nlink != 0)
+ break;
+
+ /* Race condition, try again */
+ }
+
+ if (fd < 0)
+ {
+ add_2sock(ts, CREATE_FAILED);
+ return;
+ }
+}
+
+static void do_oneoff(struct tsession *ts, char *line)
+{
+ size_t len;
+ char *cp, *ncp;
+ FILE *file;
+ char filename[256];
+ char filename_new[256];
+
+ cp= line+strlen(CMD_ONEOFF);
+
+ /* Find the end of the filename */
+ ncp= cp;
+ while (ncp[0] != '\0' && !isspace((unsigned char)ncp[0]))
+ ncp++;
+
+ len= ncp-cp;
+ if (len+1 > sizeof(filename))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ memcpy(filename, cp, len);
+ filename[len]= '\0';
+
+ if (len + strlen(ONEOFF_SUFFIX) + 1 > sizeof(filename_new))
+ {
+ add_2sock(ts, NAME_TOO_LONG);
+ return;
+ }
+ strlcpy(filename_new, filename, sizeof(filename_new));
+ strlcat(filename_new, ONEOFF_SUFFIX, sizeof(filename_new));
+
+ if (!validate_filename(filename, SAFE_PREFIX))
+ {
+ add_2sock(ts, BAD_PATH);
+ return;
+ }
+
+ /* Try to grab 'filename', if there is any. It doesn't matter if this
+ * fails.
+ */
+ rename(filename, filename_new);
+
+ file= fopen(filename_new, "a");
+ if (!file)
+ {
+ add_2sock(ts, CREATE_FAILED);
+ return;
+ }
+
+ /* Find start of command */
+ cp= ncp;
+ while (cp[0] != '\0' && isspace((unsigned char)cp[0]))
+ cp++;
+
+ if (fprintf(file, "%s\n", cp) == -1)
+ {
+ add_2sock(ts, IO_ERROR);
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+
+ /* And rename back. Ignore any errors */
+ rename(filename_new, filename);
+}
+
+static int get_probe_id(void)
+{
+ int probe_id;
+ size_t len;
+ char *check;
+ const char *key;
+ FILE *fp;
+ char buf[80];
+
+ fp= fopen("/home/atlas/status/reg_init_reply.txt", "r");
+ if (!fp)
+ return -1;
+
+ probe_id= -1;
+ while (fgets(buf, sizeof(buf), fp) != NULL)
+ {
+ if (strchr(buf, '\n') == NULL)
+ continue;
+ key= "PROBE_ID ";
+ len= strlen(key);
+
+ if (strncmp(buf, key, len) != 0 || strlen(buf) <= len)
+ continue;
+ probe_id= strtol(buf+len, &check, 10);
+ break;
+ }
+ fclose(fp);
+ return probe_id;
+}
+
+#endif /* ATLAS */
diff --git a/networking/tftp.c b/networking/tftp.c
index 1f70685..7f837eb 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -626,6 +626,7 @@ int tftp_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_TFTPD
/* TODO: libbb candidate? */
+#if 0
static len_and_sockaddr *get_sock_lsa(int s)
{
len_and_sockaddr *lsa;
@@ -638,6 +639,7 @@ static len_and_sockaddr *get_sock_lsa(int s)
getsockname(s, &lsa->u.sa, &lsa->len);
return lsa;
}
+#endif
int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int tftpd_main(int argc UNUSED_PARAM, char **argv)