diff options
Diffstat (limited to 'networking/httppost.c')
-rw-r--r-- | networking/httppost.c | 1283 |
1 files changed, 1283 insertions, 0 deletions
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); +} |