diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4c28c04 --- /dev/null +++ b/Makefile @@ -0,0 +1,155 @@ +# The GPL applies to this program. +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the +# OpenSSL library under certain conditions as described in each +# individual source file, and distribute linked combinations +# including the two. +# You must obey the GNU General Public License in all respects +# for all of the code used other than OpenSSL. If you modify +# file(s) with this exception, you may extend this exception to your +# version of the file(s), but you are not obligated to do so. If you +# do not wish to do so, delete this exception statement from your +# version. If you delete this exception statement from all source +# files in the program, then also delete it here. +# $Revision: 278 $ + +-include makefile.inc + +# *** configure script *** +# support for tcp fast open? +#TFO=yes +# disable SSL? (no = disable so the default is use openssl) +# SSL=no +# enable NCURSES interface? +#NC=yes +# do fft in ncurses interface? (requires libfftw3) +#FW=yes + +############# do not change anything below here ############# + +include version + +TARGET=httping + +LOCALEDIR=/usr/share/locale + +DEBUG=yes +WFLAGS=-Wall -W -Wextra -pedantic -D_FORTIFY_SOURCE=2 +OFLAGS= +CFLAGS+=$(WFLAGS) $(OFLAGS) -DVERSION=\"$(VERSION)\" -DLOCALEDIR=\"$(LOCALEDIR)\" +LDFLAGS+=-lm + +PACKAGE=$(TARGET)-$(VERSION) +PREFIX?=/usr +BINDIR=$(PREFIX)/bin +MANDIR=$(PREFIX)/share/man +DOCDIR=$(PREFIX)/share/doc/$(TARGET) + +INSTALL=install +INSTALLDIR=$(INSTALL) -m 0755 -d +INSTALLBIN=$(INSTALL) -m 0755 +INSTALLMAN=$(INSTALL) -m 0644 +INSTALLDOC=$(INSTALL) -m 0644 +STRIP=/usr/bin/strip +RMDIR=/bin/rm -rf +MKDIR=/bin/mkdir +ARCHIVE=/bin/tar cf - +COMPRESS=/bin/gzip -9 + +TRANSLATIONS=nl.mo + +OBJS=gen.o http.o io.o error.o utils.o main.o tcp.o res.o socks5.o kalman.o cookies.o help.o colors.o + +MAN_EN=httping.1 +MAN_NL=httping-nl.1 + +DOCS=license.txt license.OpenSSL readme.txt + +ifeq ($(SSL),no) +CFLAGS+=-DNO_SSL +else +OBJS+=mssl.o +LDFLAGS+=-lssl -lcrypto +endif + +ifeq ($(TFO),yes) +CFLAGS+=-DTCP_TFO +endif + +ifeq ($(NC),yes) +CFLAGS+=-DNC +OBJS+=nc.o +LDFLAGS+=-lncursesw +endif + +ifeq ($(FW),yes) +CFLAGS+=-DFW +OBJS+=fft.o +LDFLAGS+=-lfftw3 +endif + +ifeq ($(DEBUG),yes) +CFLAGS+=-D_DEBUG -ggdb +LDFLAGS+=-g +endif + +ifeq ($(ARM),yes) +CC=arm-linux-gcc +endif + +all: $(TARGET) $(TRANSLATIONS) + +$(TARGET): $(OBJS) + $(CC) $(WFLAGS) $(OBJS) $(LDFLAGS) -o $(TARGET) + # + # Oh, blatant plug: http://www.vanheusden.com/wishlist.php + +install: $(TARGET) $(TRANSLATIONS) + $(INSTALLDIR) $(DESTDIR)/$(BINDIR) + $(INSTALLBIN) $(TARGET) $(DESTDIR)/$(BINDIR) + $(INSTALLDIR) $(DESTDIR)/$(MANDIR)/man1 + $(INSTALLMAN) $(MAN_EN) $(DESTDIR)/$(MANDIR)/man1 + $(INSTALLDIR) $(DESTDIR)/$(MANDIR)/nl/man1 + $(INSTALLMAN) $(MAN_NL) $(DESTDIR)/$(MANDIR)/nl/man1 + $(INSTALLDIR) $(DESTDIR)/$(DOCDIR) + $(INSTALLDOC) $(DOCS) $(DESTDIR)/$(DOCDIR) +ifneq ($(DEBUG),yes) + $(STRIP) $(DESTDIR)/$(BINDIR)/$(TARGET) +endif + mkdir -p $(DESTDIR)/$(PREFIX)/share/locale/nl/LC_MESSAGES + cp nl.mo $(DESTDIR)/$(PREFIX)/share/locale/nl/LC_MESSAGES/httping.mo + +makefile.inc: + ./configure + +nl.mo: nl.po + msgfmt -o nl.mo nl.po + +clean: + $(RMDIR) $(OBJS) $(TARGET) *~ core cov-int *.mo + +distclean: clean + rm -f makefile.inc + +package: + # source package + $(RMDIR) $(PACKAGE)* + $(MKDIR) $(PACKAGE) + $(INSTALLDOC) *.c *.h configure Makefile *.po version $(MAN_EN) $(MAN_NL) $(DOCS) $(PACKAGE) + $(INSTALLBIN) configure $(PACKAGE) + $(ARCHIVE) $(PACKAGE) | $(COMPRESS) > $(PACKAGE).tgz + $(RMDIR) $(PACKAGE) + +check: makefile.inc + cppcheck -v --force -j 3 --enable=all --std=c++11 --inconclusive -I. . 2> err.txt + # + make clean + scan-build make + +coverity: makefile.inc + make clean + rm -rf cov-int + CC=gcc cov-build --dir cov-int make all + tar vczf ~/site/coverity/httping.tgz README cov-int/ + putsite -q + /home/folkert/.coverity-hp.sh diff --git a/colors.c b/colors.c new file mode 100644 index 0000000..e984bca --- /dev/null +++ b/colors.c @@ -0,0 +1,51 @@ +#include "colors.h" + +const char *c_error = ""; +const char *c_normal = ""; +const char *c_very_normal = ""; +const char *c_red = ""; +const char *c_blue = ""; +const char *c_green = ""; +const char *c_yellow = ""; +const char *c_magenta = ""; +const char *c_cyan = ""; +const char *c_white = ""; +const char *c_bright = ""; + +void set_colors(char nc) +{ + if (nc) + { + c_red = COLOR_ESCAPE "1"; + c_blue = COLOR_ESCAPE "2"; + c_green = COLOR_ESCAPE "3"; + c_yellow = COLOR_ESCAPE "4"; + c_magenta = COLOR_ESCAPE "5"; + c_cyan = COLOR_ESCAPE "6"; + c_white = COLOR_ESCAPE "7"; + + c_bright = COLOR_ESCAPE "8"; + c_normal = COLOR_ESCAPE "9"; + + c_very_normal = COLOR_ESCAPE "7" COLOR_ESCAPE "9"; + + c_error = COLOR_ESCAPE "1"; + } + else + { + c_red = "\033[31;40m"; + c_blue = "\033[34;40m"; + c_green = "\033[32;40m"; + c_yellow = "\033[33;40m"; + c_magenta = "\033[35;40m"; + c_cyan = "\033[36;40m"; + c_white = "\033[37;40m"; + + c_bright = "\033[1;40m"; + c_normal = "\033[0;37;40m"; + + c_very_normal = "\033[0m"; + + c_error = "\033[1;4;40m"; + } +} diff --git a/colors.h b/colors.h new file mode 100644 index 0000000..9343098 --- /dev/null +++ b/colors.h @@ -0,0 +1,15 @@ +extern const char *c_error; +extern const char *c_normal; +extern const char *c_very_normal; +extern const char *c_red; +extern const char *c_blue; +extern const char *c_green; +extern const char *c_yellow; +extern const char *c_magenta; +extern const char *c_cyan; +extern const char *c_white; +extern const char *c_bright; + +#define COLOR_ESCAPE "\001" + +void set_colors(char nc); diff --git a/configure b/configure new file mode 100755 index 0000000..451c2b2 --- /dev/null +++ b/configure @@ -0,0 +1,96 @@ +#! /bin/sh + +FILE=`mktemp` +FILE2=`mktemp` + +echo \*\*\* HTTPing v`grep VERSION version | cut -d = -f 2` \(`echo $Revision$ | awk '{ print $2; }'`\) configure script \*\*\* +echo + +if [ -z "$CC" ] +then + CC=gcc +fi + +F_TFO=0 +F_NC=0 +F_OS=0 +F_FW=0 +for var in "$@" +do + case "$var" in + --with-tfo) + F_TFO=1 + ;; + + --with-ncurses) + F_NC=1 + ;; + + --with-openssl) + F_OS=1 + ;; + + --with-fftw3) + F_FW=1 + ;; + + --help) + echo "--with-tfo force enable tcp fast open" + echo "--with-ncurses force enable ncurses" + echo "--with-openssl force enable openssl" + echo "--with-fftw3 force enable fftw3" + exit 0 + ;; + + *) + echo WARNING: Command line parameter \"$var\" is not understood. + echo Re-run this script with --help to see a list of switches. + ;; + esac +done + +$CC -O0 -o $FILE test_TFO.c 2> $FILE2 +if [ $? -eq 0 ] || [ $F_TFO -eq 1 ] ; then + echo \+ system supports TCP fast open + TFO="TFO=yes" +else + echo \- this system does NOT support TCP fast open - this is an optional feature + TFO="" +fi + +$CC -O0 -lncursesw -o $FILE test_ncurses.c 2> $FILE2 +if [ $? -eq 0 ] || [ $F_NC -eq 1 ] ; then + echo \+ system has ncurses development libraries + NC="NC=yes" +else + echo \- this system does NOT have the ncurses development libraries - they are optional + NC="" +fi + +$CC -O0 -lssl -lcrypto -o $FILE test_openssl.c 2> $FILE2 +if [ $? -eq 0 ] || [ $F_OS -eq 1 ] ; then + echo \+ system has OpenSSL development libraries + SSL="SSL=yes" +else + echo \- this system does NOT have the OpenSSL development libraries - they are optional + SSL="SSL=no" +fi + +$CC -O0 -lfftw3 -o $FILE test_fftw3.c 2> $FILE2 +if [ $? -eq 0 ] || [ $F_FW -eq 1 ] ; then + echo \+ system has FFTW3 development libraries + FW="FW=yes" +else + echo \- this system does NOT have the FFTW3 development libraries - they are optional and only useful when also including ncurses + FW="" +fi + +> makefile.inc +echo $NC >> makefile.inc +echo $SSL >> makefile.inc +echo $TFO >> makefile.inc +echo $FW >> makefile.inc + +rm -f $FILE $FILE2 + +echo diff --git a/cookies.c b/cookies.c new file mode 100644 index 0000000..6597ceb --- /dev/null +++ b/cookies.c @@ -0,0 +1,129 @@ +#include +#include + +#include "utils.h" + +void add_cookie(char ***cookies, int *n_cookies, char *in) +{ + char *in_copy = strdup(in), *is = strchr(in_copy, '='); + int index = 0, found_at = -1; + + if (is) + *is = 0x00; + + for(index=0; index<*n_cookies; index++) + { + char *dummy = strdup((*cookies)[index]); + + is = strchr(dummy, '='); + if (is) + *is = 0x00; + + if (strcmp(in_copy, dummy) == 0) + { + found_at = index; + free(dummy); + break; + } + + free(dummy); + } + + if (found_at >= 0) + { + free((*cookies)[found_at]); + + (*cookies)[found_at] = strdup(in); + } + else + { + *cookies = (char **)realloc(*cookies, (*n_cookies + 1) * sizeof(char *)); + + (*cookies)[*n_cookies] = strdup(in); + + (*n_cookies)++; + } + + free(in_copy); +} + +void combine_cookie_lists(char ***destc, int *n_dest, char **src, int n_src) +{ + int loop = 0; + + *destc = (char **)realloc(*destc, (*n_dest + n_src) * sizeof(char *)); + + for(loop=0; loop +#include +#include +#include +#include +#include +#include +#include + +char last_error[4096] = { 0 }; + +void error_exit(char *format, ...) +{ + int e = errno; + va_list ap; + + va_start(ap, format); + (void)vfprintf(stderr, format, ap); + va_end(ap); + + fprintf(stderr, gettext("\n\nerrno=%d which means %s (if applicable)\n"), e, strerror(e)); + + exit(1); +} + +void set_error(const char *fmt, ...) +{ + int buffer_size = sizeof last_error; + va_list ap; + + if (last_error[0]) + fprintf(stderr, "%s\n", last_error); + + va_start(ap, fmt); + if (vsnprintf(last_error, sizeof last_error, fmt, ap) >= buffer_size) + error_exit(gettext("Error message '%s' truncated"), last_error); + va_end(ap); +} + +void clear_error() +{ + last_error[0] = 0x00; +} + +char * get_error() +{ + return last_error; +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..361c9fe --- /dev/null +++ b/error.h @@ -0,0 +1,7 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +void error_exit(const char *format, ...); +void set_error(const char *str, ...); +void clear_error(void); +char * get_error(void); diff --git a/fft.c b/fft.c new file mode 100644 index 0000000..579e25b --- /dev/null +++ b/fft.c @@ -0,0 +1,69 @@ +/* $Revision$ */ +#include +#include +#include +#include +#include + +#include "error.h" + +double *pin = NULL; +fftw_complex *pout = NULL; +fftw_plan plan; +int sample_rate = 44100; + +void fft_init(int sample_rate_in) +{ + sample_rate = sample_rate_in; + + pin = (double *)malloc(sizeof(double) * sample_rate_in); + if (!pin) + error_exit(gettext("failed allocating memory for fft (1)")); + + pout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * sample_rate_in + 1); + if (!pout) + error_exit(gettext("failed allocating memory for fft (2)")); + + /* init fftw */ + plan = fftw_plan_dft_r2c_1d(sample_rate_in, pin, pout, FFTW_ESTIMATE); + if (!plan) + error_exit(gettext("failed calculating plan for fft")); +} + +void fft_free(void) +{ + if (pin) + { + fftw_free(pout); + free(pin); + } + + fftw_destroy_plan(plan); +} + +void fft_stop(void) +{ + fftw_cleanup(); +} + +void fft_do(double *in, double *output_mag, double *output_phase) +{ + int loop = 0; + + memcpy(pin, in, sizeof(double) * sample_rate); + + /* calc fft */ + fftw_execute(plan); + + for(loop=0; loop<(sample_rate / 2) + 1; loop++) + { + double real = pout[loop][0]; + double img = pout[loop][1]; + + /* magnitude */ + output_mag[loop] = sqrt(pow(real, 2.0) + pow(img, 2.0)); + + /* phase */ + output_phase[loop] = (real == 0 && img == 0) ? 0 : atan2(real, img); + } +} diff --git a/fft.h b/fft.h new file mode 100644 index 0000000..38e014d --- /dev/null +++ b/fft.h @@ -0,0 +1,5 @@ +/* $Revision$ */ +void fft_init(int sample_rate_in); +void fft_free(void); +void fft_do(double *in, double *output_mag, double *output_phase); +void fft_stop(void); diff --git a/gen.c b/gen.c new file mode 100644 index 0000000..1a93480 --- /dev/null +++ b/gen.c @@ -0,0 +1,85 @@ +/* $Revision$ */ +#include +#include + +#include "gen.h" + +void init_statst(stats_t *data) +{ + memset(data, 0x00, sizeof(stats_t)); + + data -> min = MY_DOUBLE_INF; + data -> max = -data -> min; +} + +void update_statst(stats_t *data, double in) +{ + data -> cur = in; + data -> min = min(data -> min, in); + data -> max = max(data -> max, in); + data -> avg += in; + data -> sd += in * in; + (data -> n)++; + data -> valid = 1; + data -> cur_valid = 1; +} + +void reset_statst_cur(stats_t *data) +{ + data -> cur_valid = 0; +} + +double calc_sd(stats_t *in) +{ + double avg = 0.0; + + if (in -> n == 0 || !in -> valid) + return 0; + + avg = in -> avg / (double)in -> n; + + return sqrt((in -> sd / (double)in -> n) - pow(avg, 2.0)); +} + +/* Base64 encoding start */ +const char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void encode_tryptique(char source[3], char result[4]) +/* Encode 3 char in B64, result give 4 Char */ + { + int tryptique, i; + tryptique = source[0]; + tryptique *= 256; + tryptique += source[1]; + tryptique *= 256; + tryptique += source[2]; + for (i=0; i<4; i++) + { + result[3-i] = alphabet[tryptique%64]; + tryptique /= 64; + } +} + +int enc_b64(char *source, int source_lenght, char *target) +{ + /* Divide string /3 and encode trio */ + while (source_lenght >= 3) { + encode_tryptique(source, target); + source_lenght -= 3; + source += 3; + target += 4; + } + /* Add padding to the rest */ + if (source_lenght > 0) { + char pad[3]; + memset(pad, 0, sizeof pad); + memcpy(pad, source, source_lenght); + encode_tryptique(pad, target); + target[3] = '='; + if (source_lenght == 1) target[2] = '='; + target += 4; + } + target[0] = 0; + return 1; +} +/* Base64 encoding END */ diff --git a/gen.h b/gen.h new file mode 100644 index 0000000..c68b59f --- /dev/null +++ b/gen.h @@ -0,0 +1,59 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#ifndef __GEN_H__ +#define __GEN_H__ + +#define RC_OK 0 +#define RC_SHORTREAD -1 +#define RC_SHORTWRITE -1 +#define RC_TIMEOUT -2 +#define RC_CTRLC -3 +#define RC_INVAL -4 + +#define RECV_BUFFER_SIZE (128 * 1024) + +#define SPAM_FILE "/tmp/httping.dat" + +#define MAX_SHOW_SUPPRESSION 3 + +#ifdef NO_SSL + #define SSL void + #define SSL_CTX void + #define BIO void +#endif + +#define PI (4 * atan(1.0)) +#define MY_DOUBLE_INF 999999999999999.9 + +#ifdef TCP_TFO + #ifndef MSG_FASTOPEN + #define MSG_FASTOPEN 0x20000000 + #endif + + #ifndef TCP_FASTOPEN + #define TCP_FASTOPEN 23 + #endif + #ifndef TCPI_OPT_SYN_DATA + #define TCPI_OPT_SYN_DATA 32 + #endif +#endif + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + +typedef struct +{ + double cur, min, avg, max, sd; + int n; + char valid, cur_valid; +} stats_t; + +int enc_b64(char *source, int source_lenght, char *target); + +void init_statst(stats_t *data); +void update_statst(stats_t *data, double in); +void reset_statst_cur(stats_t *data); +double calc_sd(stats_t *in); + +#endif diff --git a/help.c b/help.c new file mode 100644 index 0000000..2052117 --- /dev/null +++ b/help.c @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include +#include + +#include "gen.h" +#include "main.h" +#include "help.h" +#include "utils.h" + +void new_version_alert(void) +{ + char new_version = 0; + FILE *fh = fopen(SPAM_FILE, "r"); + if (!fh) + new_version = 1; + else + { + char buffer[4096], *dummy = 0x00; + + fgets(buffer, sizeof buffer, fh); + + fclose(fh); + + dummy = strchr(buffer, '\n'); + if (dummy) + *dummy = 0x00; + + if (strcmp(buffer, VERSION) != 0) + new_version = 1; + } + + if (new_version) + { + struct utsname buf; + FILE *fh = fopen(SPAM_FILE, "w"); + if (fh) + { + fprintf(fh, "%s\n", VERSION); + + fclose(fh); + } + + printf("Welcome to the new HTTPing version " VERSION "!\n\n"); +#ifdef NC + printf("Did you know that with -K you can start a fullscreen GUI version with nice graphs and lots more information? And that you can disable the moving graphs with -D?\n"); +#ifndef FW + printf("And if you compile this program with libfftw3, that it can also show a fourier transform of the measured values?\n"); +#endif +#else + printf("Did you know that if you compile this program with NCURSES, that it then includes a nice GUI with lots more information and graphs?\n"); +#endif + +#if !defined(TCP_TFO) && defined(linux) + if (uname(&buf) == 0) + { + char **rparts = NULL; + int n_rparts = 0; + + split_string(buf.release, ".", &rparts, &n_rparts); + + if (n_rparts >= 2 && ((atoi(rparts[0]) >= 3 && atoi(rparts[1]) >= 6) || atoi(rparts[0]) >= 4)) + printf("This program supports TCP Fast Open! (if compiled in and only on Linux kernels 3.6 or more recent) See the readme.txt how to enable this.\n"); + + free_splitted_string(rparts, n_rparts); + } +#endif + + printf("\n\n"); + } +} + +void version(void) +{ + fprintf(stderr, gettext("HTTPing v" VERSION ", (C) 2003-2013 folkert@vanheusden.com\n")); +#ifndef NO_SSL + fprintf(stderr, gettext(" * SSL support included (-l)\n")); +#endif + +#ifdef NC +#ifdef FW + fprintf(stderr, gettext(" * ncurses interface with FFT included (-K)\n")); +#else + fprintf(stderr, gettext(" * ncurses interface included (-K)\n")); +#endif +#endif + +#ifdef TCP_TFO + fprintf(stderr, gettext(" * TFO (TCP fast open) support included (-F)\n")); +#endif + fprintf(stderr, gettext("\n")); +} + +void format_help(const char *short_str, const char *long_str, const char *descr) +{ + int par_width = SWITCHES_COLUMN_WIDTH, max_wrap_width = par_width / 2, cur_par_width = 0; + int descr_width = max_x - (par_width + 1); + char *line = NULL, *p = (char *)descr; + char first = 1; + + if (long_str && short_str) + str_add(&line, "%-4s / %s", short_str, long_str); + else if (long_str) + str_add(&line, "%s", long_str); + else + str_add(&line, "%s", short_str); + + cur_par_width = fprintf(stderr, "%-*s ", par_width, line); + + free(line); + + if (par_width + 1 >= max_x || cur_par_width >= max_x) + { + fprintf(stderr, "%s\n", descr); + return; + } + + for(;strlen(p);) + { + char *n = NULL, *kn = NULL, *copy = NULL; + int n_len = 0, len_after_ww = 0, len_before_ww = 0; + int str_len = 0, cur_descr_width = first ? max_x - cur_par_width : descr_width; + + while(*p == ' ') + p++; + + str_len = strlen(p); + if (!str_len) + break; + + len_before_ww = min(str_len, cur_descr_width); + + n = &p[len_before_ww]; + kn = n; + + if (str_len > cur_descr_width) + { + while (*n != ' ' && n_len < max_wrap_width) + { + n--; + n_len++; + } + + if (n_len >= max_wrap_width) + n = kn; + } + + len_after_ww = (int)(n - p); + if (len_after_ww <= 0) + break; + + copy = (char *)malloc(len_after_ww + 1); + memcpy(copy, p, len_after_ww); + copy[len_after_ww] = 0x00; + + if (first) + first = 0; + else + fprintf(stderr, "%*s ", par_width, ""); + + fprintf(stderr, "%s\n", copy); + + free(copy); + + p = n; + } +} + +void usage(const char *me) +{ + char *dummy = NULL, has_color = 0; + char host[256] = { 0 }; + + /* where to connect to */ + fprintf(stderr, gettext(" *** where to connect to ***\n")); + format_help("-g x", "--url", gettext("URL to ping (e.g. -g http://localhost/)")); + format_help("-h x", "--hostname", gettext("hostname to ping (e.g. localhost) - use either -g or -h")); + format_help("-p x", "--port", gettext("portnumber (e.g. 80) - use with -h")); + format_help("-6", "--ipv6", gettext("use IPv6 when resolving/connecting")); +#ifndef NO_SSL + format_help("-l", "--use-ssl", gettext("connect using SSL. pinging an https URL automatically enables this setting")); +#endif + fprintf(stderr, gettext("\n")); + + /* proxy settings */ + fprintf(stderr, gettext(" *** proxy settings ***\n")); + format_help("-x x", "--proxy", gettext("x should be \"host:port\" which are the network settings of the http/https proxy server. ipv6 ip-address should be \"[ip:address]:port\"")); + format_help("-E", NULL, gettext("fetch proxy settings from environment variables")); + format_help(NULL, "--proxy-user x", gettext("username for authentication against proxy")); + format_help(NULL, "--proxy-password x", gettext("password for authentication against proxy")); + format_help(NULL, "--proxy-password-file x", gettext("read password for proxy authentication from file x")); + format_help("-5", NULL, gettext("proxy is a socks5 server")); + format_help(NULL, "--proxy-buster x", gettext("adds \"&x=[random value]\" to the request URL")); + fprintf(stderr, gettext("\n")); + + /* timing settings */ + fprintf(stderr, gettext(" *** timing settings ***\n")); + format_help("-c x", "--count", gettext("how many times to ping")); + format_help("-i x", "--interval", gettext("delay between each ping")); + format_help("-t x", "--timeout", gettext("timeout (default: 30s)")); + format_help(NULL, "--ai / --adaptive-interval", gettext("execute pings at multiples of interval relative to start, automatically enabled in ncurses output mode")); + format_help("-f", "--flood", gettext("flood connect (no delays)")); + fprintf(stderr, gettext("\n")); + + /* http settings */ + fprintf(stderr, gettext(" *** HTTP settings ***\n")); + format_help("-Z", "--no-cache", gettext("ask any proxies on the way not to cache the requests")); + format_help(NULL, "--divert-connect", gettext("connect to a different host than in the URL given")); + format_help(NULL, "--keep-cookies", gettext("return the cookies given by the HTTP server in the following request(s)")); + format_help(NULL, "--no-host-header", gettext("do not add \"Host:\"-line to the request headers")); + format_help("-Q", "--persistent-connections", gettext("use a persistent connection. adds a 'C' to the output if httping had to reconnect")); + format_help("-I x", "--user-agent", gettext("use 'x' for the UserAgent header")); + format_help("-R x", "--referer", gettext("use 'x' for the Referer header")); + format_help(NULL, "--header", gettext("adds an extra request-header")); + fprintf(stderr, gettext("\n")); + + /* network settings */ + fprintf(stderr, gettext(" *** networking settings ***\n")); + format_help(NULL, "--max-mtu", gettext("limit the MTU size")); + format_help(NULL, "--no-tcp-nodelay", gettext("do not disable Naggle")); + format_help(NULL, "--recv-buffer", gettext("receive buffer size")); + format_help(NULL, "--tx-buffer", gettext("transmit buffer size")); + format_help("-r", "--resolve-once", gettext("resolve hostname only once (useful when pinging roundrobin DNS: also takes the first DNS lookup out of the loop so that the first measurement is also correct)")); + format_help("-W", NULL, gettext("do not abort the program if resolving failed: keep retrying")); + format_help("-y x", "--bind-to", gettext("bind to an ip-address (and thus interface) with an optional port")); +#ifdef TCP_TFO + format_help("-F", "--tcp-fast-open", gettext("\"TCP fast open\" (TFO), reduces the latency of TCP connects")); +#endif +#ifdef linux + format_help(NULL, "--priority", gettext("set priority of packets")); +#endif + format_help(NULL, "--tos", gettext("set TOS (type of service)")); + fprintf(stderr, gettext("\n")); + + /* http authentication */ + fprintf(stderr, gettext(" *** HTTP authentication ***\n")); + format_help("-A", "--basic-auth", gettext("activate (\"basic\") authentication")); + format_help("-U x", "--username", gettext("username for authentication")); + format_help("-P x", "--password", gettext("password for authentication")); + format_help("-T x", NULL, gettext("read the password fom the file 'x' (replacement for -P)")); + fprintf(stderr, gettext("\n")); + + /* output settings */ + fprintf(stderr, gettext(" *** output settings ***\n")); + format_help("-s", "--show-statuscodes", gettext("show statuscodes")); + format_help("-S", "--split-time", gettext("split measured time in its individual components (resolve, connect, send, etc.")); + format_help(NULL, "--threshold-red", gettext("from what ping value to show the value in red (must be bigger than yellow), only in color mode (-Y)")); + format_help(NULL, "--threshold-yellow", gettext("from what ping value to show the value in yellow")); + format_help(NULL, "--threshold-show", gettext("from what ping value to show the results")); + format_help(NULL, "--timestamp / --ts", gettext("put a timestamp before the measured values, use -v to include the date and -vv to show in microseconds")); + format_help(NULL, "--aggregate x[,y[,z]]", gettext("show an aggregate each x[/y[/z[/etc]]] seconds")); +#ifndef NO_SSL + format_help("-z", "--show-fingerprint", gettext("show fingerprint (SSL)")); +#endif + format_help("-v", NULL, gettext("verbose mode")); + fprintf(stderr, gettext("\n")); + + /* GET settings */ + fprintf(stderr, gettext(" *** \"GET\" (instead of HTTP \"HEAD\") settings ***\n")); + format_help("-G", "--get-request", gettext("do a GET request instead of HEAD (read the contents of the page as well)")); + format_help("-b", "--show-transfer-speed", gettext("show transfer speed in KB/s (use with -G)")); + format_help("-B", "--show-xfer-speed-compressed", gettext("like -b but use compression if available")); + format_help("-L x", "--data-limit", gettext("limit the amount of data transferred (for -b) to 'x' (in bytes)")); + format_help("-X", "--show-kb", gettext("show the number of KB transferred (for -b)")); + fprintf(stderr, gettext("\n")); + + /* output mode settings */ + fprintf(stderr, gettext(" *** output mode settings ***\n")); + format_help("-q", "--quiet", gettext("quiet, only returncode")); + format_help("-m", "--parseable-output", gettext("give machine parseable output (see also -o and -e)")); + format_help("-M", NULL, gettext("json output, cannot be combined with -m")); + format_help("-o rc,rc,...", "--ok-result-codes", gettext("what http results codes indicate 'ok' comma seperated WITHOUT spaces inbetween default is 200, use with -e")); + format_help("-e x", "--result-string", gettext("string to display when http result code doesn't match")); + format_help("-n warn,crit", "--nagios-mode-1 / --nagios-mode-2", gettext("Nagios-mode: return 1 when avg. response time >= warn, 2 if >= crit, otherwhise return 0")); + format_help("-N x", NULL, gettext("Nagios mode 2: return 0 when all fine, 'x' when anything failes")); + format_help("-C cookie=value", "--cookie", gettext("add a cookie to the request")); + format_help("-Y", "--colors", gettext("add colors")); + format_help("-a", "--audible-ping", gettext("audible ping")); + fprintf(stderr, gettext("\n")); + + /* GUI/ncurses mode */ +#if defined(NC) + fprintf(stderr, gettext(" *** GUI/ncurses mode settings ***\n")); + format_help("-K", "--ncurses / --gui", gettext("ncurses/GUI mode")); +#if defined(FW) + format_help(NULL, "--draw-phase", gettext("draw phase (fourier transform) in gui")); +#endif + format_help(NULL, "--slow-log", gettext("when the duration is x or more, show ping line in the slow log window (the middle window)")); + format_help(NULL, "--graph-limit x", gettext("do not scale to values above x")); + format_help("-D", "--no-graph", gettext("do not show graphs (in ncurses/GUI mode)")); + fprintf(stderr, gettext("\n")); +#endif + + format_help("-V", "--version", gettext("show the version")); + fprintf(stderr, gettext("\n")); + + dummy = getenv("TERM"); + if (dummy) + { + if (strstr(dummy, "ANSI") || strstr(dummy, "xterm") || strstr(dummy, "screen")) + has_color = 1; + } + + if (gethostname(host, sizeof host)) + strcpy(host, "localhost"); + + fprintf(stderr, gettext("Example:\n")); + fprintf(stderr, "\t%s %s%s -s -Z\n\n", me, host, has_color ? " -Y" : ""); + + new_version_alert(); +} diff --git a/help.h b/help.h new file mode 100644 index 0000000..201cf62 --- /dev/null +++ b/help.h @@ -0,0 +1,6 @@ +#define SWITCHES_COLUMN_WIDTH 24 + +void new_version_alert(void); +void version(void); +void help_long(void); +void usage(const char *me); diff --git a/http.c b/http.c new file mode 100644 index 0000000..73d1db7 --- /dev/null +++ b/http.c @@ -0,0 +1,120 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#include +#include +#include +#include +#ifndef NO_SSL +#include +#include "mssl.h" +#endif + +#include "gen.h" +#include "http.h" +#include "io.h" +#include "utils.h" + +int get_HTTP_headers(int socket_h, SSL *ssl_h, char **headers, int *overflow, double timeout) +{ + char *term = NULL; + int len_in=0, len=4096; + char *buffer = (char *)malloc(len + 1); + int rc = RC_OK; + + *headers = NULL; + + memset(buffer, 0x00, len); + + for(;;) + { + int rrc = -1; + int now_n = len - len_in; + +#ifndef NO_SSL + if (ssl_h) + rrc = SSL_read(ssl_h, &buffer[len_in], now_n); + else +#endif + rrc = read_to(socket_h, &buffer[len_in], now_n, timeout); + + if (rrc == 0 || rrc == RC_SHORTREAD) /* socket closed before request was read? */ + { + rc = RC_SHORTREAD; + break; + } + else if (rrc < 0) + { + free(buffer); + return rrc; + } + + len_in += rrc; + + assert(len_in >= 0); + assert(len_in <= len); + + buffer[len_in] = 0x00; + + if (strstr(buffer, "\r\n\r\n") != NULL) + break; + + if (len_in >= len) + { + len <<= 1; + buffer = (char *)realloc(buffer, len + 1); + } + } + + *headers = buffer; + + term = strstr(buffer, "\r\n\r\n"); + if (term) + *overflow = len_in - (term - buffer + 4); + else + *overflow = 0; + + return rc; +} + +int dumb_get_HTTP_headers(int socket_h, char **headers, double timeout) +{ + int len_in=0, len=4096; + char *buffer = (char *)malloc(len); + int rc = RC_OK; + + *headers = NULL; + + for(;;) + { + int rrc = read_to(socket_h, &buffer[len_in], 1, timeout); + if (rrc == 0 || rrc == RC_SHORTREAD) /* socket closed before request was read? */ + { + rc = RC_SHORTREAD; + break; + } + else if (rrc == RC_TIMEOUT) /* timeout */ + { + free(buffer); + return RC_TIMEOUT; + } + + len_in += rrc; + + buffer[len_in] = 0x00; + if (memcmp(&buffer[len_in - 4], "\r\n\r\n", 4) == 0) + break; + if (memcmp(&buffer[len_in - 2], "\n\n", 2) == 0) /* broken proxies */ + break; + + if (len_in == (len - 1)) + { + len <<= 1; + buffer = (char *)realloc(buffer, len); + } + } + + *headers = buffer; + + return rc; +} diff --git a/http.h b/http.h new file mode 100644 index 0000000..aba4cac --- /dev/null +++ b/http.h @@ -0,0 +1,5 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +int get_HTTP_headers(int socket_h, SSL *ssl_h, char **headers, int *overflow, double timeout); +int dumb_get_HTTP_headers(int socket_h, char **headers, double timeout); diff --git a/httping-nl.1 b/httping-nl.1 new file mode 100644 index 0000000..b532e7f --- /dev/null +++ b/httping-nl.1 @@ -0,0 +1,240 @@ +.\" Copyright Folkert van Heusden, 2003-2013 +.\" +.\" This file may be copied under the conditions described +.\" in the GNU GENERAL PUBLIC LICENSE, version 2 +.\" that can be found on the website of the free software +.\" foundation. +.\" +.TH HTTPING 1 2013-03 "httping" +.SH NAME +httping - meten van de latency en doorvoorsnelheid van een http server +.SH SAMENVATTING +.BI "httping [" opties "] +.sp +een aantal opties: +.BI "[\-g URL] [\-h systeem naam] [\-p port nummer] [\-x proxy systeem naam:port] [\-c aantal] [\-i interval] [\-t duur limiet] [\-s] [\-K] [\-Y] +.SH BESCHRIJVING +The program +Het programma +.B httping +meet de latency van een http server. Sinds versie 1.0.6 kan ook de dooorvoorsnelheid gemeten worden. +.PP +.SH OPTIES +.TP +.B "\-5" +De geselecteerde server is een SOCKS5 server. +.TP +.B "\-6" +Schakel IPv6 mode in. Standaard instelling is IPv4. +.TP +.B "\-a" +Hoorbare ping +.TP +.B "\-b" +Gebruik deze optie samen met \-G. HTTPing zal dan de doorvoorsnelheid (in kB/s) tonen. +.TP +.B "\-B" +Gebruik deze optie samen met \-G. HTTPing zal dan de doorvoorsnelheid (in kB/s) tonen. HTTPing zal echter vragen aan de webserver of deze de data comprimeerd. +.TP +.B "\-c aantal" +Hoevaak te pingen. +.TP +.B "\-D" +Teken geen grafieken (in ncurses (\-K) mode). +.TP +.B "\-e str" +Als de status-code anders is dan die ingesteld is met \-o, dan zal 'str' getoond worden. +.TP +.B "\-E" +Haal de proxy instellingen uit omgevings variabelen. +.TP +.B "\-F" +Probeer verbindingen met "TFO (TCP Fast open)" op te zetten. Dit werkt alleen met Linux kernel 3.7 of recenter. +.TP +.B "\-f" +Ping zo snel als mogelijk achter elkaar. +.TP +.B "\-G" +Doe een GET-verzoek in plaats van een HEAD-verzoek. Daarmee wordt ook de pagina-inhoud opgehaald. +.TP +.B "\-g URL" +Kies welke URL gepinged moet worden. Bijvoorbeeld: http://www.microsoft.com/ +.TP +.B "\-h systeem naam" +In plaats van een URL kan men ook alleen een systeem naam opgeven. +.TP +.B "\-I str" +Welke "UserAgent" te sturen naar de webserver. +.TP +.B "\-i interval" +Hoeveel seconden (of delen daarvan) te wachten tussen het verzenden van iedere ping. +.TP +.B "\-K" +Gebruik de ncurses gebruikers interface. +.TP +.B "\-L x" +In combinatie met -G zet dit een limiet op hoeveel data er binnengehaald wordt. +.TP +.B "\-l" +Maak een SSL verbinding. Dit vereist een https-URL. +.TP +.B "\-m" +Geef output die makkelijker met een script te bewerken is. +.TP +.B "\-N x" +Nagios-mode 1: geef 1 terug als de gemiddel reaktie snelheid >= "warn" en 2 als die snelheid >= "crit", anders geef 0 terug. Nagios mode 2: geef 0 terug als alles goed ging, anders 'x'. +.TP +.B "\-n warn,crit" +Schakelt HTTPing naar Nagios-plugin mode 1. Geef afsluit code 1 als de gemiddelde response tijd groter is dan 'warn', geef 2 terug als die tijd groter is dan 'crit'. Anders 0. +.TP +.B "\-o x,x,..." +Selecteer de HTTP status-codes die als 'ok' beschouwd worden. +.TP +.B "\-p portnumber" +Gebruik dit on combinatie met h. Het zet het port-nummer om te pingen. +.TP +.B "\-q" +Geef geen uitvoer, alleen een teruggave code. +.TP +.B "\-R str" +Welke "Referer" te zenden naar de webserver. +.TP +.B "\-r" +Eenmalig systeemnaam vertalen (dit is zinvol bij het pingen van een roterende DNS, bovendien haalt het de eerste vertaling uit de ping lus zodat de 1e ping niet een bovengemiddelde tijd duurt). +.TP +.B "\-S" +Toon alle individuele componenten (verbinden, zenden, etc) van de gemeten tijden. +.TP +.B "\-s" +Toon de HTTP statuscodes. +.TP +.B "\-T x" +Lees de toegangscode voor website authenticatie uit bestand 'x'. +.TP +.B "\-t timeout" +Hoelang te wachten op een reactie van de webserver. +.TP +.B "\-U" +Gebruik authenticatie bij toegang tot de website. Combineer dit met \-P of \-T. +.TP +.B "\-v" +Toon meer details. +.TP +.B "\-W" +Stop HTTPing niet als het opzoeken van de server naam niet lukt. +.TP +.B "\-X" +Gebruik deze in combinatie met \-G. Toon hoeveel data er ontvangen werd. +.TP +.B "\-x proxyhost[:port] +Gebruik een proxy server om een verbinding op te zetten. +.TP +.B "\-Y" +Gebruik kleuren. +.TP +.B "\-z" +Toon de vingerafdruk van de X.509 certificaten bij het opzetten van een SSL verbinding. +.TP +.B "\-\-abbreviate" +Kort waardes af wanneer ze groter zijn dan 1000/1000000/etc. +.TP +.B "\-\-adaptive-interval" or "\-\-ai" +Zorg ervoor dat pings steeds met dezelfde interval uitgevoerd worden, relatief tot het start tijdstip. deze instelling wordt automatisch aangezet in "ncurses"\-mode. +.TP +.B "\-\-aggregates x[,y[,z[,etc.]]]" +Toon cumulatief de waardes van x[/y[/etc]] seconden. +.TP +.B "\-\-divert\-connect x" +Gebruik niet de systeemnaam uit de URL om naar te verbinden. Verbind naar 'x'. +.TP +.B "\-\-draw-phase" +Toon fase diagram. +.TP +.B "\-\-graph\-limit x" +Bij het bepalen van de grafiek-bandbreedte: negeer waardes hoger dan x. +.TP +.B "\-\-header x" +Voeg een extra verzoek-regel toe. +.TP +.B "\-\-keep\-cookies" +Als de HTTP server cookies geeft, zend die dan mee terug bij volgende pings. +.TP +.B "\-\-max\-mtu x" +Welke MTU te gebruiken. Kan niet groter zijn dan de MTU van de netwerk adapter. +.TP +.B "\-\-no\-host\-header" +Voeg niet een "Host:"\-regel toe aan het verzoek. +.TP +.B "\-\-no\-tcp\-nodelay" +Zet het Naggle-algorithme niet uit. +.TP +.B "\-\-priority x" +Geef packets een bepaalde prioriteit. +.TP +.B "\-\-tos x" +Configureer de TOS. +.TP +.B "\-\-proxy\-user x" +Gebruikersnaam voor proxy authenticatie. +.TP +.B "\-\-proxy\-password x" +Toegangscode voor proxy authenticatie. +.TP +.B "\-\-proxy\-password-file x" +Lees toegangscode voor proxy authenticatie uit bestand 'x'. +.TP +.B "\-\-recv-buffer x" +Configureer de grootte van de ontvangst buffer. +.TP +.B "\-\-slow\-log x" +Als een ping-tijd meer dan x is, toon het resultaat dan in het middelste venster. +.TP +.B "\-\-threshold\-red x" +Toon metingen in rood als de gemeten waarde meer dan x is. +.TP +.B "\-\-threshold\-yellow x" +Toon metingen in geel als de gemeten waarde meer dan x is. +.TP +.B "\-\-threshold\-show x" +Toon metingen alleen als ze hoger dan x zijn. +.TP +.B "\-\-timestamp" or "\-\-ts" +Voeg een een tijdstempel toe aan de uitvoer. Gebruik -v om ook een datum te zien en \-vv om ook microseconden te zien. +.TP +.B "\-\-tx-buffer x" +Configureer de grootte van de zend-buffer. +.TP +.B "\-V" +Toon versie informatie. + +.SH UITVOER +In de -S mode zal iets als "tijd=0.08+24.09+23.17+15.64+0.02=62.98 ms" getoond worden. De eerste waarde is hoe lang het duurde om de systeem naam te vertalen, de 2e waarde hoe lang het duurde om te verbinden, de 3e waarde geeft aan hoe lang het duurde om het verzoek te verzenden en de 4e waarde is hoelang het duurde voordat de http-server een antwoord formuleerde en terugzond. de 5e waarde geeft aan hoelang het duurde om de socket te sluiten. + +.SH GRAFIEK +De grafiek in ncurses mode gebruikt een aantal kleuren. Groen: de waarde is minder dan de ondergrens. Rood: de waarde is hoger dan de bovengrens. Blauw: de waarde is gelimiteerd door '--graph-limit'. Cyaan: er is geen meetwaarde. + +.SH TOETSEN +Druk + om het programma af te breken. Er zal dan een samenvatting getoond worden. +In ncurses mode: + ververst het scherm, H stopt (en hervat) de grafieken en ook q zal het programma stoppen. + +.SH VOORBEELDEN +.TP +.B "httping \-g http://localhost/" +Ping de HTTP server op URL http://localhost/ +.TP +.B "httping \-h localhost \-p 1000" +Ping de HTTP server op systeem 'localhost' en port nummer 1000. +.TP + +.SH BUGS +Geen. + +.SH "ZIE OOK" +.BR http://www.vanheusden.com/httping/ + +.SH NOTITIES +Deze man-page beschrijft +.B httping +versie 2.3: andere versies kunnen iets of wat verschillen. +Stuur a.u.b. correcties, toevingen en foutraporten naar folkert@vanheusden.com +Wanneer u een donatie wilt doen, dan kunt u Bitcoins sturen naar: 1N5Sn4jny4xVwTwSYLnf7WnFQEGoVRmTQF diff --git a/httping.1 b/httping.1 new file mode 100644 index 0000000..496743e --- /dev/null +++ b/httping.1 @@ -0,0 +1,245 @@ +.\" Copyright Folkert van Heusden, 2003-2013 +.\" +.\" This file may be copied under the conditions described +.\" in the GNU GENERAL PUBLIC LICENSE, version 2 +.\" that can be found on the website of the free software +.\" foundation. +.\" +.TH HTTPING 1 2013-07 "httping" +.SH NAME +httping - measure the latency and throughput of a webserver +.SH SYNOPSIS +.BI "httping [" options "] +.sp +options: +.BI "[\-g url] [\-h hostname] [\-p portnumber] [\-x proxyhost:port] [\-c count] [\-i interval] [\-t timeout] [\-s] [\-G] [\-b] [\-L xferlimit] [\-X] [\-l] [\-z] [\-f] [\-m] [\-o rc,...] [\-e string]" +.BI "[\-I useragent string] [\-R referer string] [\-r] [\-n warn,crit] [\-N mode] [\-q] [\-V]" +.SH DESCRIPTION +The program +.B httping +lets you measure the latency of a webserver. Since version 1.0.6 also the throughput can be measured. +.PP +.SH OPTIONS +.TP +.B "\-5" +The proxy server selected is a SOCKS5 server. +.TP +.B "\-6" +Enable IPv6 mode. Default is IPv4. +.TP +.B "\-a" +Audible ping +.TP +.B "\-b" +Use this switch together with '\-G'. When this option is used, the transferspeed (in KB/s) is shown. +.TP +.B "\-B" +Use this switch together with '\-G'. Ask the HTTP server to compress the returned data: this will reduce the influence of the bandwidth of your connection while increasing the influence of the processorpower of the HTTP server. +.TP +.B "\-c count" +How many probes to send before exiting. +.TP +.B "\-D" +Do not draw graphs in ncurses mode (\-K). +.TP +.B "\-e str" +When the status-code differs from the ones selected with '\-o', the given string is displayed. +.TP +.B "\-E" +Retrieve proxy settings from environment variables ('http_proxy' and 'https_proxy'). +.TP +.B "\-F" +Attempt TCP Fast Open while trying to connect to a server (for Linux, version 3.7 onwards of the kernel) +.TP +.B "\-f" +Flood ping: do not sit idle between each ping but ping as fast as the computer and network allow you to. +.TP +.B "\-G" +Do a GET request instead of a HEAD request: this means that also the complete page/file must be transferred. Note that in this case you're no longer measuring the latency! +.TP +.B "\-g url" +This selects the url to probe. E.g.: http://localhost/ +.TP +.B "\-h hostname" +Instead of '\-g' one can also set a hostname to probe with -h: -h localhost +.TP +.B "\-I str" +UserAgent-string to send to the webserver (instead of 'HTTPing '). +.TP +.B "\-i interval" +How many seconds to sleep between every probe sent. +.TP +.B "\-K" +Enable ncurses user interface. +.TP +.B "\-L x" +Use this switch together with '\-G'. Limit the amount of data transferred to 'x'. Note that this only affects the content of the page/file and not the headerdata. +.TP +.B "\-l" +Connect using SSL: for this to work you need to give a 'https'-url or a 443 portnumber. +.TP +.B "\-m" +Show machine readable output (also check '\-o' and '\-e'). +.TP +.B "\-N x" +Switches HTTPing to Nagios-plugin mode 2: return 0 when everything is fine, 'x' when anything fails. E.g.: 1 => Nagios warning state, 2 => Nagios critical state. +.TP +.B "\-n warn,crit" +Switches HTTPing to Nagios-plugin mode 1: return exitcode '1' when the average response time is bigger then 'warn', return exitcode '2' when the the average response time is bigger then 'crit'. In all other cases return exitcode '0'. +.TP +.B "\-o x,x,..." +This selects the HTTP status-codes which are regarded as an OK-state (only with '\-m'). +.TP +.B "\-p portnumber" +-p can be used together with \-h. \-p selects the portnumber to probe. +.TP +.B "\-q" +Be quiet, only return an exit-code. +.TP +.B "\-R str" +Referer-string to send to the webserver. +.TP +.B "\-r" +Only resolve the hostname once: this takes the resolving out of the loop so that the latency of the DNS is not measured. Also useful when you want to measure only 1 webserver while the DNS returns a different ip-address for each resolve ('roundrobin'). +.TP +.B "\-S" +Split measured latency in time to connect and time to exchange a request with the HTTP server. +.TP +.B "\-s" +When a successfull transaction was done, show the HTTP statuscode (200, 404, etc.). +.TP +.B "\-T x" +Read the password for website authentication from file 'x' (instead of entering it on the command line). +.TP +.B "\-t timeout" +How long to wait for answer from the other side. +.TP +.B "\-U" +Enable authentication against website. Set username with \-U, set password with \-P (or \-T to read the password from a file). +.TP +.B "\-v" +Increase verbosity mode. To show standard deviation and dates in output. +.TP +.B "\-W" +Do not abort program if resolving fails. +.TP +.B "\-X" +Use this switch together with '-G'. For each "ping" show the amount of data transferred (excluding the headers). +.TP +.B "\-x proxyhost[:port] +Probe using a proxyserver. Note that you're also measuring the latency of the proxyserver! +.TP +.B "\-Y" +Enable colors +.TP +.B "\-z" +When connecting using SSL, display the fingerprint of the X509 certificate(s) of the peer. +.TP +.B "\-\-abbreviate" +Abbreviate values bigger than thousand, million, billion, etc. +.TP +.B "\-\-adaptive-interval" or "\-\-ai" +(Try to) ping on the same interval. E.g. if interval is set to 1.0 seconds and ping a ping t[n] occurs at 500s with duration 250ms, then the next ping (t[n+1]) will happen at 501 seconds and not at 501.25 seconds. Of course when the ping duration is > bigger than the interval, a ping will be "skipped" (not literally: the sequence number will continue) and t[n+1] will then be e.g. 502s instead of the expected 501s. This is useful for example in the ncurses output mode where an fft is calculated over the ping times. +.TP +.B "\-\-aggregates x[,y[,z[,etc.]]]" +Show aggregates every x[/y[/z[/etc]]] seconds. +.TP +.B "\-\-divert\-connect x" +Ignore the hostname in the URL and connect to 'x' instead. The given URL will be requested at 'x'. +.TP +.B "\-\-draw-phase" +Not only draw the magnitude of the fourier transform, draw the phase as well. +.TP +.B "\-\-graph\-limit x" +If values measured are bigger than x, then they're limitted to x. +.TP +.B "\-\-header x" +Add an additional request-header 'x'. +.TP +.B "\-\-keep\-cookies" +When the server sends a cookie, it will be returned in the next request. +.TP +.B "\-\-max\-mtu x" +Maximum MTU to use. Cannot be larger than network interface MTU. +.TP +.B "\-\-no\-host\-header" +Do not put a "Host:"-header in the request header. +.TP +.B "\-\-no\-tcp\-nodelay" +Do not disable "tcp delay" (Naggle). +.TP +.B "\-\-priority x" +Set priority of packets. +.TP +.B "\-\-tos x" +Set type of service. +.TP +.B "\-\-proxy\-user x" +Use username 'x' to authenticate against proxy (http/socks5) server (optional). +.TP +.B "\-\-proxy\-password x" +Use password 'x' to authenticate against proxy (http/socks5) server (optional). +.TP +.B "\-\-proxy\-password-file x" +Read password from file 'x' to authenticate against proxy (http/socks5) server (optional). +.TP +.B "\-\-recv-buffer x" +Set the size of the receive buffer (in bytes). +.TP +.B "\-\-slow\-log x" +When the duration is x or more, show ping line in the slow log window (the middle window). +.TP +.B "\-\-threshold\-red x" +If the measured threshold is higher than x (and -Y is given), then the shown value is colored red. If you also use --threshold-yellow, then this value must be bigger. +.TP +.B "\-\-threshold\-yellow x" +If the measured threshold is higher than x (and -Y is given), then the shown value is colored yellow. +.TP +.B "\-\-threshold\-show x" +If the measured threshold is higher than x, then the result is shown (default is show always). The value x is in ms. +.TP +.B "\-\-timestamp" or "\-\-ts" +Put a timestamp before the result-lines. Use -v to also show a date. +.TP +.B "\-\-tx-buffer x" +Set the size of the transmit buffer (in bytes). +.TP +.B "\-V" +Show the version and exit. + +.SH OUTPUT +In split mode (-S) something like "time=0.08+24.09+23.17+15.64+0.02=62.98 ms" is shown. The first value is the time it took to resolve the hostname (or 'n/a' if it did not resolve in this iteration, e.g. in "resolve once" (-r) mode), then the time it took to connect (or -1 for example in persistent connection (-Q, HTTP v1.1), after that the time it took to put the request on the wire, then the time it took for the HTTP server to process the request and send it back and lastly the time it took to close the connection. + +.SH GRAPH +The graph in the ncurses uses colors to encode a meaning. Green: value is less than 1 block. Red: the value did not fit in the graph. Blue: the value was limitted by --graph-limit. Cyan: no measurement for that point in time. + +.SH KEYS +Press + to exit the program. It will display a summary of what was measured. +In the ncurses gui, press + to forcibly redraw the screen. Press 'H' to halt the graphs (and again to continue). Press 'q' to stop the program ( + will work too). + +.SH EXAMPLES +.TP +.B "httping \-g http://localhost/" +Ping the webserver on host 'localhost'. +.TP +.B "httping \-h localhost \-p 1000" +Ping the webserver on host 'localhost' and portnumber 1000. +.TP +.B "httping \-l \-g https://localhost/" +Ping the webserver on host 'localhost' using an SSL connection. +.TP +.B "httping \-g http://localhost/ -U username -P password" +Ping the webserver on host 'localhost' using the Basic HTTP Authentication. +.SH BUGS +None. This program is totally bug-free. + +.SH "SEE ALSO" +.BR http://www.vanheusden.com/httping/ + +.SH NOTES +This page describes +.B httping +as found in the httping-2.3 package; other versions may differ slightly. +Please mail corrections and additions to folkert@vanheusden.com. +Report bugs in the program to folkert@vanheusden.com. +Please consider sending bitcoins to 1N5Sn4jny4xVwTwSYLnf7WnFQEGoVRmTQF diff --git a/io.c b/io.c new file mode 100644 index 0000000..48ba9b0 --- /dev/null +++ b/io.c @@ -0,0 +1,193 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gen.h" +#include "error.h" + +ssize_t read_to(int fd, char *whereto, size_t len, double timeout) +{ + for(;;) + { + ssize_t rc; + struct timeval to; + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + to.tv_sec = (long)(timeout / 1000.0); + to.tv_usec = (long)(timeout * 1000.0) % 1000000; + + rc = select(fd + 1, &rfds, NULL, NULL, &to); + if (rc == 0) + return RC_TIMEOUT; + else if (rc == -1) + { + if (errno == EAGAIN) + continue; + if (errno == EINTR) + return RC_CTRLC; + + set_error(gettext("myread::select failed: %s"), strerror(errno)); + + return RC_SHORTREAD; + } + + return read(fd, whereto, len); + } +} + +ssize_t myread(int fd, char *whereto, size_t len, double timeout) +{ + ssize_t cnt=0; + + while(len>0) + { + ssize_t rc; + struct timeval to; + fd_set rfds; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + to.tv_sec = (long)(timeout / 1000.0); + to.tv_usec = (long)(timeout * 1000.0) % 1000000; + + rc = select(fd + 1, &rfds, NULL, NULL, &to); + if (rc == 0) + return RC_TIMEOUT; + else if (rc == -1) + { + if (errno == EAGAIN) + continue; + if (errno == EINTR) + return RC_CTRLC; + + set_error(gettext("myread::select failed: %s"), strerror(errno)); + + return RC_SHORTREAD; + } + + if (FD_ISSET(fd, &rfds)) + { + rc = read(fd, whereto, len); + + if (rc == -1) + { + if (errno == EAGAIN) + continue; + if (errno == EINTR) + return RC_CTRLC; + + set_error(gettext("myread::read failed: %s"), strerror(errno)); + + return RC_SHORTREAD; + } + else if (rc == 0) + break; + else + { + whereto += rc; + len -= rc; + cnt += rc; + } + } + } + + return cnt; +} + +ssize_t mywrite(int fd, char *wherefrom, size_t len, double timeout) +{ + ssize_t cnt=0; + + while(len>0) + { + ssize_t rc; + struct timeval to; + fd_set wfds; + + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + to.tv_sec = (long)(timeout / 1000.0); + to.tv_usec = (long)(timeout * 1000.0) % 1000000; + + rc = select(fd + 1, NULL, &wfds, NULL, &to); + if (rc == 0) + return RC_TIMEOUT; + else if (rc == -1) + { + if (errno == EAGAIN) + continue; + if (errno == EINTR) + return RC_CTRLC; + + set_error(gettext("mywrite::select failed: %s"), strerror(errno)); + + return RC_SHORTWRITE; + } + + rc = write(fd, wherefrom, len); + + if (rc == -1) + { + if (errno == EAGAIN) + continue; + if (errno == EINTR) + return RC_CTRLC; + + set_error(gettext("mywrite::write failed: %s"), strerror(errno)); + + return RC_SHORTWRITE; + } + else if (rc == 0) + break; + else + { + wherefrom += rc; + len -= rc; + cnt += rc; + } + } + + return cnt; +} + +int set_fd_nonblocking(int fd) +{ + /* set fd to non-blocking */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) + { + fprintf(stderr, gettext("set_fd_nonblocking failed! (%s)\n"), strerror(errno)); + + return -1; + } + + return 0; +} + +int set_fd_blocking(int fd) +{ + /* set fd to blocking */ + if (fcntl(fd, F_SETFL, 0) == -1) + { + fprintf(stderr, gettext("set_fd_blocking failed! (%s)\n"), strerror(errno)); + + return -1; + } + + return 0; +} diff --git a/io.h b/io.h new file mode 100644 index 0000000..3fffa82 --- /dev/null +++ b/io.h @@ -0,0 +1,8 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +ssize_t read_to(int fd, char *whereto, size_t len, double timeout); +ssize_t myread(int fd, char *whereto, size_t len, double timeout); +ssize_t mywrite(int fd, const char *wherefrom, size_t len, double timeout); +int set_fd_nonblocking(int fd); +int set_fd_blocking(int fd); diff --git a/kalman.c b/kalman.c new file mode 100644 index 0000000..766f1a7 --- /dev/null +++ b/kalman.c @@ -0,0 +1,81 @@ +/* #define _TEST */ + +#include +#ifdef _TEST +#include +#include +#endif + +double x_est_last = 0.0, P_last = 0.0, Q = 0.0, R = 0.0, K = 0.0, P = 0.0, P_temp = 0.0, x_temp_est = 0.0, x_est = 0.0, z_measured = 0.0, z_real = 0.0, sum_error_kalman = 0.0, sum_error_measure = 0.0; +char first = 1; + +void kalman_init(double ideal_value) +{ + /* initial values for the kalman filter */ + x_est_last = 0; + P_last = 0; + /* the noise in the system (FIXME?) */ + Q = 0.022; + R = 0.617; + z_real = ideal_value; /* 0.5; the ideal value we wish to measure */ + + first = 1; +} + +double kalman_do(double z_measured) +{ + /* initialize with a measurement */ + if (first) + { + first = 0; + x_est_last = z_measured; + } + + /* do a prediction */ + x_temp_est = x_est_last; + P_temp = P_last + Q; + /* calculate the Kalman gain */ + K = P_temp * (1.0/(P_temp + R)); + /* measure */ + /*z_measured = z_real + frand()*0.09; the real measurement plus noise*/ + /* correct */ + x_est = x_temp_est + K * (z_measured - x_temp_est); + P = (1- K) * P_temp; + /* we have our new system */ + +#ifdef _TEST + printf("Ideal position: %6.3f \n",z_real); + printf("Mesaured position: %6.3f [diff:%.3f]\n",z_measured,fabs(z_real-z_measured)); + printf("Kalman position: %6.3f [diff:%.3f]\n",x_est,fabs(z_real - x_est)); +#endif + + sum_error_kalman += fabs(z_real - x_est); + sum_error_measure += fabs(z_real-z_measured); + + /* update our last's */ + P_last = P; + x_est_last = x_est; + +#ifdef _TEST + printf("Total error if using raw measured: %f\n",sum_error_measure); + printf("Total error if using kalman filter: %f\n",sum_error_kalman); + printf("Reduction in error: %d%% \n",100-(int)((sum_error_kalman/sum_error_measure)*100)); +#endif + + return x_est; +} + +#ifdef _TEST +int main(int argc, char *argv[]) +{ + kalman_init(0.0); + + for(int loop=0; loop<25; loop++) + { + double v = drand48(); + printf("%d] %f %f\n", loop + 1, v, kalman_do(v)); + } + + return 0; +} +#endif diff --git a/kalman.h b/kalman.h new file mode 100644 index 0000000..a145567 --- /dev/null +++ b/kalman.h @@ -0,0 +1,4 @@ +/* taken (and adapted) from http://www.dzone.com/snippets/simple-kalman-filter-c */ + +void kalman_init(double ideal_value); +double kalman_do(double value); diff --git a/license.OpenSSL b/license.OpenSSL new file mode 100644 index 0000000..29c7a2b --- /dev/null +++ b/license.OpenSSL @@ -0,0 +1 @@ +Please see license.txt. diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..510c1a5 --- /dev/null +++ b/license.txt @@ -0,0 +1,17 @@ +HTTPing is (C) 2003-2013 by folkert@vanheusden.com + +The GPL version 2 applies to this program. That document can be found +on the website of the free software foundation. + + In addition, as a special exception, the copyright holder gives + permission to link the code of portions of this program with the + OpenSSL library under certain conditions as described in each + individual source file, and distribute linked combinations + including the two. + You must obey the GNU General Public License in all respects + for all of the code used other than OpenSSL. If you modify + file(s) with this exception, you may extend this exception to your + version of the file(s), but you are not obligated to do so. If you + do not wish to do so, delete this exception statement from your + version. If you delete this exception statement from all source + files in the program, then also delete it here. diff --git a/main.c b/main.c new file mode 100644 index 0000000..a32103d --- /dev/null +++ b/main.c @@ -0,0 +1,2442 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision: 278 $ */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef NO_SSL +#include +#include "mssl.h" +#endif +#include +#include +#include +#if defined(sun) || defined(__sun) +#include +#endif +#ifdef NC +#include +#endif + +#include "gen.h" +#include "help.h" +#include "colors.h" +#include "http.h" +#include "io.h" +#include "tcp.h" +#include "res.h" +#include "utils.h" +#include "error.h" +#include "socks5.h" +#ifdef NC +#include "nc.h" +#endif +#include "cookies.h" + +volatile int stop = 0; + +int quiet = 0; +char machine_readable = 0; +char json_output = 0; +char show_ts = 0; + +int max_x = 80, max_y = 24; + +char nagios_mode = 0; +char ncurses_mode = 0; + +int fd = -1; + +volatile char got_sigquit = 0; + +void handler_quit(int s) +{ + signal(SIGQUIT, handler_quit); + + got_sigquit = 1; +} + +void determine_terminal_size(int *max_y, int *max_x) +{ + struct winsize size; + + *max_x = *max_y = 0; + + if (!isatty(1)) + { + *max_y = 24; + *max_x = 80; + } +#ifdef TIOCGWINSZ + else if (ioctl(1, TIOCGWINSZ, &size) == 0) + { + *max_y = size.ws_row; + *max_x = size.ws_col; + } +#endif + + if (!*max_x || !*max_y) + { + char *dummy = getenv("COLUMNS"); + if (dummy) + *max_x = atoi(dummy); + else + *max_x = 80; + + dummy = getenv("LINES"); + if (dummy) + *max_y = atoi(dummy); + else + *max_y = 24; + } +} + +void emit_statuslines(double run_time) +{ +#ifdef NC + if (ncurses_mode) + { + time_t t = time(NULL); + char *t_str = ctime(&t); + char *dummy = strchr(t_str, '\n'); + + if (dummy) + *dummy = 0x00; + + status_line(gettext("%s, run time: %.3fs, press ctrl + c to stop"), t_str, run_time); + } +#else + (void)run_time; +#endif +} + +void emit_headers(char *in) +{ +#ifdef NC + static char shown = 0; + int len_in = -1; + + if (!shown && ncurses_mode && in != NULL && (len_in = strlen(in) - 4) > 0) + { + int pos = 0, pos_out = 0; + char *copy = (char *)malloc(len_in + 1), *dummy = NULL; + + for(pos=0; pos 1) + printf(", \n"); + printf("{ "); + printf("\"status\" : \"%d\", ", ok); + printf("\"seq\" : \"%d\", ", seq); + printf("\"start_ts\" : \"%f\", ", start_ts); + if (t_resolve!=NULL && t_resolve -> cur_valid) + printf("\"resolve_ms\" : \"%e\", ", t_resolve -> cur); + else + printf("\"resolve_ms\" : \"%e\", ",-1.0); + if (t_connect!=NULL && t_connect -> cur_valid) + printf("\"connect_ms\" : \"%e\", ", t_connect -> cur); + else + printf("\"connect_ms\" : \"%e\", ",-1.0); + printf("\"request_ms\" : \"%e\", ", t_request -> cur); + printf("\"total_ms\" : \"%e\", ", t_total -> cur); + printf("\"http_code\" : \"%d\", ", http_code); + printf("\"msg\" : \"%s\", ", msg); + printf("\"header_size\" : \"%d\", ", header_size); + printf("\"data_size\" : \"%d\", ", data_size); + printf("\"bps\" : \"%f\", ", Bps); + printf("\"host\" : \"%s\", ", host); + printf("\"ssl_fingerprint\" : \"%s\", ", ssl_fp ? ssl_fp : ""); + printf("\"time_offset\" : \"%f\", ", toff_diff_ts); + printf("\"tfo_success\" : \"%s\", ", tfo_success ? "true" : "false"); + if (t_ssl -> cur_valid) + printf("\"ssl_ms\" : \"%e\", ", t_ssl -> cur); + printf("\"tfo_succes\" : \"%s\", ", tfo_success ? "true" : "false"); + if (t_ssl !=NULL && t_ssl -> cur_valid) + printf("\"ssl_ms\" : \"%e\", ", t_ssl -> cur); + printf("\"write\" : \"%e\", ", t_write -> cur); + printf("\"close\" : \"%e\", ", t_close -> cur); + printf("\"cookies\" : \"%d\", ", n_cookies); + if (stats_to != NULL && stats_to -> cur_valid) + printf("\"to\" : \"%e\", ", stats_to -> cur); + if (tcp_rtt_stats !=NULL && tcp_rtt_stats -> cur_valid) + printf("\"tcp_rtt_stats\" : \"%e\", ", tcp_rtt_stats -> cur); + printf("\"re_tx\" : \"%d\", ", re_tx); + printf("\"pmtu\" : \"%d\", ", pmtu); + printf("\"tos\" : \"%02x\" ", recv_tos); + printf("}"); +} + +char *get_ts_str(int verbose) +{ + char buffer[4096] = { 0 }; + struct tm *tvm = NULL; + struct timeval tv; + + (void)gettimeofday(&tv, NULL); + + tvm = localtime(&tv.tv_sec); + + if (verbose == 1) + sprintf(buffer, "%04d/%02d/%02d ", tvm -> tm_year + 1900, tvm -> tm_mon + 1, tvm -> tm_mday); + else if (verbose >= 2) + sprintf(buffer, "%.6f", get_ts()); + + if (verbose <= 1) + sprintf(&buffer[strlen(buffer)], "%02d:%02d:%02d.%03d", tvm -> tm_hour, tvm -> tm_min, tvm -> tm_sec, (int)(tv.tv_usec / 1000)); + + return strdup(buffer); +} + +void emit_error(int verbose, int seq, double start_ts) +{ + char *ts = show_ts ? get_ts_str(verbose) : NULL; + +#ifdef NC + if (ncurses_mode) + { + slow_log("\n%s%s", ts ? ts : "", get_error()); + update_terminal(); + } + else +#endif + if (!quiet && !machine_readable && !nagios_mode && !json_output) + printf("%s%s%s%s\n", ts ? ts : "", c_error, get_error(), c_normal); + + if (json_output) + emit_json(0, seq, start_ts, NULL, NULL, NULL, -1, get_error(), -1, -1, -1, "", "", -1, 0, NULL, NULL, NULL, 0, NULL, NULL, 0, 0, 0, NULL); + + clear_error(); + + free(ts); + + fflush(NULL); +} + +void handler(int sig) +{ +#ifdef NC + if (sig == SIGWINCH) + win_resize = 1; + else +#endif + { + if (!json_output) + fprintf(stderr, gettext("Got signal %d\n"), sig); + + stop = 1; + } +} + +char * read_file(const char *file) +{ + char buffer[4096] = { 0 }, *lf = NULL; + FILE *fh = fopen(file, "rb"); + if (!fh) + error_exit(gettext("Cannot open password-file %s"), file); + + if (!fgets(buffer, sizeof buffer, fh)) + error_exit(gettext("Problem reading password from file %s"), file); + + fclose(fh); + + lf = strchr(buffer, '\n'); + if (lf) + *lf = 0x00; + + return strdup(buffer); +} + +char * create_http_request_header(const char *get, char use_proxy_host, char get_instead_of_head, char persistent_connections, const char *hostname, const char *useragent, const char *referer, char ask_compression, char no_cache, const char *auth_usr, const char *auth_password, char **static_cookies, int n_static_cookies, char **dynamic_cookies, int n_dynamic_cookies, const char *proxy_buster, const char *proxy_user, const char *proxy_password, char **additional_headers, int n_additional_headers) +{ + int index; + char *request = NULL; + char pb[128] = { 0 }; + + if (proxy_buster) + { + if (strchr(get, '?')) + pb[0] = '&'; + else + pb[0] = '?'; + + snprintf(pb + 1, sizeof pb - 1, "%s=%ld", proxy_buster, lrand48()); + } + + if (use_proxy_host) + str_add(&request, "%s %s%s HTTP/1.%c\r\n", get_instead_of_head?"GET":"HEAD", get, pb, persistent_connections?'1':'0'); + else + { + const char *dummy = get, *slash = NULL; + if (strncasecmp(dummy, "http://", 7) == 0) + dummy += 7; + else if (strncasecmp(dummy, "https://", 7) == 0) + dummy += 8; + + slash = strchr(dummy, '/'); + if (slash) + str_add(&request, "%s %s HTTP/1.%c\r\n", get_instead_of_head?"GET":"HEAD", slash, persistent_connections?'1':'0'); + else + str_add(&request, "%s / HTTP/1.%c\r\n", get_instead_of_head?"GET":"HEAD", persistent_connections?'1':'0'); + } + + if (hostname) + str_add(&request, "Host: %s\r\n", hostname); + + if (useragent) + str_add(&request, "User-Agent: %s\r\n", useragent); + else + str_add(&request, "User-Agent: HTTPing v" VERSION "\r\n"); + + if (referer) + str_add(&request, "Referer: %s\r\n", referer); + + if (ask_compression) + str_add(&request, "Accept-Encoding: gzip,deflate\r\n"); + + if (no_cache) + { + str_add(&request, "Pragma: no-cache\r\n"); + str_add(&request, "Cache-Control: no-cache\r\n"); + } + + /* Basic Authentification */ + if (auth_usr) + { + char auth_string[256] = { 0 }; + char b64_auth_string[512] = { 0 }; + + sprintf(auth_string, "%s:%s", auth_usr, auth_password); + enc_b64(auth_string, strlen(auth_string), b64_auth_string); + + str_add(&request, "Authorization: Basic %s\r\n", b64_auth_string); + } + + /* proxy authentication */ + if (proxy_user) + { + char ppa_string[256] = { 0 }; + char b64_ppa_string[512] = { 0 }; + + sprintf(ppa_string, "%s:%s", proxy_user, proxy_password); + enc_b64(ppa_string, strlen(ppa_string), b64_ppa_string); + + str_add(&request, "Proxy-Authorization: Basic %s\r\n", b64_ppa_string); + } + + /* Cookie insertion */ + for(index=0; index= sizeof in_use) + error_exit(gettext("URL too big, HTTPing has a %d bytes limit"), sizeof in_use - 1); + + /* make url complete, if not already */ + if (strncasecmp(in, "http://", 7) == 0 || strncasecmp(in, "https://", 8) == 0) /* complete url? */ + { + snprintf(in_use, sizeof in_use - 1, "%s", in); + + if (strchr(&in[8], '/') == NULL) + in_use[strlen(in_use)] = '/'; + } + else if (strchr(in, '/')) /* hostname + location without 'http://'? */ + sprintf(in_use, "http://%s", in); + else if (use_ssl) + sprintf(in_use, "https://%s/", in); + else + sprintf(in_use, "http://%s/", in); + + /* sanity check */ + if (strncasecmp(in_use, "http://", 7) == 0 && use_ssl) + error_exit(gettext("using \"http://\" with SSL enabled (-l)")); + + *complete_url = strdup(in_use); + + /* fetch hostname */ + if (strncasecmp(in_use, "http://", 7) == 0) + *hostname = strdup(&in_use[7]); + else /* https */ + *hostname = strdup(&in_use[8]); + + dummy = strchr(*hostname, '/'); + if (dummy) + *dummy = 0x00; + + /* fetch port number */ + if (use_ssl || strncasecmp(in, "https://", 8) == 0) + *portnr = 443; + else + *portnr = 80; + + if (!use_ipv6) + { + char *at = strchr(*hostname, '@'); + char *colon = strchr(*hostname, ':'); + char *colon2 = colon ? strchr(colon + 1, ':') : NULL; + + if (colon2) + { + *colon2 = 0x00; + *portnr = atoi(colon2 + 1); + + if (at) + { + *colon = 0x00; + *at = 0x00; + + *auth_user = strdup(*hostname); + *auth_password = strdup(colon + 1); + } + } + else if (colon) + { + if (colon < at) + { + *colon = 0x00; + *at = 0x00; + + *auth_user = strdup(*hostname); + *auth_password = strdup(colon + 1); + } + else if (at) + { + *at = 0x00; + *auth_user = strdup(*hostname); + } + else + { + *colon = 0x00; + *portnr = atoi(colon + 1); + } + } + } + + /* fetch path */ + dummy = strchr(&in_use[8], '/'); + if (dummy) + *path = strdup(dummy); + else + *path = strdup("/"); +} + +typedef struct { + int interval, last_ts; + double value, sd, min, max; + int n_values; +} aggregate_t; + +void set_aggregate(char *in, int *n_aggregates, aggregate_t **aggregates) +{ + char *dummy = in; + + *n_aggregates = 0; + + for(;dummy;) + { + (*n_aggregates)++; + + *aggregates = (aggregate_t *)realloc(*aggregates, *n_aggregates * sizeof(aggregate_t)); + + memset(&(*aggregates)[*n_aggregates - 1], 0x00, sizeof(aggregate_t)); + + (*aggregates)[*n_aggregates - 1].interval = atoi(dummy); + (*aggregates)[*n_aggregates - 1].max = -MY_DOUBLE_INF; + (*aggregates)[*n_aggregates - 1].min = MY_DOUBLE_INF; + + dummy = strchr(dummy, ','); + if (dummy) + dummy++; + } +} + +void do_aggregates(double cur_ms, int cur_ts, int n_aggregates, aggregate_t *aggregates, int verbose, char show_ts) +{ + int index=0; + + /* update measurements */ + for(index=0; index aggregates[index].max) + aggregates[index].max = cur_ms; + + aggregates[index].sd += cur_ms * cur_ms; + + aggregates[index].n_values++; + } + + /* emit */ + for(index=0; index 0; index++) + { + aggregate_t *a = &aggregates[index]; + + if (cur_ts - a -> last_ts >= a -> interval) + { + char *line = NULL; + double avg = a -> n_values ? a -> value / (double)a -> n_values : -1.0; + char *ts = get_ts_str(verbose); + + str_add(&line, "%s", show_ts ? ts : ""); + free(ts); + + str_add(&line, gettext("AGG[%d]: %d values, min/avg/max%s = %.1f/%.1f/%.1f"), a -> interval, a -> n_values, verbose ? gettext("/sd") : "", a -> min, avg, a -> max); + + if (verbose) + { + double sd = -1.0; + + if (a -> n_values) + sd = sqrt((a -> sd / (double)a -> n_values) - pow(avg, 2.0)); + + str_add(&line, "/%.1f", sd); + } + + str_add(&line, " ms"); + +#ifdef NC + if (ncurses_mode) + slow_log("\n%s", line); + else +#endif + printf("%s\n", line); + + free(line); + + aggregates[index].value = + aggregates[index].sd = 0.0; + aggregates[index].min = MY_DOUBLE_INF; + aggregates[index].max = -MY_DOUBLE_INF; + aggregates[index].n_values = 0; + aggregates[index].last_ts = cur_ts; + } + } +} + +void fetch_proxy_settings(char **proxy_user, char **proxy_password, char **proxy_host, int *proxy_port, char use_ssl, char use_ipv6) +{ + char *str = getenv(use_ssl ? "https_proxy" : "http_proxy"); + + if (!str) + { + /* FIXME from wgetrc/curlrc? */ + } + + if (str) + { + char *path = NULL, *url = NULL; + + interpret_url(str, &path, proxy_host, proxy_port, use_ipv6, use_ssl, &url, proxy_user, proxy_password); + + free(url); + free(path); + } +} + +void parse_nagios_settings(const char *in, double *nagios_warn, double *nagios_crit) +{ + char *dummy = strchr(in, ','); + if (!dummy) + error_exit(gettext("-n: missing parameter\n")); + + *nagios_warn = atof(in); + + *nagios_crit = atof(dummy + 1); +} + +void parse_bind_to(const char *in, struct sockaddr_in *bind_to_4, struct sockaddr_in6 *bind_to_6, struct sockaddr_in **bind_to) +{ + char *dummy = strchr(in, ':'); + + if (dummy) + { + *bind_to = (struct sockaddr_in *)bind_to_6; + memset(bind_to_6, 0x00, sizeof *bind_to_6); + bind_to_6 -> sin6_family = AF_INET6; + + if (inet_pton(AF_INET6, in, &bind_to_6 -> sin6_addr) != 1) + error_exit(gettext("cannot convert ip address '%s' (for -y)\n"), in); + } + else + { + *bind_to = (struct sockaddr_in *)bind_to_4; + memset(bind_to_4, 0x00, sizeof *bind_to_4); + bind_to_4 -> sin_family = AF_INET; + + if (inet_pton(AF_INET, in, &bind_to_4 -> sin_addr) != 1) + error_exit(gettext("cannot convert ip address '%s' (for -y)\n"), in); + } +} + +time_t parse_date_from_response_headers(const char *in) +{ + char *date = NULL, *komma = NULL; + if (in == NULL) + return -1; + + date = strstr(in, "\nDate:"); + komma = date ? strchr(date, ',') : NULL; + if (date && komma) + { + struct tm tm; + memset(&tm, 0x00, sizeof tm); + + /* 22 Feb 2013 09:13:56 */ + if (strptime(komma + 1, "%d %b %Y %H:%M:%S %Z", &tm)) + return mktime(&tm); + } + + return -1; +} + +int calc_page_age(const char *in, const time_t their_ts) +{ + int age = -1; + + if (in != NULL && their_ts > 0) + { + char *date = strstr(in, "\nLast-Modified:"); + char *komma = date ? strchr(date, ',') : NULL; + if (date && komma) + { + struct tm tm; + memset(&tm, 0x00, sizeof tm); + + /* 22 Feb 2013 09:13:56 */ + if (strptime(komma + 1, "%d %b %Y %H:%M:%S %Z", &tm)) + age = their_ts - mktime(&tm); + } + } + + return age; +} + +const char *get_location(const char *host, int port, char use_ssl, char *reply) +{ + if (reply) + { + char *copy = strdup(reply); + char *head = strstr(copy, "\nLocation:"); + char *lf = head ? strchr(head + 1, '\n') : NULL; + + if (head) + { + char *buffer = NULL; + char *dest = head + 11; + + if (lf) + *lf = 0x00; + + if (memcmp(dest, "http", 4) == 0) + str_add(&buffer, "%s", dest); + else + str_add(&buffer, "http%s://%s:%d%s", use_ssl ? "s" : "", host, port, dest); + + free(copy); + + return buffer; + } + + free(copy); + } + + return NULL; +} + +char check_compressed(const char *reply) +{ + if (reply != NULL) + { + char *encoding = strstr(reply, "\nContent-Encoding:"); + + if (encoding) + { + char *dummy = strchr(encoding + 1, '\r'); + if (dummy) *dummy = 0x00; + + dummy = strchr(encoding + 1, '\n'); + if (dummy) *dummy = 0x00; + + if (strstr(encoding, "gzip") == 0 || strstr(encoding, "deflate") == 0) + return 1; + } + } + + return 0; +} + +int nagios_result(int ok, int nagios_mode, int nagios_exit_code, double avg_httping_time, double nagios_warn, double nagios_crit) +{ + if (nagios_mode == 1) + { + if (ok == 0) + { + printf(gettext("CRITICAL - connecting failed: %s"), get_error()); + return 2; + } + else if (avg_httping_time >= nagios_crit) + { + printf(gettext("CRITICAL - average httping-time is %.1f\n"), avg_httping_time); + return 2; + } + else if (avg_httping_time >= nagios_warn) + { + printf(gettext("WARNING - average httping-time is %.1f\n"), avg_httping_time); + return 1; + } + + printf(gettext("OK - average httping-time is %.1f (%s)|ping=%f\n"), avg_httping_time, get_error(), avg_httping_time); + + return 0; + } + else if (nagios_mode == 2) + { + const char *err = get_error(); + + if (ok && err[0] == 0x00) + { + printf(gettext("OK - all fine, avg httping time is %.1f|ping=%f\n"), avg_httping_time, avg_httping_time); + return 0; + } + + printf(gettext("%s: - failed: %s"), nagios_exit_code == 1?"WARNING":(nagios_exit_code == 2?"CRITICAL":"ERROR"), err); + return nagios_exit_code; + } + + return -1; +} + +void proxy_to_host_and_port(char *in, char **proxy_host, int *proxy_port) +{ + if (in[0] == '[') + { + char *dummy = NULL; + + *proxy_host = strdup(in + 1); + + dummy = strchr(*proxy_host, ']'); + if (dummy) + { + *dummy = 0x00; + + /* +2: ']:' */ + *proxy_port = atoi(dummy + 2); + } + } + else + { + char *dummy = strchr(in, ':'); + + *proxy_host = in; + + if (dummy) + { + *dummy=0x00; + *proxy_port = atoi(dummy + 1); + } + } +} + +void stats_close(int *fd, stats_t *t_close, char is_failure) +{ + double t_start = get_ts(), t_end = -1;; + + if (is_failure) + failure_close(*fd); + else + close(*fd); + + *fd = -1; + + t_end = get_ts(); + + update_statst(t_close, (t_end - t_start) * 1000.0); +} + +void add_header(char ***additional_headers, int *n_additional_headers, const char *in) +{ + *additional_headers = (char **)realloc(*additional_headers, (*n_additional_headers + 1) * sizeof(char **)); + (*additional_headers)[*n_additional_headers] = strdup(in); + + (*n_additional_headers)++; +} + +void free_headers(char **additional_headers, int n_additional_headers) +{ + int index = 0; + + for(index=0; index 0) + fprintf(stderr, gettext("internal error! (curncount)\n")); + + if (count == -1) + dummy = curncount; + + printf(gettext("%s%d%s connects, %s%d%s ok, %s%3.2f%%%s failed, time %s%s%.0fms%s\n"), c_yellow, curncount, c_normal, c_green, ok, c_normal, c_red, (((double)err) / ((double)dummy)) * 100.0, c_normal, c_blue, c_bright, total_took * 1000.0, c_normal); + + if (ok > 0) + { + printf(gettext("round-trip min/avg/max%s = %s%.1f%s/%s%.1f%s/%s%.1f%s"), verbose ? gettext("/sd") : "", c_bright, t_total -> min, c_normal, c_bright, avg_httping_time, c_normal, c_bright, t_total -> max, c_normal); + + if (verbose) + { + double sd_final = t_total -> n ? sqrt((t_total -> sd / (double)t_total -> n) - pow(avg_httping_time, 2.0)) : -1.0; + printf("/%.1f", sd_final); + } + + printf(" ms\n"); + + if (bps) + printf(gettext("Transfer speed: min/avg/max = %s%f%s/%s%f%s/%s%f%s KB\n"), c_bright, bps -> Bps_min / 1024, c_normal, c_bright, (bps -> Bps_avg / (double)ok) / 1024.0, c_normal, c_bright, bps -> Bps_max / 1024.0, c_normal); + } +} + +int main(int argc, char *argv[]) +{ + double started_at = -1; + char do_fetch_proxy_settings = 0; + char *hostname = NULL; + char *proxy_host = NULL, *proxy_user = NULL, *proxy_password = NULL; + int proxy_port = 8080; + int portnr = 80; + char *get = NULL, *request = NULL; + int req_len = 0; + int c = 0; + int count = -1, curncount = 0; + double wait = 1.0; + char wait_set = 0; + int audible = 0; + int ok = 0, err = 0; + double timeout = 30.0; + char show_statuscodes = 0; + char use_ssl = 0; + const char *ok_str = "200"; + const char *err_str = "-1"; + const char *useragent = NULL; + const char *referer = NULL; + const char *auth_password = NULL; + const char *auth_usr = NULL; + char **static_cookies = NULL, **dynamic_cookies = NULL; + int n_static_cookies = 0, n_dynamic_cookies = 0; + char resolve_once = 0; + char have_resolved = 0; + double nagios_warn=0.0, nagios_crit=0.0; + int nagios_exit_code = 2; + double avg_httping_time = -1.0; + int get_instead_of_head = 0; + char show_Bps = 0, ask_compression = 0; + bps_t bps; + int Bps_limit = -1; + char show_bytes_xfer = 0, show_fp = 0; + SSL *ssl_h = NULL; + BIO *s_bio = NULL; + struct sockaddr_in *bind_to = NULL; + struct sockaddr_in bind_to_4; + struct sockaddr_in6 bind_to_6; + char split = 0, use_ipv6 = 0; + char persistent_connections = 0, persistent_did_reconnect = 0; + char no_cache = 0; + char use_tfo = 0; + char abort_on_resolve_failure = 1; + double offset_yellow = -1, offset_red = -1; + char colors = 0; + int verbose = 0; + double offset_show = -1.0; + char add_host_header = 1; + char *proxy_buster = NULL; + char proxy_is_socks5 = 0; + char *url = NULL, *complete_url = NULL; + int n_aggregates = 0; + aggregate_t *aggregates = NULL; + char *au_dummy = NULL, *ap_dummy = NULL; + stats_t t_connect, t_request, t_total, t_resolve, t_write, t_ssl, t_close, stats_to, tcp_rtt_stats, stats_header_size; + char first_resolve = 1; + double graph_limit = MY_DOUBLE_INF; + char nc_graph = 1; + char adaptive_interval = 0; + double show_slow_log = MY_DOUBLE_INF; + char use_tcp_nodelay = 1; + int max_mtu = -1; + int write_sleep = 500; /* in us (microseconds), determines resolution of transmit time determination */ + char keep_cookies = 0; + char abbreviate = 0; + char *divert_connect = NULL; + int recv_buffer_size = -1, tx_buffer_size = -1; + int priority = -1, send_tos = -1; + char **additional_headers = NULL; + int n_additional_headers = 0; +#ifndef NO_SSL + SSL_CTX *client_ctx = NULL; +#endif + struct sockaddr_in6 addr; + struct addrinfo *ai = NULL, *ai_use = NULL; + struct addrinfo *ai_proxy = NULL, *ai_use_proxy = NULL; + + static struct option long_options[] = + { + {"aggregate", 1, NULL, 9 }, + {"url", 1, NULL, 'g' }, + {"hostname", 1, NULL, 'h' }, + {"port", 1, NULL, 'p' }, + {"proxy", 1, NULL, 'x' }, + {"count", 1, NULL, 'c' }, + {"persistent-connections", 0, NULL, 'Q' }, + {"interval", 1, NULL, 'i' }, + {"timeout", 1, NULL, 't' }, + {"ipv6", 0, NULL, '6' }, + {"show-statuscodes", 0, NULL, 's' }, + {"split-time", 0, NULL, 'S' }, + {"get-request", 0, NULL, 'G' }, + {"show-transfer-speed", 0, NULL, 'b' }, + {"show-xfer-speed-compressed", 0, NULL, 'B' }, + {"data-limit", 1, NULL, 'L' }, + {"show-kb", 0, NULL, 'X' }, + {"no-cache", 0, NULL, 'Z' }, +#ifndef NO_SSL + {"use-ssl", 0, NULL, 'l' }, + {"show-fingerprint", 0, NULL, 'z' }, +#endif + {"flood", 0, NULL, 'f' }, + {"audible-ping", 0, NULL, 'a' }, + {"parseable-output", 0, NULL, 'm' }, + {"ok-result-codes", 1, NULL, 'o' }, + {"result-string", 1, NULL, 'e' }, + {"user-agent", 1, NULL, 'I' }, + {"referer", 1, NULL, 'S' }, + {"resolve-once",0, NULL, 'r' }, + {"nagios-mode-1", 1, NULL, 'n' }, + {"nagios-mode-2", 1, NULL, 'n' }, + {"bind-to", 1, NULL, 'y' }, + {"quiet", 0, NULL, 'q' }, + {"username", 1, NULL, 'U' }, + {"password", 1, NULL, 'P' }, + {"cookie", 1, NULL, 'C' }, + {"colors", 0, NULL, 'Y' }, + {"offset-yellow", 1, NULL, 1 }, + {"threshold-yellow", 1, NULL, 1 }, + {"offset-red", 1, NULL, 2 }, + {"threshold-red", 1, NULL, 2 }, + {"offset-show", 1, NULL, 3 }, + {"show-offset", 1, NULL, 3 }, + {"threshold-show", 1, NULL, 3 }, + {"show-threshold", 1, NULL, 3 }, + {"timestamp", 0, NULL, 4 }, + {"ts", 0, NULL, 4 }, + {"no-host-header", 0, NULL, 5 }, + {"proxy-buster", 1, NULL, 6 }, + {"proxy-user", 1, NULL, 7 }, + {"proxy-password", 1, NULL, 8 }, + {"proxy-password-file", 1, NULL, 10 }, + {"graph-limit", 1, NULL, 11 }, + {"adaptive-interval", 0, NULL, 12 }, + {"ai", 0, NULL, 12 }, + {"slow-log", 0, NULL, 13 }, + {"draw-phase", 0, NULL, 14 }, + {"no-tcp-nodelay", 0, NULL, 15 }, + {"max-mtu", 1, NULL, 16 }, + {"keep-cookies", 0, NULL, 17 }, + {"abbreviate", 0, NULL, 18 }, + {"divert-connect", 1, NULL, 19 }, + {"recv-buffer", 1, NULL, 20 }, + {"tx-buffer", 1, NULL, 21 }, + {"priority", 1, NULL, 23 }, + {"tos", 1, NULL, 24 }, + {"header", 1, NULL, 25 }, +#ifdef NC + {"ncurses", 0, NULL, 'K' }, + {"gui", 0, NULL, 'K' }, +#ifdef FW + {"no-graph", 0, NULL, 'D' }, +#endif +#endif + {"version", 0, NULL, 'V' }, + {"help", 0, NULL, 22 }, + {NULL, 0, NULL, 0 } + }; + + bps.Bps_min = 1 << 30; + bps.Bps_max = -bps.Bps_min; + bps.Bps_avg = 0; + + setlocale(LC_ALL, ""); + bindtextdomain("HTTPing", LOCALEDIR); + textdomain("HTTPing"); + + init_statst(&t_resolve); + init_statst(&t_connect); + init_statst(&t_write); + init_statst(&t_request); + init_statst(&t_total); + init_statst(&t_ssl); + init_statst(&t_close); + + init_statst(&stats_to); +#if defined(linux) || defined(__FreeBSD__) + init_statst(&tcp_rtt_stats); +#endif + init_statst(&stats_header_size); + + determine_terminal_size(&max_y, &max_x); + + signal(SIGPIPE, SIG_IGN); + + while((c = getopt_long(argc, argv, "DKEA5MvYWT:ZQ6Sy:XL:bBg:h:p:c:i:Gx:t:o:e:falqsmV?I:R:rn:N:zP:U:C:F", long_options, NULL)) != -1) + { + switch(c) + { + case 25: + add_header(&additional_headers, &n_additional_headers, optarg); + break; + + case 24: + send_tos = atoi(optarg); + break; + + case 23: +#ifdef linux + priority = atoi(optarg); +#else + error_exit("Setting the network priority is only supported on Linux.\n"); +#endif + break; + + case 21: + tx_buffer_size = atoi(optarg); + break; + + case 20: + recv_buffer_size = atoi(optarg); + break; + + case 19: + divert_connect = optarg; + break; + + case 18: + abbreviate = 1; + break; + + case 17: + keep_cookies = 1; + break; + + case 16: + max_mtu = atoi(optarg); + break; + + case 15: + use_tcp_nodelay = 0; + break; + +#ifdef NC + case 14: + draw_phase = 1; + break; +#endif + + case 13: + show_slow_log = atof(optarg); + break; + + case 12: + adaptive_interval = 1; + break; + + case 11: + graph_limit = atof(optarg); + break; + +#ifdef NC + case 'K': + ncurses_mode = 1; + adaptive_interval = 1; + if (!wait_set) + wait = 0.5; + break; +#ifdef FW + case 'D': + nc_graph = 0; + break; +#endif +#endif + + case 'E': + do_fetch_proxy_settings = 1; + break; + + case 'A': + fprintf(stderr, gettext("\n *** -A is no longer required ***\n\n")); + break; + + case 'M': + json_output = 1; + break; + + case 'v': + verbose++; + break; + + case 1: + offset_yellow = atof(optarg); + break; + + case 2: + offset_red = atof(optarg); + break; + + case 3: + offset_show = atof(optarg); + break; + + case 4: + show_ts = 1; + break; + + case 5: + add_host_header = 0; + break; + + case 6: + proxy_buster = optarg; + break; + + case '5': + proxy_is_socks5 = 1; + break; + + case 7: + proxy_user = optarg; + break; + + case 8: + proxy_password = optarg; + break; + + case 9: + set_aggregate(optarg, &n_aggregates, &aggregates); + break; + + case 10: + proxy_password = read_file(optarg); + break; + + case 'Y': + colors = 1; + break; + + case 'W': + abort_on_resolve_failure = 0; + break; + + case 'T': + auth_password = read_file(optarg); + break; + + case 'Z': + no_cache = 1; + break; + + case '6': + use_ipv6 = 1; + break; + + case 'S': + split = 1; + break; + + case 'Q': + persistent_connections = 1; + break; + + case 'y': + parse_bind_to(optarg, &bind_to_4, &bind_to_6, &bind_to); + break; + + case 'z': + show_fp = 1; + break; + + case 'X': + show_bytes_xfer = 1; + break; + + case 'L': + Bps_limit = atoi(optarg); + break; + + case 'B': + show_Bps = 1; + ask_compression = 1; + break; + + case 'b': + show_Bps = 1; + break; + + case 'e': + err_str = optarg; + break; + + case 'o': + ok_str = optarg; + break; + + case 'x': + proxy_to_host_and_port(optarg, &proxy_host, &proxy_port); + break; + + case 'g': + url = optarg; + break; + + case 'r': + resolve_once = 1; + break; + + case 'h': + free(url); + url = NULL; + str_add(&url, "http://%s/", optarg); + break; + + case 'p': + portnr = atoi(optarg); + break; + + case 'c': + count = atoi(optarg); + break; + + case 'i': + wait = atof(optarg); + if (wait < 0.0) + error_exit(gettext("-i cannot have a value smaller than zero")); + wait_set = 1; + break; + + case 't': + timeout = atof(optarg); + break; + + case 'I': + useragent = optarg; + break; + + case 'R': + referer = optarg; + break; + + case 'a': + audible = 1; + break; + + case 'f': + wait = 0; + wait_set = 1; + adaptive_interval = 0; + break; + + case 'G': + get_instead_of_head = 1; + break; + +#ifndef NO_SSL + case 'l': + use_ssl = 1; + break; +#endif + + case 'm': + machine_readable = 1; + break; + + case 'q': + quiet = 1; + break; + + case 's': + show_statuscodes = 1; + break; + + case 'V': + version(); + return 0; + + case 'n': + if (nagios_mode) + error_exit(gettext("-n and -N are mutual exclusive\n")); + else + nagios_mode = 1; + + parse_nagios_settings(optarg, &nagios_warn, &nagios_crit); + break; + + case 'N': + if (nagios_mode) error_exit(gettext("-n and -N are mutual exclusive\n")); + nagios_mode = 2; + nagios_exit_code = atoi(optarg); + break; + + case 'P': + auth_password = optarg; + break; + + case 'U': + auth_usr = optarg; + break; + + case 'C': + add_cookie(&static_cookies, &n_static_cookies, optarg); + break; + + case 'F': +#ifdef TCP_TFO + use_tfo = 1; +#else + fprintf(stderr, gettext("Warning: TCP TFO is not supported. Disabling.\n")); +#endif + break; + + case 22: + version(); + + usage(argv[0]); + + return 0; + + case '?': + default: + fprintf(stderr, "\n"); + version(); + + fprintf(stderr, gettext("\n\nPlease run:\n\t%s --help\nto see a list of options.\n\n"), argv[0]); + + return 1; + } + } + + if (do_fetch_proxy_settings) + fetch_proxy_settings(&proxy_user, &proxy_password, &proxy_host, &proxy_port, use_ssl, use_ipv6); + + if (optind < argc) + url = argv[optind]; + + if (!url) + { + fprintf(stderr, gettext("No URL/host to ping given\n\n")); + return 1; + } + + if (machine_readable + json_output + ncurses_mode > 1) + error_exit(gettext("Cannot combine -m, -M and -K")); + + if ((machine_readable || json_output) && n_aggregates > 0) + error_exit(gettext("Aggregates can only be used in non-machine/json-output mode")); + + clear_error(); + + if (!(get_instead_of_head || use_ssl) && show_Bps) + error_exit(gettext("-b/-B can only be used when also using -G (GET instead of HEAD) or -l (use SSL)\n")); + + if (use_tfo && use_ssl) + error_exit(gettext("TCP Fast open and SSL not supported together\n")); + + if (colors) + set_colors(ncurses_mode); + + if (!machine_readable && !json_output) + printf("%s%s", c_normal, c_white); + + interpret_url(url, &get, &hostname, &portnr, use_ipv6, use_ssl, &complete_url, &au_dummy, &ap_dummy); + if (!auth_usr) + auth_usr = au_dummy; + if (!auth_password) + auth_password = ap_dummy; + +#ifdef NC + if (ncurses_mode) + { + if (wait == 0.0) + wait = 0.001; + + init_ncurses_ui(graph_limit, 1.0 / wait, colors); + } +#endif + + if (strncmp(complete_url, "https://", 8) == 0 && !use_ssl) + { + use_ssl = 1; +#ifdef NC + if (ncurses_mode) + { + slow_log(gettext("\nAuto enabling SSL due to https-URL")); + update_terminal(); + } + else +#endif + { + fprintf(stderr, gettext("Auto enabling SSL due to https-URL")); + } + } + + if (verbose) + { +#ifdef NC + if (ncurses_mode) + { + slow_log(gettext("\nConnecting to host %s, port %d and requesting file %s"), hostname, portnr, get); + + if (proxy_host) + slow_log(gettext("\nUsing proxyserver: %s:%d"), proxy_host, proxy_port); + } + else +#endif + { + printf(gettext("Connecting to host %s, port %d and requesting file %s\n\n"), hostname, portnr, get); + + if (proxy_host) + fprintf(stderr, gettext("Using proxyserver: %s:%d\n"), proxy_host, proxy_port); + } + } + +#ifndef NO_SSL + if (use_ssl) + { + client_ctx = initialize_ctx(ask_compression); + if (!client_ctx) + { + set_error(gettext("problem creating SSL context")); + goto error_exit; + } + } +#endif + + if (!quiet && !machine_readable && !nagios_mode && !json_output) + { +#ifdef NC + if (ncurses_mode) + slow_log("\nPING %s:%d (%s):", hostname, portnr, get); + else +#endif + printf("PING %s%s:%s%d%s (%s):\n", c_green, hostname, c_bright, portnr, c_normal, get); + } + + if (json_output) + printf("[\n"); + + if (adaptive_interval && wait <= 0.0) + error_exit(gettext("Interval must be > 0 when using adaptive interval")); + + signal(SIGINT, handler); + signal(SIGTERM, handler); + + signal(SIGQUIT, handler_quit); + + timeout *= 1000.0; /* change to ms */ + + /* + if (follow_30x) + { + get headers + + const char *get_location(const char *host, int port, char use_ssl, char *reply) + + set new host/port/path/etc + } + */ + + started_at = get_ts(); + if (proxy_host) + { +#ifdef NC + if (ncurses_mode) + { + slow_log(gettext("\nResolving hostname %s"), proxy_host); + update_terminal(); + } +#endif + + if (resolve_host(proxy_host, &ai_proxy, use_ipv6, proxy_port) == -1) + error_exit(get_error()); + + ai_use_proxy = select_resolved_host(ai_proxy, use_ipv6); + if (!ai_use_proxy) + error_exit(gettext("No valid IPv4 or IPv6 address found for %s"), proxy_host); + } + else if (resolve_once) + { + char *res_host = divert_connect ? divert_connect : hostname; +#ifdef NC + if (ncurses_mode) + { + slow_log(gettext("\nResolving hostname %s"), res_host); + update_terminal(); + } +#endif + + if (resolve_host(res_host, &ai, use_ipv6, portnr) == -1) + { + err++; + emit_error(verbose, -1, started_at); + have_resolved = 0; + if (abort_on_resolve_failure) + error_exit(get_error()); + } + + ai_use = select_resolved_host(ai, use_ipv6); + if (!ai_use) + { + set_error(gettext("No valid IPv4 or IPv6 address found for %s"), res_host); + + if (abort_on_resolve_failure) + error_exit(get_error()); + + /* do not emit the resolve-error here: as 'have_resolved' is set to 0 + next, the program will try to resolve again anyway + this prevents a double error-message while err is increased only + once + */ + have_resolved = 0; + } + + if (have_resolved) + get_addr(ai_use, &addr); + } + + if (persistent_connections) + fd = -1; + + while((curncount < count || count == -1) && stop == 0) + { + double dstart = -1.0, dend = -1.0, dafter_connect = 0.0, dafter_resolve = 0.0, dafter_write_complete = 0.0; + char *reply = NULL; + double Bps = 0; + char is_compressed = 0; + long long int bytes_transferred = 0; + time_t their_ts = 0; + int age = -1; + char *sc = NULL, *scdummy = NULL; + char *fp = NULL; + int re_tx = 0, pmtu = 0, recv_tos = 0; + socklen_t recv_tos_len = sizeof recv_tos; + + dstart = get_ts(); + + for(;;) + { + char did_reconnect = 0; + int rc = -1; + int persistent_tries = 0; + int len = 0, overflow = 0, headers_len = 0; + char req_sent = 0; + double dummy_ms = 0.0; + double their_est_ts = -1.0, toff_diff_ts = -1.0; + char tfo_success = 0; + double ssl_handshake = 0.0; + char cur_have_resolved = 0; +#if defined(linux) || defined(__FreeBSD__) + struct tcp_info info; + socklen_t info_len = sizeof(struct tcp_info); +#endif + + curncount++; + +persistent_loop: + if ((!resolve_once || (resolve_once == 1 && have_resolved == 0)) && fd == -1 && proxy_host == NULL) + { + char *res_host = divert_connect ? divert_connect : hostname; + + memset(&addr, 0x00, sizeof addr); + +#ifdef NC + if (ncurses_mode && first_resolve) + { + slow_log(gettext("\nResolving hostname %s"), res_host); + update_terminal(); + first_resolve = 0; + } +#endif + + if (ai) + { + freeaddrinfo(ai); + + ai_use = ai = NULL; + } + + if (resolve_host(res_host, &ai, use_ipv6, portnr) == -1) + { + err++; + emit_error(verbose, curncount, dstart); + + if (abort_on_resolve_failure) + error_exit(get_error()); + break; + } + + ai_use = select_resolved_host(ai, use_ipv6); + if (!ai_use) + { + set_error(gettext("No valid IPv4 or IPv6 address found for %s"), res_host); + emit_error(verbose, curncount, dstart); + err++; + + if (abort_on_resolve_failure) + error_exit(get_error()); + + break; + } + + get_addr(ai_use, &addr); + + cur_have_resolved = have_resolved = 1; + } + + if (cur_have_resolved) + { + dafter_resolve = get_ts(); + dummy_ms = (dafter_resolve - dstart) * 1000.0; + update_statst(&t_resolve, dummy_ms); + } + + free(request); + request = create_http_request_header(proxy_host ? complete_url : get, proxy_host ? 1 : 0, get_instead_of_head, persistent_connections, add_host_header ? hostname : NULL, useragent, referer, ask_compression, no_cache, auth_usr, auth_password, static_cookies, n_static_cookies, dynamic_cookies, keep_cookies ? n_dynamic_cookies : 0, proxy_buster, proxy_user, proxy_password, additional_headers, n_additional_headers); + req_len = strlen(request); + + if (req_len >= 4096) + { + char *line = NULL; + static int notify_cnt = 0; + + notify_cnt++; + + if (notify_cnt == MAX_SHOW_SUPPRESSION + 1) + str_add(&line, gettext("Will no longer inform about request headers too large.")); + else if (notify_cnt <= MAX_SHOW_SUPPRESSION) + str_add(&line, gettext("Request headers > 4KB! (%d bytes) This may give failures with some HTTP servers."), req_len); + + if (line) + { +#ifdef NC + if (ncurses_mode) + slow_log("\n%s", line); + else +#endif + printf("%s\n", line); + } + + free(line); + } + + if ((persistent_connections && fd < 0) || !persistent_connections) + { + int rc = -1; + struct addrinfo *ai_dummy = proxy_host ? ai_use_proxy : ai_use; + + fd = create_socket((struct sockaddr *)bind_to, ai_dummy, recv_buffer_size, tx_buffer_size, max_mtu, use_tcp_nodelay, priority, send_tos); + if (fd < 0) + rc = fd; /* FIXME need to fix this, this is ugly */ + else if (proxy_host) + { + if (proxy_is_socks5) + rc = socks5connect(fd, ai_dummy, timeout, proxy_user, proxy_password, hostname, portnr, abort_on_resolve_failure); +#ifndef NO_SSL + else if (use_ssl) + rc = connect_ssl_proxy(fd, ai_dummy, timeout, proxy_user, proxy_password, hostname, portnr, &use_tfo); +#endif + else + rc = connect_to(fd, ai_dummy, timeout, &use_tfo, request, req_len, &req_sent); + } + else + { + + rc = connect_to(fd, ai_dummy, timeout, &use_tfo, request, req_len, &req_sent); + } + + if (rc < 0) + { + failure_close(fd); + fd = rc; /* FIXME need to fix this */ + } + + did_reconnect = 1; + } + + if (fd == RC_CTRLC) /* ^C pressed */ + break; + + if (fd < 0) + { + emit_error(verbose, curncount, dstart); + fd = -1; + } + + if (fd >= 0) + { + /* set fd blocking */ + if (set_fd_blocking(fd) == -1) + { + stats_close(&fd, &t_close, 1); + break; + } + +#ifndef NO_SSL + if (use_ssl && ssl_h == NULL) + { + int rc = connect_ssl(fd, client_ctx, &ssl_h, &s_bio, timeout, &ssl_handshake); + if (rc == 0) + update_statst(&t_ssl, ssl_handshake); + else + { + stats_close(&fd, &t_close, 1); + fd = rc; + + if (persistent_connections && ++persistent_tries < 2) + { + persistent_did_reconnect = 1; + + goto persistent_loop; + } + } + } +#endif + } + + dafter_connect = get_ts(); + + if (did_reconnect) + { + if (cur_have_resolved) + dummy_ms = (dafter_connect - dafter_resolve) * 1000.0; + else + dummy_ms = (dafter_connect - dstart) * 1000.0; + + update_statst(&t_connect, dummy_ms - ssl_handshake); + } + + if (fd < 0) + { + if (fd == RC_TIMEOUT) + set_error(gettext("timeout connecting to host")); + + emit_error(verbose, curncount, dstart); + err++; + + fd = -1; + + break; + } + +#ifndef NO_SSL + if (use_ssl) + rc = WRITE_SSL(ssl_h, request, req_len); + else +#endif + { + if (!req_sent) + rc = mywrite(fd, request, req_len, timeout); + else + rc = req_len; + } + + /* wait for data transmit(!) to complete, + e.g. until the transmitbuffers are empty and the data was + sent to the next hop + */ + for(;;) + { + int bytes_left = 0; + int i_rc = ioctl(fd, TIOCOUTQ, &bytes_left); + + if (i_rc == -1 || bytes_left == 0 || stop) + break; + + dafter_write_complete = get_ts(); + if ((dafter_write_complete - dafter_connect) * 1000.0 >= timeout) + break; + + /* this keeps it somewhat from becoming a busy loop + * I know of no other way to wait for the kernel to + * finish the transmission + */ + usleep(write_sleep); + } + + dafter_write_complete = get_ts(); + + dummy_ms = (dafter_write_complete - dafter_connect) * 1000.0; + update_statst(&t_write, dummy_ms); + + if (rc != req_len) + { + if (persistent_connections) + { + if (++persistent_tries < 2) + { + stats_close(&fd, &t_close, 0); + persistent_did_reconnect = 1; + goto persistent_loop; + } + } + + if (rc == -1) + set_error(gettext("error sending request to host")); + else if (rc == RC_TIMEOUT) + set_error(gettext("timeout sending to host")); + else if (rc == RC_INVAL) + set_error(gettext("retrieved invalid data from host")); + else if (rc == RC_CTRLC) + {/* ^C */} + else if (rc == 0) + set_error(gettext("connection prematurely closed by peer")); + + emit_error(verbose, curncount, dstart); + + stats_close(&fd, &t_close, 1); + err++; + + break; + } + +#if defined(linux) || defined(__FreeBSD__) +#ifdef NC + if (!use_ssl && ncurses_mode) + { + struct timeval tv; + int t_rc = -1; + + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = (long)(timeout / 1000.0) % 1000000; + tv.tv_usec = (long)(timeout * 1000.0) % 1000000; + + t_rc = select(fd + 1, &rfds, NULL, NULL, &tv); + +#ifdef linux + if (t_rc == 1 && \ + FD_ISSET(fd, &rfds) && \ + getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0 && \ + info.tcpi_unacked > 0) + { + static int in_transit_cnt = 0; + + in_transit_cnt++; + if (in_transit_cnt == MAX_SHOW_SUPPRESSION + 1) + slow_log(gettext("\nNo longer emitting message about \"still data in transit\"")); + else if (in_transit_cnt <= MAX_SHOW_SUPPRESSION) + slow_log(gettext("\nHTTP server started sending data with %d bytes still in transit"), info.tcpi_unacked); + } +#endif + + if (t_rc == 0) + { + stats_close(&fd, &t_close, 1); + + rc = RC_TIMEOUT; + set_error(gettext("timeout sending to host")); + + emit_error(verbose, curncount, dstart); + + err++; + + break; + } + } +#endif + + if (getsockopt(fd, IPPROTO_IP, IP_TOS, &recv_tos, &recv_tos_len) == -1) + { + set_error(gettext("failed to obtain TOS info")); + recv_tos = -1; + } +#endif + + rc = get_HTTP_headers(fd, ssl_h, &reply, &overflow, timeout); + +#ifdef NC + if (ncurses_mode && !get_instead_of_head && overflow > 0) + { + static int more_data_cnt = 0; + + more_data_cnt++; + if (more_data_cnt == MAX_SHOW_SUPPRESSION + 1) + slow_log(gettext("\nNo longer emitting message about \"more data than response headers\"")); + else if (more_data_cnt <= MAX_SHOW_SUPPRESSION) + slow_log(gettext("\nHTTP server sent more data than just the response headers")); + } +#endif + + emit_headers(reply); + + if (reply) + { + free_cookies(dynamic_cookies, n_dynamic_cookies); + dynamic_cookies = NULL; + n_dynamic_cookies = 0; + + get_cookies(reply, &dynamic_cookies, &n_dynamic_cookies, &static_cookies, &n_static_cookies); + } + + if ((show_statuscodes || machine_readable || json_output || ncurses_mode) && reply != NULL) + { + /* statuscode is in first line behind + * 'HTTP/1.x' + */ + char *dummy = strchr(reply, ' '); + + if (dummy) + { + sc = strdup(dummy + 1); + + /* lines are normally terminated with a + * CR/LF + */ + dummy = strchr(sc, '\r'); + if (dummy) + *dummy = 0x00; + dummy = strchr(sc, '\n'); + if (dummy) + *dummy = 0x00; + } + } + + their_ts = parse_date_from_response_headers(reply); + + age = calc_page_age(reply, their_ts); + + is_compressed = check_compressed(reply); + + if (persistent_connections && show_bytes_xfer && reply != NULL) + { + char *length = strstr(reply, "\nContent-Length:"); + if (!length) + { + set_error(gettext("'Content-Length'-header missing!")); + emit_error(verbose, curncount, dstart); + stats_close(&fd, &t_close, 1); + break; + } + + len = atoi(&length[17]); + } + + headers_len = 0; + if (reply) + { + headers_len = strlen(reply) + 4 - overflow; + free(reply); + reply = NULL; + } + + update_statst(&stats_header_size, headers_len); + + if (rc < 0) + { + if (persistent_connections) + { + if (++persistent_tries < 2) + { + stats_close(&fd, &t_close, 0); + persistent_did_reconnect = 1; + goto persistent_loop; + } + } + + if (rc == RC_SHORTREAD) + set_error(gettext("short read during receiving reply-headers from host")); + else if (rc == RC_TIMEOUT) + set_error(gettext("timeout while receiving reply-headers from host")); + + emit_error(verbose, curncount, dstart); + + stats_close(&fd, &t_close, 1); + err++; + + break; + } + + ok++; + + if (get_instead_of_head && show_Bps) + { + int buffer_size = RECV_BUFFER_SIZE; + char *buffer = (char *)malloc(buffer_size); + double dl_start = get_ts(), dl_end; + double cur_limit = Bps_limit; + + if (persistent_connections) + { + if (cur_limit == -1 || len < cur_limit) + cur_limit = len - overflow; + } + + for(;;) + { + int n = cur_limit != -1 ? min(cur_limit - bytes_transferred, buffer_size) : buffer_size; + int rc = read(fd, buffer, n); + + if (rc == -1) + { + if (errno != EINTR && errno != EAGAIN) + error_exit(gettext("read of response body dataa failed")); + } + else if (rc == 0) + { + break; + } + + bytes_transferred += rc; + + if (cur_limit != -1 && bytes_transferred >= cur_limit) + break; + } + + free(buffer); + + dl_end = get_ts(); + + Bps = (double)bytes_transferred / max(dl_end - dl_start, 0.000001); + bps.Bps_min = min(bps.Bps_min, Bps); + bps.Bps_max = max(bps.Bps_max, Bps); + bps.Bps_avg += Bps; + } + + dend = get_ts(); + +#ifndef NO_SSL + if (use_ssl) + { + if ((show_fp || json_output || ncurses_mode) && ssl_h != NULL) + fp = get_fingerprint(ssl_h); + + if (!persistent_connections) + { + if (close_ssl_connection(ssl_h) == -1) + { + set_error(gettext("error shutting down ssl")); + emit_error(verbose, curncount, dstart); + } + + SSL_free(ssl_h); + ssl_h = NULL; + s_bio = NULL; + } + } +#endif + +#if defined(linux) || defined(__FreeBSD__) + if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0) + { +#ifdef TCP_TFO + if (info.tcpi_options & TCPI_OPT_SYN_DATA) + tfo_success = 1; +#endif + + update_statst(&tcp_rtt_stats, (double)info.tcpi_rtt / 1000.0); + +#ifdef linux + re_tx = info.tcpi_retransmits; + pmtu = info.tcpi_pmtu; +#endif + } +#endif + + if (!persistent_connections) + stats_close(&fd, &t_close, 0); + + dummy_ms = (dend - dafter_write_complete) * 1000.0; + update_statst(&t_request, dummy_ms); + + dummy_ms = (dend - dstart) * 1000.0; + update_statst(&t_total, dummy_ms); + + their_est_ts = (dend + dafter_connect) / 2.0; /* estimate of when other end started replying */ + toff_diff_ts = ((double)their_ts - their_est_ts) * 1000.0; + update_statst(&stats_to, toff_diff_ts); + + if (json_output) + { + char current_host[4096] = { 0 }; + + if (proxy_host) + snprintf(current_host, sizeof current_host, "%s", hostname); + else if (getnameinfo((const struct sockaddr *)&addr, sizeof addr, current_host, sizeof current_host, NULL, 0, NI_NUMERICHOST) == -1) + snprintf(current_host, sizeof current_host, gettext("getnameinfo() failed: %d (%s)"), errno, strerror(errno)); + + emit_json(1, curncount, dstart, &t_resolve, &t_connect, &t_request, atoi(sc ? sc : "-1"), sc ? sc : "?", headers_len, len, Bps, current_host, fp, toff_diff_ts, tfo_success, &t_ssl, &t_write, &t_close, n_dynamic_cookies, &stats_to, &tcp_rtt_stats, re_tx, pmtu, recv_tos, &t_total); + } + else if (machine_readable) + { + if (sc) + { + char *dummy = strchr(sc, ' '); + if (dummy) + *dummy = 0x00; + + if (strstr(ok_str, sc)) + printf("%f", t_total.cur); + else + printf("%s", err_str); + + if (show_statuscodes) + printf(" %s", sc); + } + else + { + printf("%s", err_str); + } + + if(audible) + putchar('\a'); + + printf("\n"); + } + else if (!quiet && !nagios_mode && t_total.cur >= offset_show) + { + char *tot_str = NULL; + const char *i6_bs = "", *i6_be = ""; + char *line = NULL; + const char *ms_color = c_green; + char current_host[4096] = { 0 }; + char *operation = !persistent_connections ? gettext("connected to") : gettext("pinged host"); + const char *sep = c_bright, *unsep = c_normal; + + if (show_ts || ncurses_mode) + { + char *ts = get_ts_str(verbose); + + if (ncurses_mode) + str_add(&line, "[%s] ", ts); + else + str_add(&line, "%s ", ts); + + free(ts); + } + + if (curncount & 1) + { + str_add(&line, "%s", c_bright); + + sep = c_normal; + unsep = c_bright; + } + + if (proxy_host) + snprintf(current_host, sizeof current_host, "%s", hostname); + else if (getnameinfo((const struct sockaddr *)&addr, sizeof addr, current_host, sizeof current_host, NULL, 0, NI_NUMERICHOST) == -1) + snprintf(current_host, sizeof current_host, gettext("getnameinfo() failed: %d (%s)"), errno, strerror(errno)); + + if (addr.sin6_family == AF_INET6) + { + i6_bs = "["; + i6_be = "]"; + } + + if (offset_red > 0.0 && t_total.cur >= offset_red) + ms_color = c_red; + else if (offset_yellow > 0.0 && t_total.cur >= offset_yellow) + ms_color = c_yellow; + + if (!ncurses_mode) + str_add(&line, "%s%s ", c_white, operation); + + if (persistent_connections && show_bytes_xfer) + str_add(&line, gettext("%s%s%s%s%s:%s%d%s (%d/%d bytes), seq=%s%d%s "), c_red, i6_bs, current_host, i6_be, c_white, c_yellow, portnr, c_white, headers_len, len, c_blue, curncount-1, c_white); + else + str_add(&line, gettext("%s%s%s%s%s:%s%d%s (%d bytes), seq=%s%d%s "), c_red, i6_bs, current_host, i6_be, c_white, c_yellow, portnr, c_white, headers_len, c_blue, curncount-1, c_white); + + tot_str = format_value(t_total.cur, 6, 2, abbreviate); + + if (split) + { + char *res_str = t_resolve.cur_valid ? format_value(t_resolve.cur, 6, 2, abbreviate) : strdup(gettext(" n/a")); + char *con_str = t_connect.cur_valid ? format_value(t_connect.cur, 6, 2, abbreviate) : strdup(gettext(" n/a")); + char *wri_str = format_value(t_write.cur, 6, 2, abbreviate); + char *req_str = format_value(t_request.cur, 6, 2, abbreviate); + char *clo_str = format_value(t_close.cur, 6, 2, abbreviate); + + str_add(&line, gettext("time=%s+%s+%s+%s+%s%s=%s%s%s%s ms %s%s%s"), res_str, con_str, wri_str, req_str, clo_str, sep, unsep, ms_color, tot_str, c_white, c_cyan, sc?sc:"", c_white); + + free(clo_str); + free(req_str); + free(wri_str); + free(con_str); + free(res_str); + } + else + { + str_add(&line, gettext("time=%s%s%s ms %s%s%s"), ms_color, tot_str, c_white, c_cyan, sc?sc:"", c_white); + } + + free(tot_str); + + if (persistent_did_reconnect) + { + str_add(&line, " %sC%s", c_magenta, c_white); + persistent_did_reconnect = 0; + } + + if (show_Bps) + { + str_add(&line, " %dKB/s", (int)Bps / 1024); + if (show_bytes_xfer) + str_add(&line, " %dKB", (int)(bytes_transferred / 1024)); + if (ask_compression) + { + str_add(&line, " ("); + if (!is_compressed) + str_add(&line, gettext("not ")); + str_add(&line, gettext("compressed)")); + } + } + + if (use_ssl && show_fp && fp != NULL) + { + str_add(&line, " %s", fp); + } + + if (verbose > 0 && their_ts > 0) + { + /* if diff_ts > 0, then their clock is running too fast */ + str_add(&line, gettext(" toff=%d"), (int)toff_diff_ts); + } + + if (verbose > 0 && age > 0) + str_add(&line, gettext(" age=%d"), age); + + str_add(&line, "%s", c_normal); + + if (audible) + { +#ifdef NC + my_beep(); +#else + putchar('\a'); +#endif + } + +#ifdef TCP_TFO + if (tfo_success) + str_add(&line, " F"); +#endif + +#ifdef NC + if (ncurses_mode) + { + if (dummy_ms >= show_slow_log) + slow_log("\n%s", line); + else + fast_log("\n%s", line); + } + else +#endif + { + printf("%s\n", line); + } + + do_aggregates(t_total.cur, (int)(get_ts() - started_at), n_aggregates, aggregates, verbose, show_ts); + + free(line); + } + + if (show_statuscodes && ok_str != NULL && sc != NULL) + { + scdummy = strchr(sc, ' '); + if (scdummy) *scdummy = 0x00; + + if (strstr(ok_str, sc) == NULL) + { + ok--; + err++; + } + } + + if (got_sigquit && !quiet && !machine_readable && !nagios_mode && !json_output) + { + got_sigquit = 0; + stats_line(0, complete_url, count, curncount, err, ok, started_at, verbose, &t_total, avg_httping_time, show_Bps ? &bps : NULL); + } + + break; + } + + emit_statuslines(get_ts() - started_at); +#ifdef NC + if (ncurses_mode) + update_stats(&t_resolve, &t_connect, &t_request, &t_total, &t_ssl, curncount, err, sc, fp, use_tfo, nc_graph, &stats_to, &tcp_rtt_stats, re_tx, pmtu, recv_tos, &t_close, &t_write, n_dynamic_cookies, abbreviate, &stats_header_size); +#endif + + free(sc); + free(fp); + +#ifdef NC + if (ncurses_mode) + update_terminal(); + else +#endif + fflush(NULL); + + if (!stop && wait > 0) + { + double cur_sleep = wait; + + if (adaptive_interval) + { + double now = get_ts(); + double interval_left = fmod(now - started_at, wait); + + if (interval_left <= 0.0) + cur_sleep = wait; + else + cur_sleep = wait - interval_left; + } + + usleep((useconds_t)(cur_sleep * 1000000.0)); + } + + reset_statst_cur(&t_resolve); + reset_statst_cur(&t_connect); + reset_statst_cur(&t_ssl); + reset_statst_cur(&t_write); + reset_statst_cur(&t_request); + reset_statst_cur(&t_close); + reset_statst_cur(&t_total); + reset_statst_cur(&stats_to); +#if defined(linux) || defined(__FreeBSD__) + reset_statst_cur(&tcp_rtt_stats); +#endif + } + +#ifdef NC + if (ncurses_mode) + end_ncurses(); +#endif + + if (colors) + set_colors(0); + + if (ok) + avg_httping_time = t_total.avg / (double)t_total.n; + else + avg_httping_time = -1.0; + + if (!quiet && !machine_readable && !nagios_mode && !json_output) + stats_line(1, complete_url, count, curncount, err, ok, started_at, verbose, &t_total, avg_httping_time, show_Bps ? &bps : NULL); + +error_exit: + if (nagios_mode) + return nagios_result(ok, nagios_mode, nagios_exit_code, avg_httping_time, nagios_warn, nagios_crit); + + if (!json_output && !machine_readable) + printf("%s", c_very_normal); + + if (json_output) + printf("\n]\n"); + + free_cookies(static_cookies, n_static_cookies); + free_cookies(dynamic_cookies, n_dynamic_cookies); + freeaddrinfo(ai); + free(request); + free(get); + free(hostname); + free(complete_url); + + free_headers(additional_headers, n_additional_headers); + + free(aggregates); + +#ifndef NO_SSL + if (use_ssl) + { + SSL_CTX_free(client_ctx); + + shutdown_ssl(); + } +#endif + + fflush(NULL); + + if (ok) + return 0; + else + return 127; +} diff --git a/main.h b/main.h new file mode 100644 index 0000000..15cbe31 --- /dev/null +++ b/main.h @@ -0,0 +1,5 @@ +extern volatile int stop; +extern int max_x, max_y; + +void determine_terminal_size(int *max_y, int *max_x); +void handler(int sig); diff --git a/mssl.c b/mssl.c new file mode 100644 index 0000000..f98117a --- /dev/null +++ b/mssl.c @@ -0,0 +1,298 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "gen.h" +#include "mssl.h" +#include "tcp.h" +#include "io.h" +#include "http.h" +#include "utils.h" + +BIO *bio_err = NULL; + +void shutdown_ssl(void) +{ + BIO_free(bio_err); + + ERR_free_strings(); + + ERR_remove_state(0); + ENGINE_cleanup(); + CONF_modules_free(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +} + +char close_ssl_connection(SSL *ssl_h) +{ + int rc = SSL_shutdown(ssl_h); + + if (!rc) + rc = SSL_shutdown(ssl_h); + +/* +ignoring failures: the socket will be closed +anyway later on so any openssl failures won't +do any harm + if (rc == -1) + { + fprintf(stderr, "SSL_shutdown failed: %s\n", strerror(errno)); + return -1; + } +*/ + + return 0; +} + +int READ_SSL(SSL *ssl_h, char *whereto, int len) +{ + int cnt=len; + + while(len>0) + { + int rc; + + rc = SSL_read(ssl_h, whereto, len); + if (rc == -1) + { + if (errno != EINTR && errno != EAGAIN) + { + set_error(gettext("READ_SSL: io-error: %s"), strerror(errno)); + return -1; + } + } + else if (rc == 0) + { + return 0; + } + else + { + whereto += rc; + len -= rc; + } + } + + return cnt; +} + +int WRITE_SSL(SSL *ssl_h, const char *wherefrom, int len) +{ + int cnt=len; + + while(len>0) + { + int rc; + + rc = SSL_write(ssl_h, wherefrom, len); + if (rc == -1) + { + if (errno != EINTR && errno != EAGAIN) + { + set_error(gettext("WRITE_SSL: io-error: %s"), strerror(errno)); + return -1; + } + } + else if (rc == 0) + { + return 0; + } + else + { + wherefrom += rc; + len -= rc; + } + } + + return cnt; +} + +int connect_ssl(int fd, SSL_CTX *client_ctx, SSL **ssl_h, BIO **s_bio, double timeout, double *ssl_handshake) +{ + int dummy = -1; + double dstart = get_ts(); + + struct timeval tv; + tv.tv_sec = (long)(timeout / 1000.0); + tv.tv_usec = (long)(timeout * 1000.0) % 1000000; + + *ssl_handshake = -1.0; + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv) == -1) + { + set_error(gettext("problem setting receive timeout (%s)"), strerror(errno)); + return -1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv) == -1) + { + set_error(gettext("problem setting transmit timeout (%s)"), strerror(errno)); + return -1; + } + + *ssl_h = SSL_new(client_ctx); + + *s_bio = BIO_new_socket(fd, BIO_NOCLOSE); + SSL_set_bio(*ssl_h, *s_bio, *s_bio); + + dummy = SSL_connect(*ssl_h); + if (dummy <= 0) + { + set_error(gettext("problem starting SSL connection: %d"), SSL_get_error(*ssl_h, dummy)); + return -1; + } + + *ssl_handshake = get_ts() - dstart; + + return 0; +} + +SSL_CTX * initialize_ctx(char ask_compression) +{ + const SSL_METHOD *meth = NULL; + SSL_CTX *ctx = NULL; + + if (!bio_err) + { + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_crypto_strings(); + + /* error write context */ + bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); + } + + /* create context */ + meth = SSLv23_method(); + ctx = SSL_CTX_new(meth); + +#ifdef SSL_OP_NO_COMPRESSION + if (!ask_compression) + SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); +#endif + + return ctx; +} + +char * get_fingerprint(SSL *ssl_h) +{ + char *string = NULL; + + unsigned char fp_digest[EVP_MAX_MD_SIZE]; + X509 *x509_data = SSL_get_peer_certificate(ssl_h); + + if (x509_data) + { + unsigned int fp_digest_size = sizeof fp_digest; + + memset(fp_digest, 0x00, fp_digest_size); + + if (X509_digest(x509_data, EVP_md5(), fp_digest, &fp_digest_size)) + { + string = (char *)malloc(MD5_DIGEST_LENGTH * 3 + 1); + if (string) + { + int loop, pos =0; + + for(loop=0; loop +#include +#include +#include + +void shutdown_ssl(void); +char close_ssl_connection(SSL *ssl_h); +int READ_SSL(SSL *ssl_h, char *whereto, int len); +int WRITE_SSL(SSL *ssl_h, const char *whereto, int len); +int connect_ssl(int fd, SSL_CTX *client_ctx, SSL **ssl_h, BIO **s_bio, double timeout, double *ssl_handshake); +SSL_CTX * initialize_ctx(char ask_compression); +char * get_fingerprint(SSL *ssl_h); +int connect_ssl_proxy(int fd, struct addrinfo *ai, double timeout, const char *proxy_user, const char *proxy_password, const char *hostname, int portnr, char *tfo); diff --git a/nc.c b/nc.c new file mode 100644 index 0000000..dd82e0f --- /dev/null +++ b/nc.c @@ -0,0 +1,845 @@ +/* $Revision$ */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "error.h" +#include "colors.h" +#include "gen.h" +#include "main.h" +#include "kalman.h" +#ifdef FW +#include "fft.h" +#endif +#include "utils.h" + +char win_resize = 0; +WINDOW *w_stats = NULL, *w_line1 = NULL, *w_slow = NULL, *w_line2 = NULL, *w_fast = NULL; +int stats_h = 10; +int logs_n = 0, slow_n = 0, fast_n = 0; +char **slow_history = NULL, **fast_history = NULL; +int window_history_n = 0; +char use_colors = 0; + +double graph_limit = MY_DOUBLE_INF; +double hz = 1.0; + +double *history = NULL, *history_temp = NULL, *history_fft_magn = NULL, *history_fft_phase = NULL; +char *history_set = NULL; +int history_n = 0; + +char draw_phase = 0; + +char pause_graphs = 0; + +typedef enum { C_WHITE = 0, C_GREEN, C_YELLOW, C_BLUE, C_MAGENTA, C_CYAN, C_RED } color_t; + +void update_terminal(void) +{ + wnoutrefresh(w_stats); + wnoutrefresh(w_slow); + wnoutrefresh(w_fast); + + doupdate(); +} + +void create_windows(void) +{ + char *r_a = gettext("realloc issue"); + int nr = 0; + + if (w_stats) + { + delwin(w_stats); + delwin(w_line1); + delwin(w_slow); + delwin(w_line2); + delwin(w_fast); + } + +#ifdef FW + fft_free(); + fft_init(max_x); +#endif + + if (max_x > history_n) + { + history = (double *)realloc(history, sizeof(double) * max_x); + if (!history) + error_exit(r_a); + + history_temp = (double *)realloc(history_temp, sizeof(double) * max_x); + if (!history_temp) + error_exit(r_a); + + /* halve of it is enough really */ + history_fft_magn = (double *)realloc(history_fft_magn, sizeof(double) * max_x); + if (!history_fft_magn) + error_exit(r_a); + + history_fft_phase = (double *)realloc(history_fft_phase, sizeof(double) * max_x); + if (!history_fft_phase) + error_exit(r_a); + + history_set = (char *)realloc(history_set, sizeof(char) * max_x); + if (!history_set) + error_exit(r_a); + + memset(&history[history_n], 0x00, (max_x - history_n) * sizeof(double)); + memset(&history_set[history_n], 0x00, (max_x - history_n) * sizeof(char)); + + history_n = max_x; + } + + if ((int)max_y > window_history_n) + { + slow_history = (char **)realloc(slow_history, sizeof(char *) * max_y); + if (!slow_history) + error_exit(r_a); + + fast_history = (char **)realloc(fast_history, sizeof(char *) * max_y); + if (!fast_history) + error_exit(r_a); + + memset(&slow_history[window_history_n], 0x00, (max_y - window_history_n) * sizeof(char *)); + memset(&fast_history[window_history_n], 0x00, (max_y - window_history_n) * sizeof(char *)); + + window_history_n = max_y; + } + + w_stats = newwin(stats_h, max_x, 0, 0); + scrollok(w_stats, false); + + w_line1 = newwin(1, max_x, stats_h, 0); + scrollok(w_line1, false); + wnoutrefresh(w_line1); + + logs_n = max_y - (stats_h + 1 + 1); + fast_n = logs_n * 11 / 20; + slow_n = logs_n - fast_n; + + w_slow = newwin(slow_n, max_x, (stats_h + 1), 0); + scrollok(w_slow, true); + + w_line2 = newwin(1, max_x, (stats_h + 1) + slow_n, 0); + scrollok(w_line2, false); + wnoutrefresh(w_line2); + + w_fast = newwin(fast_n, max_x, (stats_h + 1) + slow_n + 1, 0); + scrollok(w_fast, true); + + wattron(w_line1, A_REVERSE); + wattron(w_line2, A_REVERSE); + for(nr=0; nr= 0; index--) + { + if (slow_history[index]) + myprint(w_slow, slow_history[index]); + if (fast_history[index]) + myprint(w_fast, fast_history[index]); + } + + doupdate(); + + win_resize = 0; +} + +void init_ncurses_ui(double graph_limit_in, double hz_in, char use_colors_in) +{ + graph_limit = graph_limit_in; + hz = hz_in; + use_colors = use_colors_in; + + initscr(); + start_color(); + keypad(stdscr, TRUE); + intrflush(stdscr, FALSE); + noecho(); + refresh(); + nodelay(stdscr, FALSE); + meta(stdscr, TRUE); /* enable 8-bit input */ + idlok(stdscr, TRUE); /* may give a little clunky screenredraw */ + idcok(stdscr, TRUE); /* may give a little clunky screenredraw */ + leaveok(stdscr, FALSE); + + init_pair(C_WHITE, COLOR_WHITE, COLOR_BLACK); + init_pair(C_CYAN, COLOR_CYAN, COLOR_BLACK); + init_pair(C_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); + init_pair(C_BLUE, COLOR_BLUE, COLOR_BLACK); + init_pair(C_YELLOW, COLOR_YELLOW, COLOR_BLACK); + init_pair(C_GREEN, COLOR_GREEN, COLOR_BLACK); + init_pair(C_RED, COLOR_RED, COLOR_BLACK); + + kalman_init(0.0); + + recreate_terminal(); +} + +void end_ncurses(void) +{ + int index = 0; + + for(index=0; index= end_y; y--) + mvwchgat(win, y, x, 1, A_REVERSE, C_YELLOW, dummy); + + if (limitter) + mvwchgat(win, 0, x, 1, A_REVERSE, C_BLUE, dummy); + else if (overflow) + mvwchgat(win, 0, x, 1, A_REVERSE, C_RED, dummy); + else if (height == 0) + mvwchgat(win, win_h - 1, x, 1, A_REVERSE, C_GREEN, dummy); +} + +void draw_rad_column(WINDOW *win, int x, double val) +{ + void *dummy = NULL; + int y = 0, end_y = 0, win_h = 0, win_w = 0; + int center_y = 0; + + getmaxyx(win, win_h, win_w); + (void)win_w; /* silence warnings */ + + center_y = win_h / 2; + end_y = (int)((double)(win_h / 2) * ((val / PI) + 1.0)); + + if (end_y > center_y) + { + for(y=center_y; y 0 ? history[index - 1] : 0; + + if (val > graph_limit) + val = graph_limit; + + history_temp[index] = val; + } + + fft_do(history_temp, history_fft_magn, history_fft_phase); + + for(index=1; index mx_mag) + { + mx_mag = history_fft_magn[index]; + highest = index; + } + } + + highest_freq = (hz / (double)max_x) * (double)highest; + + avg_freq_index /= total_val; + avg_freq = (hz / (double)max_x) * avg_freq_index; + + wattron(w_line1, A_REVERSE); + myprintloc(w_line1, 0, 38, gettext("highest: %6.2fHz, avg: %6.2fHz"), highest_freq, avg_freq); + wattroff(w_line1, A_REVERSE); + wnoutrefresh(w_line1); + + dummy = max_x / 2 + 1; + + if (draw_phase) + { + int y = 0; + + for(y=0; y ma) + continue; + + avg2 += val; + sd2 += val * val; + n2++; + } + + if (n2) + { + avg2 /= (double)n2; + sd2 = sqrt((sd2 / (double)n2) - pow(avg2, 2.0)); + + mi = max(mi, avg2 - sd2); + ma = min(ma, avg2 + sd2); + diff = ma - mi; + + if (diff == 0.0) + diff = 1.0; + + wattron(w_line1, A_REVERSE); + myprintloc(w_line1, 0, 0, gettext("graph range: %7.2fms - %7.2fms "), mi, ma); + wattroff(w_line1, A_REVERSE); + wnoutrefresh(w_line1); + + /* fprintf(stderr, "%d| %f %f %f %f\n", h_stats.n, mi, avg, ma, sd); */ + + for(index=0; index 1.0) + { + height = 1.0; + overflow = 1; + } + + i_h = (int)(height * stats_h); + /* fprintf(stderr, "%d %f %f %d %d\n", index, history[index], height, i_h, overflow); */ + + draw_column(w_stats, x, i_h, overflow, limitter); + } + } +} + +void show_stats_t(int y, int x, char *header, stats_t *data, char abbreviate) +{ + if (data -> valid) + { + char *cur_str = format_value(data -> cur, 6, 2, abbreviate); + char *min_str = format_value(data -> min, 6, 2, abbreviate); + char *avg_str = format_value(data -> avg / (double)data -> n, 6, 2, abbreviate); + char *max_str = format_value(data -> max, 6, 2, abbreviate); + char *sd_str = format_value(calc_sd(data), 6, 2, abbreviate); + + myprintloc(w_stats, y, x, "%s: %s %s %s %s %s", header, + data -> cur_valid ? cur_str : gettext(" n/a"), + min_str, avg_str, max_str, sd_str); + + free(sd_str); + free(max_str); + free(avg_str); + free(min_str); + free(cur_str); + } + else + { + myprintloc(w_stats, y, x, gettext("%s: n/a"), header); + } +} + +void update_stats(stats_t *resolve, stats_t *connect, stats_t *request, stats_t *total, stats_t *ssl_setup, int n_ok, int n_fail, const char *last_connect_str, const char *fp, char use_tfo, char dg, stats_t *st_to, stats_t *tcp_rtt_stats, int re_tx, int pmtu, int tos, stats_t *close_st, stats_t *t_write, int n_cookies, char abbreviate, stats_t *stats_header_size) +{ + double k = 0.0; + char force_redraw = 0; + struct pollfd p = { 0, POLLIN, 0 }; + + werase(w_stats); + + if (n_ok) + { + char buffer[4096] = { 0 }, *scc_str = NULL, *kalman_str = NULL; + int buflen = 0; + + myprintloc(w_stats, 0, 0, " %6s %6s %6s %6s %6s", gettext("latest"), gettext("min"), gettext("avg"), gettext("max"), gettext("sd")); + show_stats_t(1, 0, gettext("resolve"), resolve, abbreviate); + show_stats_t(2, 0, gettext("connect"), connect, abbreviate); + show_stats_t(3, 0, gettext("ssl "), ssl_setup, abbreviate); + show_stats_t(4, 0, gettext("send "), t_write, abbreviate); + show_stats_t(5, 0, gettext("request"), request, abbreviate); + show_stats_t(6, 0, gettext("close "), close_st, abbreviate); + show_stats_t(7, 0, gettext("total "), total, abbreviate); + + scc_str = format_value(get_cur_scc(), 5, 3, abbreviate); + kalman_str = format_value(kalman_do(total -> cur), 5, 3, abbreviate); + myprintloc(w_stats, 8, 0, gettext("ok: %3d, fail: %3d%s, scc: %s, kalman: %s"), n_ok, n_fail, use_tfo ? gettext(", with TFO") : "", scc_str, kalman_str); + free(kalman_str); + free(scc_str); + + if (max_x >= 44 * 2 + 1) + { + double trend = calc_trend(); + char trend_dir = ' '; + + myprintloc(w_stats, 0, 45, " %6s %6s %6s %6s %6s", gettext("cur"), gettext("min"), gettext("avg"), gettext("max"), gettext("sd")); + show_stats_t(1, 45, gettext("t offst"), st_to, abbreviate); + +#if defined(linux) || defined(__FreeBSD__) + show_stats_t(2, 45, gettext("tcp rtt"), tcp_rtt_stats, abbreviate); +#endif + show_stats_t(3, 45, gettext("headers"), stats_header_size, abbreviate); + + if (trend < 0) + trend_dir = '-'; + else if (trend > 0) + trend_dir = '+'; + + myprintloc(w_stats, 8, 48, gettext("# cookies: %d"), n_cookies); + +#ifdef linux + myprintloc(w_stats, 9, 48, gettext("trend: %c%6.2f%%, re-tx: %2d, pmtu: %5d, TOS: %02x"), trend_dir, fabs(trend), re_tx, pmtu, tos); +#else + myprintloc(w_stats, 9, 48, gettext("trend: %c%6.2f%%, TOS: %02x"), trend_dir, fabs(trend), tos); +#endif + } + + buflen = snprintf(buffer, sizeof buffer, gettext("HTTP rc: %s, SSL fp: %s"), last_connect_str, fp ? fp : gettext("n/a")); + + if (buflen <= max_x) + myprintloc(w_stats, 9, 0, "%s", buffer); + else + { + static char prev_sf[48] = { 0 }; + + myprintloc(w_stats, 9, 0, gettext("http result code: %s"), last_connect_str); + + if (fp && strcmp(prev_sf, fp)) + { + slow_log(gettext("\nSSL fingerprint: %s"), fp); + + memcpy(prev_sf, fp, 47); + } + } + } + + memmove(&history[1], &history[0], (history_n - 1) * sizeof(double)); + memmove(&history_set[1], &history_set[0], (history_n - 1) * sizeof(char)); + + history[0]= total -> cur; + history_set[0] = 1; + + if (poll(&p, 1, 0) == 1 && p.revents == POLLIN) + { + int c = getch(); + + if (c == 12) /* ^L */ + force_redraw = 1; + + if (c == 'H') + pause_graphs = !pause_graphs; + + if (c == 'q') + stop = 1; + } + + if (dg && !pause_graphs) + { + draw_graph(k); +#ifdef FW + draw_fft(); +#endif + } + + wnoutrefresh(w_stats); + + if (win_resize || force_redraw) + recreate_terminal(); +} diff --git a/nc.h b/nc.h new file mode 100644 index 0000000..d6a2808 --- /dev/null +++ b/nc.h @@ -0,0 +1,14 @@ +/* $Revision$ */ +#include + +extern char win_resize; +extern char draw_phase; + +void init_ncurses_ui(double graph_limit_in, double hz_in, char use_colors); +void end_ncurses(void); +void fast_log(const char *fmt, ...); +void slow_log(const char *fmt, ...); +void my_beep(void); +void update_terminal(void); +void status_line(char *fmt, ...); +void update_stats(stats_t *resolve, stats_t *connect, stats_t *request, stats_t *total, stats_t *ssl_setup, int n_ok, int n_fail, const char *last_connect_str, const char *fp, char use_tfo, char dg, stats_t *st_to, stats_t *tcp_rtt_stats, int re_tx, int pmtu, int tos, stats_t *close_st, stats_t *t_write, int n_cookies, char abbreviate, stats_t *stats_header_size); diff --git a/nl.po b/nl.po new file mode 100644 index 0000000..98cca31 --- /dev/null +++ b/nl.po @@ -0,0 +1,1229 @@ +# Dutch translations for HTTPing package. +# Copyright (C) 2013 folkert van heusden +# This file is distributed under the same license as the HTTPing package. +# folkert van heusden , 2013. +# Translated by Joris Zwart - http://joriszwart.nl/ +# +msgid "" +msgstr "" +"Project-Id-Version: HTTPing\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-05-04 15:29+0200\n" +"PO-Revision-Date: 2013-05-04 18:41+0100\n" +"Last-Translator: Joris Zwart \n" +"Language-Team: Dutch\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Poedit-Language: Dutch\n" +"X-Poedit-Country: NETHERLANDS\n" + +#: error.c:23 +#, c-format +msgid "" +"\n" +"\n" +"errno=%d which means %s (if applicable)\n" +msgstr "" +"\n" +"\n" +"errno=%d en dat betekent: %s (indien van toepassing)\n" + +#: error.c:39 +#, c-format +msgid "Error message '%s' truncated" +msgstr "De foutmelding '%s' past niet in de buffer." + +#: fft.c:21 +msgid "failed allocating memory for fft (1)" +msgstr "Geheugentoewijzingsprobleem voor 'fft (1)'." + +#: fft.c:25 +msgid "failed allocating memory for fft (2)" +msgstr "Geheugentoewijzingsprobleem voor 'fft (2)'." + +#: fft.c:30 +msgid "failed calculating plan for fft" +msgstr "Probleem bij het voorbereiden van de FFT berekening." + +#: help.c:77 +#, c-format +msgid "HTTPing v" +msgstr "HTTPing versie " + +msgid "adds an extra request-header" +msgstr "voeg een extra verzoek-regel toe" + +#: help.c:79 +#, c-format +msgid " * SSL support included (-l)\n" +msgstr " * Ondersteuning voor SSL (-l)\n" + +#: help.c:84 +#, c-format +msgid " * ncurses interface with FFT included (-K)\n" +msgstr " * ncurses gebruikersinterface met fourier transformatie berekening (-K)\n" + +#: help.c:86 +#, c-format +msgid " * ncurses interface included (-K)\n" +msgstr " * ncurses gebruikersinterface (-K)\n" + +#: help.c:91 +#, c-format +msgid " * TFO (TCP fast open) support included (-F)\n" +msgstr " * Ondersteuning voor 'TFO (TCP fast open)' (-F)\n" + +#: help.c:93 +#: help.c:293 +#, c-format +msgid "\n" +msgstr "\n" + +#: help.c:176 +#, c-format +msgid " *** where to connect to ***\n" +msgstr " *** adresspecificaties ***\n" + +#: help.c:177 +msgid "URL to ping (e.g. -g http://localhost/)" +msgstr "URL om te pingen (bijvoorbeeld -g http://localhost/)" + +#: help.c:178 +msgid "hostname to ping (e.g. localhost) - use either -g or -h" +msgstr "Systeemnaam om te pingen (bijvoorbeeld localhost). -g en -h kunnen niet gecombineerd worden" + +#: help.c:179 +msgid "portnumber (e.g. 80) - use with -h" +msgstr "poortnummer (bijv. 80), voor gebruik in combinatie met -h" + +#: help.c:180 +msgid "use IPv6 when resolving/connecting" +msgstr "gebruik IPv6 bij het omzetten van een systeemnaam en bij het maken van een verbinding" + +#: help.c:182 +msgid "connect using SSL. pinging an https URL automatically enables this setting" +msgstr "gebruik SSL encryptie. Wanneer men een https-URL kiest, dan zal deze optie automatisch ingeschakeld worden." + +#: help.c:187 +#, c-format +msgid " *** proxy settings ***\n" +msgstr " *** proxy instellingen ***\n" + +#: help.c:188 +msgid "x should be \"host:port\" which are the network settings of the http/https proxy server. ipv6 ip-address should be \"[ip:address]:port\"" +msgstr "x moet zijn \"host:port\", dat zijn de parameters van de http/https proxyserver. ipv6 ip-adressen moeten opgebouwd zijn als volgt: \"[ip:adres]:port\"" + +#: help.c:189 +msgid "fetch proxy settings from environment variables" +msgstr "haal de proxy-instellingen uit de omgevingsvariabelen" + +#: help.c:190 +msgid "username for authentication against proxy" +msgstr "gebruikersnaam voor proxy-authenticatie" + +#: help.c:191 +msgid "password for authentication against proxy" +msgstr "toegangscode voor proxy-authenticatie" + +#: help.c:192 +msgid "read password for proxy authentication from file x" +msgstr "lees toegangscode voor proxy-authenticatie uit bestand x" + +#: help.c:193 +msgid "proxy is a socks5 server" +msgstr "de proxy is een socks5 systeem" + +#: help.c:194 +msgid "adds \"&x=[random value]\" to the request URL" +msgstr "voeg een \"&x=[willekeurig getal]\" aan de geselecteerde URL" + +#: help.c:198 +#, c-format +msgid " *** timing settings ***\n" +msgstr " *** tijdsinstellingen ***\n" + +#: help.c:199 +msgid "how many times to ping" +msgstr "hoe vaak te pingen" + +#: help.c:200 +msgid "delay between each ping" +msgstr "pauze tussen iedere ping" + +#: help.c:201 +msgid "timeout (default: 30s)" +msgstr "hoelang te wachten op een server die niet reageert (standaard waarde: 30 seconden)" + +#: help.c:202 +msgid "execute pings at multiples of interval relative to start, automatically enabled in ncurses output mode" +msgstr "zorg ervoor dat pings steeds met dezelfde interval uitgevoerd worden, relatief tot het start tijdstip. deze instelling wordt automatisch aangezet in \"ncurses\"-mode" + +#: help.c:203 +msgid "flood connect (no delays)" +msgstr "geen vertraging tussen iedere ping" + +#: help.c:207 +#, c-format +msgid " *** HTTP settings ***\n" +msgstr " *** HTTP-instellingen ***\n" + +#: help.c:208 +msgid "ask any proxies on the way not to cache the requests" +msgstr "vraag aan de proxy-sysstemen in de route tussen het lokale systeem en het bestemmingssysteem om de verzoeken niet te cachen" + +#: help.c:209 +msgid "connect to a different host than in the URL given" +msgstr "verbind en zend de URL-verzoeken naar een ander systeem dan wat in de URL is opgegeven" + +#: help.c:210 +msgid "return the cookies given by the HTTP server in the following request(s)" +msgstr "als de HTTP-server cookies geeft, zend die dan mee terug bij volgende pings" + +#: help.c:211 +msgid "do not add \"Host:\"-line to the request headers" +msgstr "voeg geen \"Host:\"-regel in het verbindingsverzoek" + +#: help.c:212 +msgid "use a persistent connection. adds a 'C' to the output if httping had to reconnect" +msgstr "gebruik een blijvende verbinding. als het bestemmingssysteem de verbinding verbroken heeft, dan zal HTTPing een \"C\" toevoegen aan de uitvoer" + +#: help.c:213 +msgid "use 'x' for the UserAgent header" +msgstr "gebruik 'x' in de UserAgent verzoekinstellingen" + +#: help.c:214 +msgid "use 'x' for the Referer header" +msgstr "gebruik 'x' in de Referer verzoekinstellingen" + +#: help.c:218 +#, c-format +msgid " *** networking settings ***\n" +msgstr " *** netwerk instellingen ***\n" + +#: help.c:219 +msgid "limit the MTU size" +msgstr "beperkt de MTU omvang" + +#: help.c:220 +msgid "do not disable Naggle" +msgstr "zet het Naggle-algorithme niet uit" + +#: help.c:221 +msgid "receive buffer size" +msgstr "ontvangst buffer omvang" + +#: help.c:222 +msgid "transmit buffer size" +msgstr "verzend buffer omvang" + +#: help.c:223 +msgid "resolve hostname only once (useful when pinging roundrobin DNS: also takes the first DNS lookup out of the loop so that the first measurement is also correct)" +msgstr "eenmalig systeemnaam vertalen (dit is zinvol bij het pingen van een roterende DNS, bovendien haalt het de eerste vertaling uit de ping lus zodat de 1e ping niet een bovengemiddelde tijd duurt)" + +#: help.c:224 +msgid "do not abort the program if resolving failed: keep retrying" +msgstr "breek het programma niet af als de systeemnaamvertaling mislukt is" + +#: help.c:225 +msgid "bind to an ip-address (and thus interface) with an optional port" +msgstr "stuur verzoeken via een specifieke netwerk adapter, eventueel zelfs vanaf een specifieke port" + +#: help.c:227 +msgid "\"TCP fast open\" (TFO), reduces the latency of TCP connects" +msgstr "\"TCP fast open\" (TFO), dit verlaagt de latency van opvolgende TCP verbindingen" + +#: help.c:229 +msgid "set priority of packets" +msgstr "zet prioriteit van pakketten" + +#: help.c:230 +msgid "set TOS (type of service)" +msgstr "zet de TOS (diensttype)" + +#: help.c:234 +#, c-format +msgid " *** HTTP authentication ***\n" +msgstr " *** HTTP authenticatie ***\n" + +#: help.c:235 +msgid "activate (\"basic\") authentication" +msgstr "activeer (\"basic\")-authenticatie" + +#: help.c:236 +msgid "username for authentication" +msgstr "gebruikersnaam voor authenticatie" + +#: help.c:237 +msgid "password for authentication" +msgstr "toegangscode voor authenticatie" + +#: help.c:238 +msgid "read the password fom the file 'x' (replacement for -P)" +msgstr "lees de toegangscode uit bestand 'x'" + +#: help.c:242 +#, c-format +msgid " *** output settings ***\n" +msgstr " *** uitvoerinstellingen ***\n" + +#: help.c:243 +msgid "show statuscodes" +msgstr "toon statuscodes" + +#: help.c:244 +msgid "split measured time in its individual components (resolve, connect, send, etc." +msgstr "toon alle individuele componenten (verbinden, zenden, etc) van de gemeten tijden" + +#: help.c:245 +msgid "from what ping value to show the value in red (must be bigger than yellow), only in color mode (-Y)" +msgstr "toon de waardes in rood vanaf welke gemeten tijden" + +#: help.c:246 +msgid "from what ping value to show the value in yellow" +msgstr "toon de waardes in 't geel vanaf welke gemeten tijden" + +#: help.c:247 +msgid "from what ping value to show the results" +msgstr "filter alle metingen die beneden deze waarde vallen" + +#: help.c:248 +msgid "put a timestamp before the measured values, use -v to include the date and -vv to show in microseconds" +msgstr "toon een tijdstempel voor iedere gemeten waarde. gebruik -v om ook de datum te zien en -vv om ook microseconden te zien" + +#: help.c:249 +msgid "show an aggregate each x[/y[/z[/etc]]] seconds" +msgstr "toon cumulatief de waardes van x[/y[/etc]] seconden" + +#: help.c:251 +msgid "show fingerprint (SSL)" +msgstr "toon een vingerafdruk voor de SSL-verbinding" + +#: help.c:253 +msgid "verbose mode" +msgstr "toon meer details" + +#: help.c:257 +#, c-format +msgid " *** \"GET\" (instead of HTTP \"HEAD\") settings ***\n" +msgstr " *** \"GET\" (in plaats van HTTP \"HEAD\") instellingen ***\n" + +#: help.c:258 +msgid "do a GET request instead of HEAD (read the contents of the page as well)" +msgstr "doe een GET verzoek in plaats van een HEAD verzoek en haal ook de inhoud van een pagina op" + +#: help.c:259 +msgid "show transfer speed in KB/s (use with -G)" +msgstr "toon de verzendsnelheid in KB/s (gebruik in combinatie met -G)" + +#: help.c:260 +msgid "like -b but use compression if available" +msgstr "zoals -b maar gebruik compressie als het HTTP systeem dit ondersteunt" + +#: help.c:261 +msgid "limit the amount of data transferred (for -b) to 'x' (in bytes)" +msgstr "limiteer de hoeveelheid verzonden data (ism -b) tot 'x' bytes" + +#: help.c:262 +msgid "show the number of KB transferred (for -b)" +msgstr "toon de totaal verzonden hoeveelheid data (in KB, ism -b)" + +#: help.c:266 +#, c-format +msgid " *** output mode settings ***\n" +msgstr " *** uitvoer modeinstellingen ***\n" + +#: help.c:267 +msgid "quiet, only returncode" +msgstr "toon niets, geef alleen een resultaatwaarde terug" + +#: help.c:268 +msgid "give machine parseable output (see also -o and -e)" +msgstr "toon resultaat in computervriendelijkformaat (zie ook -o en -e)" + +#: help.c:269 +msgid "json output, cannot be combined with -m" +msgstr "json uitvoer, kan niet met -m gecombineerd worden" + +#: help.c:270 +msgid "what http results codes indicate 'ok' comma seperated WITHOUT spaces inbetween default is 200, use with -e" +msgstr "welke HTTP statuscodes als 'ok' te beschouwen. dit moet een met komma gescheiden lijst (geen spaties) zijn. de standaard waarde is 200. gebruik deze instelling ism -e" + +#: help.c:271 +msgid "string to display when http result code doesn't match" +msgstr "wat te laten zien als de HTTP statuscodes niet overeenkomen" + +#: help.c:272 +msgid "Nagios-mode: return 1 when avg. response time >= warn, 2 if >= crit, otherwhise return 0" +msgstr "Nagios-mode 1: geef 1 terug als de gemiddelde reactiesnelheid >= \"warn\" en 2 als die snelheid >= \"crit\", anders geef 0 terug" + +#: help.c:273 +msgid "Nagios mode 2: return 0 when all fine, 'x' when anything failes" +msgstr "Nagios mode 2: geef 0 terug als alles goed ging, anders 'x'" + +#: help.c:274 +msgid "add a cookie to the request" +msgstr "voeg een cookie toe aan het HTTP-verzoek" + +#: help.c:275 +msgid "add colors" +msgstr "gebruik kleuren" + +#: help.c:276 +msgid "audible ping" +msgstr "hoorbare piep" + +#: help.c:281 +#, c-format +msgid " *** GUI/ncurses mode settings ***\n" +msgstr " *** grafische interface/ncurses mode instellingen ***\n" + +#: help.c:282 +msgid "ncurses/GUI mode" +msgstr "ncurses/GUI mode" + +#: help.c:284 +msgid "draw phase (fourier transform) in gui" +msgstr "toon fasediagram" + +#: help.c:286 +msgid "when the duration is x or more, show ping line in the slow log window (the middle window)" +msgstr "als de tijdmetingen groter zijn dan x, toon dan het resultaat in het middelste venster" + +#: help.c:287 +msgid "do not scale to values above x" +msgstr "schaal niet naar waardes boven 'x'" + +#: help.c:288 +msgid "do not show graphs (in ncurses/GUI mode)" +msgstr "toon geen grafieken" + +#: help.c:292 +msgid "show the version" +msgstr "toon de versie van dit programma" + +#: help.c:305 +#, c-format +msgid "Voorbeeld:\n" +msgstr "" + +#: io.c:43 +#: io.c:78 +#, c-format +msgid "myread::select failed: %s" +msgstr "leesfout: %s" + +#: io.c:94 +#, c-format +msgid "myread::read failed: %s" +msgstr "leesfout: %s" + +#: io.c:138 +#, c-format +msgid "mywrite::select failed: %s" +msgstr "selecteer fout: %s" + +#: io.c:152 +#, c-format +msgid "mywrite::write failed: %s" +msgstr "schrijffout: %s" + +#: io.c:174 +#, c-format +msgid "set_fd_nonblocking failed! (%s)\n" +msgstr "\"set_fd_nonblocking\" fout: %s\n" + +#: io.c:187 +#, c-format +msgid "set_fd_blocking failed! (%s)\n" +msgstr "\"set_fd_blocking\" fout: %s\n" + +#: main.c:109 +#: main.c:108 +#, c-format +msgid "%s, run time: %.3fs, press ctrl + c to stop" +msgstr "%s, loop tijd: %.3fs, druk op ctrl + c om het programma te stoppen" + +#: main.c:244 +#: main.c:243 +#, c-format +msgid "Got signal %d\n" +msgstr "Signaal %d\n" + +#: main.c:255 +#: main.c:254 +#, c-format +msgid "Cannot open password-file %s" +msgstr "Kan het toegangscodebestand '%s' niet openen" + +#: main.c:258 +#: main.c:257 +#, c-format +msgid "Problem reading password from file %s" +msgstr "Probleem bij het lezen van de toegangscode uit bestand '%s'" + +#: main.c:365 +#: main.c:364 +#, c-format +msgid "URL too big, HTTPing has a %d bytes limit" +msgstr "URL is te groot, HTTPing heeft daar een limiet van %d bytes op" + +#: main.c:384 +#: main.c:383 +msgid "using \"http://\" with SSL enabled (-l)" +msgstr "\"http://\" gebruikt met SSL ingeschakeld (-l)" + +#: main.c:519 +#: main.c:518 +#, c-format +msgid "AGG[%d]: %d values, min/avg/max%s = %.1f/%.1f/%.1f" +msgstr "CUM[%d]: %d waardes, min/gem/max%s = %.1f/%.1f/%.1f" + +#: main.c:519 +#: main.c:2294 +#: main.c:518 +#: main.c:2289 +msgid "/sd" +msgstr "/sd" + +#: main.c:576 +#: main.c:575 +msgid "-n: missing parameter\n" +msgstr "-n: ontbrekende parameter\n" + +#: main.c:594 +#: main.c:603 +#: main.c:593 +#: main.c:602 +#, c-format +msgid "cannot convert ip address '%s' (for -y)\n" +msgstr "kan het IP-adres '%s' niet vertalen (-y)\n" + +#: main.c:710 +#: main.c:709 +#, c-format +msgid "CRITICAL - connecting failed: %s" +msgstr "KRITIEK - verbinding mislukt: %s" + +#: main.c:715 +#: main.c:714 +#, c-format +msgid "CRITICAL - average httping-time is %.1f\n" +msgstr "KRITIEK - gemiddelde HTTPing tijd is %.1f\n" + +#: main.c:720 +#: main.c:719 +#, c-format +msgid "WARNING - average httping-time is %.1f\n" +msgstr "WAARSCHUWING - gemiddelde HTTPing tijd is %.1f\n" + +#: main.c:724 +#: main.c:723 +#, c-format +msgid "OK - average httping-time is %.1f (%s)|ping=%f\n" +msgstr "OK - gemiddelde HTTPing-tijd is %.1f (%s)|ping=%f\n" + +#: main.c:734 +#: main.c:733 +#, c-format +msgid "OK - all fine, avg httping time is %.1f|ping=%f\n" +msgstr "OK - gemiddelde HTTPing-tijd is %.1f|ping=%f\n" + +#: main.c:738 +#: main.c:737 +#, c-format +msgid "%s: - failed: %s" +msgstr "%s: - mislukt: %s" + +#: main.c:1025 +#: main.c:1020 +#, c-format +msgid "" +"\n" +" *** -A is no longer required ***\n" +"\n" +msgstr "" +"\n" +" *** -A is niet langer nodig ***\n" +"\n" + +#: main.c:1170 +#: main.c:1165 +msgid "-i cannot have a value smaller than zero" +msgstr "-i waarde kan niet kleiner dan 0 zijn" + +#: main.c:1224 +#: main.c:1232 +#: main.c:1219 +#: main.c:1227 +msgid "-n and -N are mutual exclusive\n" +msgstr "-n en -N zijn wederzijds uitsluitende\n" + +#: main.c:1253 +#: main.c:1248 +#, c-format +msgid "Warning: TCP TFO is not supported. Disabling.\n" +msgstr "Let op: TCP TFO is niet ondersteund.\n" + +#: main.c:1269 +#: main.c:1264 +#, c-format +msgid "" +"\n" +"\n" +"Please run:\n" +"\t%s --help\n" +"to see a list of options.\n" +"\n" +msgstr "" +"\n" +"\n" +"Start:\n" +"\t%s --help\n" +"om een lijst van opties te zien.\n" +"\n" + +#: main.c:1281 +#, c-format +msgid "" +"No URL/host to ping given\n" +"\n" +msgstr "" +"Geen URL of systeemnaam gespecificeerd om te pingen\n" +"\n" + +#: main.c:1291 +#: main.c:1286 +msgid "Cannot combine -m, -M and -K" +msgstr "-m, -M en -K kunnen niet gecombineerd worden" + +#: main.c:1294 +#: main.c:1289 +msgid "Aggregates can only be used in non-machine/json-output mode" +msgstr "Cumulatieven kunnen niet in machine-uitvoer/json-uitvoer modi gebruikt worden" + +#: main.c:1299 +#: main.c:1294 +msgid "-b/-B can only be used when also using -G (GET instead of HEAD) or -l (use SSL)\n" +msgstr "-b/-B kunnen alleen gebruikt worden ism -G or -l\n" + +#: main.c:1302 +#: main.c:1297 +msgid "TCP Fast open and SSL not supported together\n" +msgstr "\"TCP Fast open\" en SSL kunnen niet gecombineerd worden\n" + +#: main.c:1332 +#: main.c:1327 +msgid "" +"\n" +"Auto enabling SSL due to https-URL" +msgstr "" +"\n" +"SSL ingeschakeld vanwege https-URL" + +#: main.c:1338 +#: main.c:1333 +#, c-format +msgid "Auto enabling SSL due to https-URL" +msgstr "SSL ingeschakeld vanwege https-URL" + +#: main.c:1347 +#: main.c:1342 +#, c-format +msgid "" +"\n" +"Connecting to host %s, port %d and requesting file %s" +msgstr "" +"\n" +"Verbinden met %s:%d en opvragen van bestand %s" + +#: main.c:1350 +#: main.c:1345 +#, c-format +msgid "" +"\n" +"Using proxyserver: %s:%d" +msgstr "" +"\n" +"Proxy %s:%d wordt gebruikt" + +#: main.c:1355 +#: main.c:1350 +#, c-format +msgid "" +"Connecting to host %s, port %d and requesting file %s\n" +"\n" +msgstr "" +"Verbinden met %s:%d en opvragen van bestand %s\n" +"\n" + +#: main.c:1358 +#: main.c:1353 +#, c-format +msgid "Using proxyserver: %s:%d\n" +msgstr "Proxy: %s:%d\n" + +#: main.c:1369 +#: main.c:1364 +msgid "problem creating SSL context" +msgstr "Probleem bij aanmaken SSL-context" + +#: main.c:1389 +#: main.c:1384 +msgid "Interval must be > 0 when using adaptive interval" +msgstr "Interval moet groter dan 0 zijn bij adaptieve interval" + +#: main.c:1417 +#: main.c:1435 +#: main.c:1519 +#: main.c:1412 +#: main.c:1430 +#: main.c:1514 +#, c-format +msgid "" +"\n" +"Resolving hostname %s" +msgstr "" +"\n" +"Vertalen systeemnaam '%s'" + +#: main.c:1427 +#: main.c:1452 +#: main.c:1545 +#: main.c:1422 +#: main.c:1447 +#: main.c:1540 +#, c-format +msgid "No valid IPv4 or IPv6 address found for %s" +msgstr "Geen geldig IPv4 of IPv6 adres gevonden voor '%s'" + +#: main.c:1579 +#: main.c:1574 +msgid "Will no longer inform about request headers too large." +msgstr "Zal niet langer meldingen tonen over te grote verzoeken." + +#: main.c:1581 +#: main.c:1576 +#, c-format +msgid "Request headers > 4KB! (%d bytes) This may give failures with some HTTP servers." +msgstr "Verzoek is groter dan 4KB! (%d bytes) Dit kan problemen opleveren met sommige HTTP-servers." + +#: main.c:1685 +#: main.c:1680 +msgid "timeout connecting to host" +msgstr "systeem reageerde niet op tijd" + +#: main.c:1748 +#: main.c:1743 +msgid "error sending request to host" +msgstr "probleem bij verzenden verzoek naar systeem" + +#: main.c:1750 +#: main.c:1800 +#: main.c:1745 +#: main.c:1795 +msgid "timeout sending to host" +msgstr "systeem reageerde niet op tijd bij verzenden verzoek" + +#: main.c:1752 +#: main.c:1747 +msgid "retrieved invalid data from host" +msgstr "ongeldige data ontvangen van systeem" + +#: main.c:1756 +#: main.c:1751 +msgid "connection prematurely closed by peer" +msgstr "verbinding voortijdig gesloten door systeem" + +#: main.c:1790 +#: main.c:1785 +msgid "" +"\n" +"No longer emitting message about \"still data in transit\"" +msgstr "" +"\n" +"Er zullen geen meldingen meer getoond worden over data die nog getransporteerd wordt." + +#: main.c:1792 +#: main.c:1787 +#, c-format +msgid "" +"\n" +"HTTP server started sending data with %d bytes still in transit" +msgstr "" +"\n" +"HTTP server begon al een reaktie te versturen terwijl er nog %d bytes onderweg waren" + +#: main.c:1813 +#: main.c:1808 +msgid "failed to obtain TOS info" +msgstr "probleem bij ophalen TOS-informatie" + +#: main.c:1827 +#: main.c:1822 +msgid "" +"\n" +"No longer emitting message about \"more data than response headers\"" +msgstr "" +"\n" +"De melding \"HTTP server verzond meer data dan alleen de antwoord metadata\" zal niet meer getoond worden." + +#: main.c:1829 +#: main.c:1824 +msgid "" +"\n" +"HTTP server sent more data than just the response headers" +msgstr "" +"\n" +"HTTP server verzond meer data dan alleen de antwoord metadata" + +#: main.c:1878 +#: main.c:1873 +msgid "'Content-Length'-header missing!" +msgstr "'Content-Length' metadata ontbreekt" + +#: main.c:1910 +#: main.c:1905 +msgid "short read during receiving reply-headers from host" +msgstr "ontvangprobleem bij metadata van systeem" + +#: main.c:1912 +#: main.c:1907 +msgid "timeout while receiving reply-headers from host" +msgstr "geen reactie bij ontvangen metadata van systeem" + +#: main.c:1945 +#: main.c:1940 +msgid "read of response body dataa failed" +msgstr "lezen van reactiedata mislukt" + +#: main.c:1980 +#: main.c:1975 +msgid "error shutting down ssl" +msgstr "probleem bij stoppen SSL-verbinding" + +#: main.c:2026 +#: main.c:2087 +#: main.c:2021 +#: main.c:2082 +#, c-format +msgid "getnameinfo() failed: %d (%s)" +msgstr "getnameinfo() faalde: %d (%s)" + +#: main.c:2061 +#: main.c:2056 +msgid "connected to" +msgstr "verbinden met" + +#: main.c:2061 +#: main.c:2056 +msgid "pinged host" +msgstr "gepingde systeem" + +#: main.c:2105 +#: main.c:2100 +#, c-format +msgid "%s%s%s%s%s:%s%d%s (%d/%d bytes), seq=%s%d%s " +msgstr "%s%s%s%s%s:%s%d%s (%d/%d bytes), volgnr=%s%d%s " + +#: main.c:2107 +#: main.c:2102 +#, c-format +msgid "%s%s%s%s%s:%s%d%s (%d bytes), seq=%s%d%s " +msgstr "%s%s%s%s%s:%s%d%s (%d bytes), volgnr=%s%d%s " + +#: main.c:2113 +#: main.c:2114 +#: nc.c:721 +#: main.c:2108 +#: main.c:2109 +msgid " n/a" +msgstr " ---" + +#: main.c:2119 +#: main.c:2114 +#, c-format +msgid "time=%s+%s+%s+%s+%s%s=%s%s%s%s ms %s%s%s" +msgstr "tijd=%s+%s+%s+%s+%s%s=%s%s%s%s ms %s%s%s" + +#: main.c:2129 +#: main.c:2124 +#, c-format +msgid "time=%s%s%s ms %s%s%s" +msgstr "tijd=%s%s%s ms %s%s%s" + +#: main.c:2149 +#: main.c:2144 +msgid "not " +msgstr "niet " + +#: main.c:2150 +#: main.c:2145 +msgid "compressed)" +msgstr "gecomprimeerd)" + +#: main.c:2162 +#: main.c:2157 +#, c-format +msgid " toff=%d" +msgstr " tijdoffset=%d" + +#: main.c:2166 +#: main.c:2161 +#, c-format +msgid " age=%d" +msgstr " leeftijd=%d" + +#: main.c:2282 +#: main.c:2277 +#, c-format +msgid "--- %s ping statistics ---\n" +msgstr "--- %s ping statistieken ---\n" + +#: main.c:2285 +#: main.c:2280 +#, c-format +msgid "internal error! (curncount)\n" +msgstr "interne fout! (curncount)\n" + +#: main.c:2290 +#: main.c:2285 +#, c-format +msgid "%s%d%s connects, %s%d%s ok, %s%3.2f%%%s failed, time %s%s%.0fms%s\n" +msgstr "%s%d%s verbindingen, %s%d%s ok, %s%3.2f%%%s mislukt, tijd %s%s%.0fms%s\n" + +#: main.c:2294 +#: main.c:2289 +#, c-format +msgid "round-trip min/avg/max%s = %s%.1f%s/%s%.1f%s/%s%.1f%s" +msgstr "retour min/gem/max%s = %s%.1f%s/%s%.1f%s/%s%.1f%s" + +#: main.c:2305 +#: main.c:2300 +#, c-format +msgid "Transfer speed: min/avg/max = %s%f%s/%s%f%s/%s%f%s KB\n" +msgstr "Verzendsnelheid: min/gem/max = %s%f%s/%s%f%s/%s%f%s KB\n" + +#: mssl.c:79 +#, c-format +msgid "READ_SSL: io-error: %s" +msgstr "SSL lezen, I/O fout: %s" + +#: mssl.c:110 +#, c-format +msgid "WRITE_SSL: io-error: %s" +msgstr "SSL schrijven, I/O fout: %s" + +#: mssl.c:141 +#, c-format +msgid "problem setting receive timeout (%s)" +msgstr "probleem bij instellen ontvangsttijdslimiet (%s)" + +#: mssl.c:147 +#, c-format +msgid "problem setting transmit timeout (%s)" +msgstr "probleem bij instellen verzendtijdslimiet (%s)" + +#: mssl.c:159 +#, c-format +msgid "problem starting SSL connection: %d" +msgstr "probleem bij starten SSL verbinding: %d" + +#: mssl.c:259 +msgid "Problem sending request to proxy" +msgstr "Probleem bij verzenden verzoek naar proxy" + +#: mssl.c:268 +msgid "Problem retrieving proxy response" +msgstr "Probleem in ontvangen proxyreactie" + +#: mssl.c:282 +msgid "Invalid proxy response headers" +msgstr "Ongeldige proxyreactie" + +#: mssl.c:289 +#, c-format +msgid "Proxy indicated error: %s" +msgstr "Proxy gaf een fout aan: %s" + +#: nc.c:57 +msgid "realloc issue" +msgstr "geheugenfout" + +#: nc.c:542 +#, c-format +msgid "highest: %6.2fHz, avg: %6.2fHz" +msgstr "hoogste: %6.2fHz, gem: %6.2fHz" + +#: nc.c:668 +#, c-format +msgid "graph range: %7.2fms - %7.2fms " +msgstr "grafiekbandbreedte: %7.2fms - %7.2fms " + +#: nc.c:732 +#, c-format +msgid "%s: n/a" +msgstr "%s: ---" + +#: nc.c:749 +msgid "latest" +msgstr "laatste" + +#: nc.c:749 +#: nc.c:769 +msgid "min" +msgstr "min" + +#: nc.c:749 +#: nc.c:769 +msgid "avg" +msgstr "gem" + +#: nc.c:749 +#: nc.c:769 +msgid "max" +msgstr "max" + +#: nc.c:749 +#: nc.c:769 +msgid "sd" +msgstr "sd" + +#: nc.c:750 +msgid "resolve" +msgstr "vertaal" + +#: nc.c:751 +msgid "connect" +msgstr "verbind" + +#: nc.c:752 +msgid "ssl " +msgstr "ssl " + +#: nc.c:753 +msgid "send " +msgstr "zend " + +#: nc.c:754 +msgid "request" +msgstr "verzoek" + +#: nc.c:755 +msgid "close " +msgstr "sluiten" + +#: nc.c:756 +msgid "total " +msgstr "totaal " + +#: nc.c:760 +#, c-format +msgid "ok: %3d, fail: %3d%s, scc: %s, kalman: %s" +msgstr "ok: %2d, mislukt: %2d%s, scc: %s, Kalman: %s" + +#: nc.c:760 +msgid ", with TFO" +msgstr ", met TFO" + +#: nc.c:769 +msgid "cur" +msgstr "nu" + +#: nc.c:770 +msgid "t offst" +msgstr "t offst" + +#: nc.c:773 +msgid "tcp rtt" +msgstr "TCP RTT" + +#: nc.c:775 +msgid "headers" +msgstr "meta " + +#: nc.c:782 +#, c-format +msgid "# cookies: %d" +msgstr "# cookies: %d" + +#: nc.c:784 +#, c-format +msgid "trend: %c%6.2f%%, re-tx: %2d, pmtu: %5d, TOS: %02x" +msgstr "trend: %c%6.2f%%, re-tx: %2d, pmtu: %5d, TOS: %02x" + +#: nc.c:787 +#, c-format +msgid "HTTP rc: %s, SSL fp: %s" +msgstr "HTTP rc: %s, SSL va: %s" + +#: nc.c:787 +msgid "n/a" +msgstr "---" + +#: nc.c:795 +#, c-format +msgid "http result code: %s" +msgstr "HTTP statuscode: %s" + +#: nc.c:799 +#, c-format +msgid "" +"\n" +"SSL fingerprint: %s" +msgstr "" +"\n" +"SSL vingerafdruk: %s" + +#: res.c:36 +#, c-format +msgid "Resolving %s %sfailed: %s" +msgstr "Vertalen %s %smislukt: %s" + +#: res.c:36 +msgid "(IPv6) " +msgstr "(IPv6) " + +#: res.c:71 +#, c-format +msgid "Problem resolving %s (IPv4): %s" +msgstr "Probleem vertalen %s (IPv4): %s" + +#: socks5.c:56 +#, c-format +msgid "socks5connect: reply with requested authentication method does not say version 5 (%02x)" +msgstr "socks5connect: reactie geeft niet versie 5 aan (%02x)" + +#: socks5.c:70 +#, c-format +msgid "socks5connect: socks5 refuses our authentication methods: %02x" +msgstr "socks5connect: socks5 systeem wijst onze authenticatie methode af: %02x" + +#: socks5.c:81 +msgid "socks5connect: socks5 server requests username/password authentication" +msgstr "socks5connect: systeem vraagt gebruikersnaamauthenticatie" + +#: socks5.c:90 +msgid "socks5connect: failed transmitting username/password to socks5 server" +msgstr "socks5connect: probleem bij verzenden gebrukersnaam/toegangscode naar systeem" + +#: socks5.c:96 +msgid "socks5connect: failed receiving authentication reply" +msgstr "socks5connect: authenticatie reactie ontvangen mislukt" + +#: socks5.c:102 +msgid "socks5connect: password authentication failed" +msgstr "socks5connect: authenticatie faalde" + +#: socks5.c:116 +#, c-format +msgid "Cannot resolve %s" +msgstr "Kan %s niet vertalen" + +#: socks5.c:133 +msgid "socks5connect: failed to transmit associate request" +msgstr "socks5connect: \"associatie\"-verzoek verzenden mislukt" + +#: socks5.c:139 +msgid "socks5connect: command reply receive failure" +msgstr "socks5connect: commando-reactie ontvangen mislukt" + +#: socks5.c:146 +#, c-format +msgid "socks5connect: bind request replies with version other than 0x05 (%02x)" +msgstr "socks5connect: verbindingsreactie geeft ander versienummer (%02x) dan 0x05 terug" + +#: socks5.c:152 +#, c-format +msgid "socks5connect: failed to connect (%02x)" +msgstr "socks5connect: verbinden mislukt (%02x)" + +#: socks5.c:158 +#, c-format +msgid "socks5connect: only accepting bind-replies with IPv4 address (%02x)" +msgstr "socks5connect: alleen \"bind\"-reakties voor IPv4 ondersteund (%02x)" + +#: tcp.c:31 +#: tcp.c:42 +#, c-format +msgid "could not set TCP_NODELAY on socket (%s)" +msgstr "kan TCP_NODELAY niet inschakelen op verbinding (%s)" + +#: tcp.c:57 +#, c-format +msgid "problem creating socket (%s)" +msgstr "probleem bij maken socket (%s)" + +#: tcp.c:69 +#, c-format +msgid "error setting sockopt to interface (%s)" +msgstr "sockopt zetten op adapter mislukt (%s)" + +#: tcp.c:75 +#, c-format +msgid "error binding to interface (%s)" +msgstr "probleem bij binden aan adapter (%s)" + +#: tcp.c:84 +#, c-format +msgid "error setting MTU size (%s)" +msgstr "probleem bij instellen MTU-grootte (%s)" + +#: tcp.c:101 +#, c-format +msgid "error setting transmit buffer size (%s)" +msgstr "probleem bij instellen verzendbuffergrootte (%s)" + +#: tcp.c:110 +#, c-format +msgid "error setting receive buffer size (%s)" +msgstr "probleem bij instellen ontvangstbuffergrootte (%s)" + +#: tcp.c:119 +#, c-format +msgid "error setting priority (%s)" +msgstr "probleem bij instellen prioriteit (%s)" + +#: tcp.c:128 +msgid "failed to set TOS info" +msgstr "probleem bij instellen TOS-info" + +#: tcp.c:165 +#, c-format +msgid "TCP TFO Not Supported. Please check if \"/proc/sys/net/ipv4/tcp_fastopen\" is 1. Disabling TFO for now.\n" +msgstr "TCP TFO is niet ondersteund. Controleer of \"/proc/sys/net/ipv4/tcp_fastopen\" de waarde 1 bevat.\n" + +#: tcp.c:195 +#, c-format +msgid "problem connecting to host: %s" +msgstr "probleem bij verbinden met systeem: %s" + +#: tcp.c:208 +msgid "connect time out" +msgstr "verbindingstimeout" + +#: tcp.c:216 +#, c-format +msgid "select() failed: %s" +msgstr "select() mislukt: %s" + +#: tcp.c:228 +#, c-format +msgid "getsockopt failed (%s)" +msgstr "getsockopt mislukt (%s)" + +#: tcp.c:240 +#, c-format +msgid "could not connect (%s)" +msgstr "kan niet verbinden (%s)" + +#: utils.c:25 +msgid "gettimeofday failed" +msgstr "gettimeofday mislukt" + +#~ #: main.c:1276 main.c:1271 + +#~ msgid "Cannot combine maximum MTU size setting with proxy connections or SSL" +#~ msgstr "" diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..792bce0 --- /dev/null +++ b/readme.txt @@ -0,0 +1,47 @@ +Installation: +------------ +make install + +This will first invoke the 'configure'-script and then build the sources. +You can also run 'configure' manually. Then the following switches apply: + --with-tfo force enable tcp fast open + --with-ncurses force enable ncurses + --with-openssl force enable openssl + --with-fftw3 force enable fftw3 + +Please note that TCP fast open requires a Linux kernel of version 3.7 or more recent. + +'fftw3' support is only useful if you include the ncurses interface. + +Debian package names: + ncurses requires libncursesw5-dev + fftw3 requires libfftw3-dev + openssl requires libssl-dev + + +Usage: +----- +httping www.vanheusden.com + + +See: + httping -h +for a list of commandline switches. Also check the man-page. + +plot-json.py is a script to convert the json-output of httping to a script for gnuplot. + +If this script fails with the following error: + ValueError: Expecting object: [...] +then make sure the json-file ends with a ']' (without the quotes). +In some cases this character is missing. + + +Thanks to Thanatos for cookie and authentication support. +Many thanks to Olaf van der Spek for lots of bug-reports, testing, ideas and suggestions. + + +For everything more or less related to 'httping', please feel free +to contact me on: folkert@vanheusden.com + +Please support my opensource development: http://www.vanheusden.com/wishlist.php +Or send any surplus bitcoins to 1N5Sn4jny4xVwTwSYLnf7WnFQEGoVRmTQF diff --git a/res.c b/res.c new file mode 100644 index 0000000..c96cbdc --- /dev/null +++ b/res.c @@ -0,0 +1,81 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gen.h" +#include "res.h" +#include "error.h" + +int resolve_host(const char *host, struct addrinfo **ai, char use_ipv6, int portnr) +{ + int rc = -1; + char servname[10]; + struct addrinfo myaddr; + + memset(&myaddr, 0, sizeof myaddr); + + /* myaddr.ai_flags = AI_PASSIVE; */ + myaddr.ai_socktype = SOCK_STREAM; + myaddr.ai_protocol = IPPROTO_TCP; + myaddr.ai_family = use_ipv6 ? AF_INET6 : AF_INET; + snprintf(servname, sizeof servname, "%d", portnr); + + rc = getaddrinfo(host, servname, &myaddr, ai); + + if (rc != 0) + set_error(gettext("Resolving %s %sfailed: %s"), host, use_ipv6 ? gettext("(IPv6) ") : "", gai_strerror(rc)); + + return rc; +} + +struct addrinfo * select_resolved_host(struct addrinfo *ai, char use_ipv6) +{ + struct addrinfo *p = ai; + while(p) + { + if (p -> ai_family == AF_INET6 && use_ipv6) + return p; + + if (p -> ai_family == AF_INET) + return p; + + p = p -> ai_next; + } + + return NULL; +} + +void get_addr(struct addrinfo *ai_use, struct sockaddr_in6 *addr) +{ + memcpy(addr, ai_use->ai_addr, ai_use->ai_addrlen); +} + +#define incopy(a) *((struct in_addr *)a) + +int resolve_host_ipv4(const char *host, struct sockaddr_in *addr) +{ + struct hostent *hostdnsentries = gethostbyname(host); + + if (hostdnsentries == NULL) + { + set_error(gettext("Problem resolving %s (IPv4): %s"), host, hstrerror(h_errno)); + + return -1; + } + + /* create address structure */ + addr -> sin_family = hostdnsentries -> h_addrtype; + addr -> sin_addr = incopy(hostdnsentries -> h_addr_list[0]); + + return 0; +} diff --git a/res.h b/res.h new file mode 100644 index 0000000..10c6436 --- /dev/null +++ b/res.h @@ -0,0 +1,10 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#define incopy(a) *((struct in_addr *)a) + +int resolve_host(const char *host, struct addrinfo **ai, char use_ipv6, int portnr); +struct addrinfo * select_resolved_host(struct addrinfo *ai, char use_ipv6); +void get_addr(struct addrinfo *ai_use, struct sockaddr_in6 *addr); + +int resolve_host_ipv4(const char *host, struct sockaddr_in *addr); diff --git a/socks5.c b/socks5.c new file mode 100644 index 0000000..652ffc1 --- /dev/null +++ b/socks5.c @@ -0,0 +1,163 @@ +/* $Revision$ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "gen.h" +#include "io.h" +#include "res.h" +#include "tcp.h" + +int socks5connect(int fd, struct addrinfo *ai, double timeout, const char *socks5_username, const char *socks5_password, const char *host, int port, char abort_on_resolve_failure) +{ + struct sockaddr_in sai; + uint32_t addr = 0; + unsigned char io_buffer[256] = { 0 }; + int io_len = 0, rc = -1; + + if ((rc = connect_to(fd, ai, timeout, NULL, NULL, 0, NULL)) < 0) + return rc; + + /* inform socks server about the auth. methods we support */ + if (socks5_username != NULL) + { + io_buffer[0] = 0x05; /* version */ + io_buffer[1] = 2; /* 2 authentication methods */ + io_buffer[2] = 0x00; /* method 1: no authentication */ + io_buffer[3] = 0x02; /* method 2: username/password */ + io_len = 4; + } + else + { + io_buffer[0] = 0x05; /* version */ + io_buffer[1] = 1; /* 2 authentication methods */ + io_buffer[2] = 0x00; /* method 1: no authentication */ + io_len = 3; + } + + if ((rc = mywrite(fd, (char *)io_buffer, io_len, timeout)) < 0) + return rc; + + /* wait for reply telling selected authentication method */ + if ((rc = myread(fd, (char *)io_buffer, 2, timeout)) < 0) + return rc; + + if (io_buffer[0] != 0x05) + { + set_error(gettext("socks5connect: reply with requested authentication method does not say version 5 (%02x)"), io_buffer[0]); + return RC_INVAL; + } + + if (io_buffer[1] == 0x00) + { + /* printf("socks5connect: \"no authentication at all\" selected by server\n"); */ + } + else if (io_buffer[1] == 0x02) + { + /* printf("socks5connect: selected username/password authentication\n"); */ + } + else + { + set_error(gettext("socks5connect: socks5 refuses our authentication methods: %02x"), io_buffer[1]); + return RC_INVAL; + } + + /* in case the socks5 server asks us to authenticate, do so */ + if (io_buffer[1] == 0x02) + { + int io_len = 0; + + if (socks5_username == NULL || socks5_password == NULL) + { + set_error(gettext("socks5connect: socks5 server requests username/password authentication")); + return RC_INVAL; + } + + io_buffer[0] = 0x01; /* version */ + io_len = snprintf((char *)&io_buffer[1], sizeof io_buffer - 1, "%c%s%c%s", (int)strlen(socks5_username), socks5_username, (int)strlen(socks5_password), socks5_password); + + if ((rc = mywrite(fd, (char *)io_buffer, io_len + 1, timeout)) < 0) + { + set_error(gettext("socks5connect: failed transmitting username/password to socks5 server")); + return rc; + } + + if ((rc = myread(fd, (char *)io_buffer, 2, timeout)) < 0) + { + set_error(gettext("socks5connect: failed receiving authentication reply")); + return rc; + } + + if (io_buffer[1] != 0x00) + { + set_error(gettext("socks5connect: password authentication failed")); + return RC_INVAL; + } + } + + /* ask socks5 server to associate with server */ + io_buffer[0] = 0x05; /* version */ + io_buffer[1] = 0x01; /* connect to */ + io_buffer[2] = 0x00; /* reserved */ + io_buffer[3] = 0x01; /* ipv4 */ + + if (resolve_host_ipv4(host, &sai) == -1) + { + if (abort_on_resolve_failure) + error_exit(gettext("Cannot resolve %s"), host); + + return RC_INVAL; + } + + addr = ntohl(sai.sin_addr.s_addr); + + io_buffer[4] = (addr >> 24) & 255; + io_buffer[5] = (addr >> 16) & 255; + io_buffer[6] = (addr >> 8) & 255; + io_buffer[7] = (addr ) & 255; + + io_buffer[8] = (port >> 8) & 255; + io_buffer[9] = (port ) & 255; + + if ((rc = mywrite(fd, (char *)io_buffer, 10, timeout)) < 0) + { + set_error(gettext("socks5connect: failed to transmit associate request")); + return rc; + } + + if ((rc = myread(fd, (char *)io_buffer, 10, timeout)) < 0) + { + set_error(gettext("socks5connect: command reply receive failure")); + return rc; + } + + /* verify reply */ + if (io_buffer[0] != 0x05) + { + set_error(gettext("socks5connect: bind request replies with version other than 0x05 (%02x)"), io_buffer[0]); + return RC_INVAL; + } + + if (io_buffer[1] != 0x00) + { + set_error(gettext("socks5connect: failed to connect (%02x)"), io_buffer[1]); + return RC_INVAL; + } + + if (io_buffer[3] != 0x01) + { + set_error(gettext("socks5connect: only accepting bind-replies with IPv4 address (%02x)"), io_buffer[3]); + return RC_INVAL; + } + + return RC_OK; +} diff --git a/socks5.h b/socks5.h new file mode 100644 index 0000000..7a5b436 --- /dev/null +++ b/socks5.h @@ -0,0 +1,2 @@ +/* $Revision$ */ +int socks5connect(int fd, struct addrinfo *ai, double timeout, const char *socks5_username, const char *socks5_password, const char *host, int port, char abort_on_resolve_failure); diff --git a/tcp.c b/tcp.c new file mode 100644 index 0000000..2870fa3 --- /dev/null +++ b/tcp.c @@ -0,0 +1,252 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "gen.h" +#include "io.h" +#include "main.h" +#include "tcp.h" + +void failure_close(int fd) +{ + struct linger sl; + + sl.l_onoff = 1; + sl.l_linger = 0; + + if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &sl, sizeof sl) == -1) + set_error(gettext("could not set TCP_NODELAY on socket (%s)"), strerror(errno)); + + close(fd); +} + +int set_no_delay(int fd) +{ + int flag = 1; + + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) + { + set_error(gettext("could not set TCP_NODELAY on socket (%s)"), strerror(errno)); + return -1; + } + + return 0; +} + +int create_socket(struct sockaddr *bind_to, struct addrinfo *ai, int recv_buffer_size, int tx_buffer_size, int max_mtu, char use_no_delay, int priority, int tos) +{ + int fd = -1; + + /* create socket */ + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) + { + set_error(gettext("problem creating socket (%s)"), strerror(errno)); + return RC_INVAL; + } + + /* go through a specific interface? */ + if (bind_to) + { + int set = 1; + + /* set reuse flags */ + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof set) == -1) + { + set_error(gettext("error setting sockopt to interface (%s)"), strerror(errno)); + close(fd); + return RC_INVAL; + } + + if (bind(fd, bind_to, sizeof *bind_to) == -1) + { + set_error(gettext("error binding to interface (%s)"), strerror(errno)); + close(fd); + return RC_INVAL; + } + } + + if (max_mtu >= 0) + { + if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG, &max_mtu, sizeof max_mtu) == -1) + { + set_error(gettext("error setting MTU size (%s)"), strerror(errno)); + close(fd); + return RC_INVAL; + } + } + + if (use_no_delay) + { + int rc = -1; + + if ((rc = set_no_delay(fd)) != 0) + return rc; + } + + if (tx_buffer_size > 0) + { + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *)&tx_buffer_size, sizeof tx_buffer_size) == -1) + { + set_error(gettext("error setting transmit buffer size (%s)"), strerror(errno)); + close(fd); + return RC_INVAL; + } + } + + if (recv_buffer_size > 0) + { + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buffer_size, sizeof recv_buffer_size) == -1) + { + set_error(gettext("error setting receive buffer size (%s)"), strerror(errno)); + close(fd); + return RC_INVAL; + } + } + +#ifdef linux + if (priority >= 0) + { + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (char *)&priority, sizeof priority) == -1) + { + set_error(gettext("error setting priority (%s)"), strerror(errno)); + close(fd); + return RC_INVAL; + } + } +#endif + + if (tos >= 0) + { + if (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof tos) == -1) + { + set_error(gettext("failed to set TOS info")); + close(fd); + return RC_INVAL; + } + } + + return fd; +} + +int connect_to(int fd, struct addrinfo *ai, double timeout, char *tfo, char *msg, int msg_len, char *msg_accepted) +{ + int rc = -1; + struct timeval to; + fd_set wfds; + + /* make fd nonblocking */ + if (set_fd_nonblocking(fd) == -1) + return RC_INVAL; + + /* wait for connection */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + to.tv_sec = (long)(timeout / 1000.0); + to.tv_usec = (long)(timeout * 1000.0) % 1000000; + + /* connect to peer */ +#ifdef TCP_TFO + if (tfo && *tfo) + { + rc = sendto(fd, msg, msg_len, MSG_FASTOPEN, ai -> ai_addr, ai -> ai_addrlen); + + if(rc == msg_len) + *msg_accepted = 1; + if(errno == 0) + return RC_OK; + if(errno == ENOTSUP) + { + printf(gettext("TCP TFO Not Supported. Please check if \"/proc/sys/net/ipv4/tcp_fastopen\" is 1. Disabling TFO for now.\n")); + *tfo = 0; + goto old_connect; + } + } + + else +#else + (void)tfo; + (void)msg; + (void)msg_len; + (void)msg_accepted; +#endif + { + int rc = -1; + +old_connect: + rc = connect(fd, ai -> ai_addr, ai -> ai_addrlen); + + if (rc == 0) + { + /* connection made, return */ + return RC_OK; + } + + if (rc == -1) + { + /* problem connecting */ + if (errno != EINPROGRESS) + { + set_error(gettext("problem connecting to host: %s"), strerror(errno)); + return RC_INVAL; + } + } + } + + if (stop) + return RC_CTRLC; + + /* wait for connection */ + rc = select(fd + 1, NULL, &wfds, NULL, &to); + if (rc == 0) + { + set_error(gettext("connect time out")); + return RC_TIMEOUT; /* timeout */ + } + else if (rc == -1) + { + if (errno == EINTR) + return RC_CTRLC;/* ^C pressed */ + + set_error(gettext("select() failed: %s"), strerror(errno)); + + return RC_INVAL; /* error */ + } + else + { + int optval=0; + socklen_t optvallen = sizeof optval; + + /* see if the connect succeeded or failed */ + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optvallen) == -1) + { + set_error(gettext("getsockopt failed (%s)"), strerror(errno)); + return RC_INVAL; + } + + /* no error? */ + if (optval == 0) + return RC_OK; + + /* don't ask */ + errno = optval; + } + + set_error(gettext("could not connect (%s)"), strerror(errno)); + + return RC_INVAL; +} diff --git a/tcp.h b/tcp.h new file mode 100644 index 0000000..d521e0d --- /dev/null +++ b/tcp.h @@ -0,0 +1,6 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +int create_socket(struct sockaddr *bind_to, struct addrinfo *ai, int recv_buffer_size, int tx_buffer_size, int max_mtu, char use_no_delay, int priority, int tos); +int connect_to(int fd, struct addrinfo *ai, double timeout, char *tfo, char *msg, int msg_len, char *msg_accepted); +void failure_close(int fd); diff --git a/test_TFO.c b/test_TFO.c new file mode 100644 index 0000000..08b7539 --- /dev/null +++ b/test_TFO.c @@ -0,0 +1,13 @@ +/* $Revision$ */ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int qlen = 5; + + setsockopt(sfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)); + + return 0; +} diff --git a/test_fftw3.c b/test_fftw3.c new file mode 100644 index 0000000..6d3d4d6 --- /dev/null +++ b/test_fftw3.c @@ -0,0 +1,13 @@ +/* $Revision$ */ +#include + +int main(int argc, char *argv[]) +{ + /* note: it only needs to compile */ + void *dummy = fftw_malloc(4096); + double *dummyd = (double *)dummy; + + void *plan = fftw_plan_dft_r2c_1d(12345, dummyd, NULL, FFTW_ESTIMATE); + + return 0; +} diff --git a/test_ncurses.c b/test_ncurses.c new file mode 100644 index 0000000..7960fac --- /dev/null +++ b/test_ncurses.c @@ -0,0 +1,9 @@ +/* $Revision$ */ +#include + +int main(int argc, char *argv[]) +{ + initscr(); + + return 0; +} diff --git a/test_ncurses2.c b/test_ncurses2.c new file mode 100644 index 0000000..89aa89a --- /dev/null +++ b/test_ncurses2.c @@ -0,0 +1,9 @@ +/* $Revision$ */ +#include + +int main(int argc, char *argv[]) +{ + initscr(); + + return 0; +} diff --git a/test_openssl.c b/test_openssl.c new file mode 100644 index 0000000..ed8d46a --- /dev/null +++ b/test_openssl.c @@ -0,0 +1,42 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + BIO *bio_err = NULL; + + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_crypto_strings(); + + /* error write context */ + bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); + + const SSL_METHOD *meth = SSLv23_method(); + + SSL_CTX *ctx = SSL_CTX_new(meth); + + /***/ + + BIO_free(bio_err); + + ERR_free_strings(); + + ERR_remove_state(0); + ENGINE_cleanup(); + CONF_modules_free(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + + return 0; +} diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..f7089e3 --- /dev/null +++ b/utils.c @@ -0,0 +1,118 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "utils.h" + +double get_ts(void) +{ + struct timeval ts; + struct timezone tz; + + if (gettimeofday(&ts, &tz) == -1) + error_exit(gettext("gettimeofday failed")); + + return (double)ts.tv_sec + ((double)ts.tv_usec)/1000000.0 + (double)(tz.tz_minuteswest * 60); +} + +void split_string(const char *in, const char *split, char ***list, int *list_n) +{ + char *copy = strdup(in), *pc = copy; + int split_len = strlen(split); + + for(;;) + { + char *term = strstr(pc, split); + + if (term) + *term = 0x00; + + *list = (char **)realloc(*list, (*list_n + 1) * sizeof(char *)); + (*list)[*list_n] = strdup(pc); + (*list_n)++; + + if (!term) + break; + + pc = term + split_len; + } + + free(copy); +} + +void free_splitted_string(char **list, int n) +{ + int index=0; + + for(index=0; index= GIGA) + { + div = GIGA; + mul = "G"; + } + else if (a >= MEGA) + { + div = MEGA; + mul = "M"; + } + else if (a >= KILO) + { + div = KILO; + mul = "k"; + } + + if (mul[0]) + digits_sig--; + + (void)asprintf(&out, "%*.*f%s", digits_sig, digits_nsig, value / div, mul); + + return out; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..62314bd --- /dev/null +++ b/utils.h @@ -0,0 +1,18 @@ +/* Released under GPLv2 with exception for the OpenSSL library. See license.txt */ +/* $Revision$ */ + +double get_ts(void); + +void split_string(const char *in, const char *split, char ***list, int *list_n); +void free_splitted_string(char **list, int n); + +void str_add(char **to, const char *what, ...); + +#define GIGA 1000000000.0 +#define MEGA 1000000.0 +#define KILO 1000.0 + +char * format_value(double value, int digits_sig, int digits_nsig, char abbreviate); + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) diff --git a/version b/version new file mode 100644 index 0000000..d86a2bd --- /dev/null +++ b/version @@ -0,0 +1 @@ +VERSION=2.4