diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2019-01-24 14:18:17 +0100 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2019-01-24 14:21:57 +0100 |
commit | 22a07d791bf74b7c6478058e8b1c91018b1bc2f7 (patch) | |
tree | d9900b963cf331a3a6cb69061576c76800a88501 | |
parent | 476d6b28848f952c09a094e2227f20d69d4f1482 (diff) | |
parent | cea2b50c81db45d3f0eb19c327d2cc04bc01e1d2 (diff) |
Merge branch 'master' into releasev1.32
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | COPYING | 2 | ||||
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | configure | 289 | ||||
-rw-r--r-- | configure.ac | 36 | ||||
-rw-r--r-- | debian/copyright | 2 | ||||
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/Makefile.in | 26 | ||||
-rw-r--r-- | src/challenge.c | 7 | ||||
-rw-r--r-- | src/challenge.h | 2 | ||||
-rw-r--r-- | src/config.h.in | 15 | ||||
-rw-r--r-- | src/options.c | 2 | ||||
-rw-r--r-- | src/options.h | 2 | ||||
-rw-r--r-- | src/pconfig.h | 2 | ||||
-rw-r--r-- | src/pdesc.c | 4 | ||||
-rw-r--r-- | src/pdesc.h | 2 | ||||
-rw-r--r-- | src/pkt.c | 17 | ||||
-rw-r--r-- | src/pkt.h | 2 | ||||
-rw-r--r-- | src/ptunnel.c | 36 | ||||
-rw-r--r-- | src/ptunnel.h | 3 | ||||
-rw-r--r-- | src/utils.c | 29 | ||||
-rw-r--r-- | src/utils.h | 4 | ||||
-rwxr-xr-x | test/fuzzer.py | 278 |
25 files changed, 727 insertions, 58 deletions
diff --git a/.travis.yml b/.travis.yml index 7e7d6ad..e9a181d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: c before_install: - sudo apt-get -qq update - sudo apt-get install -y git debhelper fakeroot -- sudo apt-get install -y libpcap-dev libselinux1-dev +- sudo apt-get install -y libpcap-dev libselinux1-dev libbsd-dev - sudo apt-get install -y binutils-mingw-w64-i686 gcc-mingw-w64 mingw-w64-i686-dev mingw-w64-common - sudo apt-get install -y autoconf automake dh-autoreconf @@ -1,4 +1,4 @@ -Copyright (c) 2017, Toni Uhlig <matzeton@googlemail.com> +Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> All rights reserved. Redistribution and use in source and binary forms, with or without @@ -1,6 +1,13 @@ PingTunnel-NG Changelog ======================= +1.32 - 24. January 2019 +- improved error logging +- introduced icmp_filter via setsockopt to filter unwanted icmp messages +- more "secure" random number generator +- fixed NULL deref and invalid memory access by elnerd +(https://github.com/elnerd) PoC: https://www.securityfocus.com/bid/54627/info + 1.31 - 03. December 2018 - added Android build support (requires a root'ed device!) - fixed ArchLinux PKGBUILD/AUR @@ -128,6 +128,6 @@ Daniel Stoedle et al. License ------- -Ping Tunnel NG is Copyright (c) 2017, Toni Uhlig <matzeton@googlemail.com>, +Ping Tunnel NG is Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com>, All rights reserved. Ping Tunnel NG is licensed under the BSD License. Please see the COPYING file for details. @@ -144,7 +144,7 @@ Daniel Stoedle et al. ## License ``` -Ping Tunnel NG is Copyright (c) 2017, Toni Uhlig <matzeton@googlemail.com>, +Ping Tunnel NG is Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com>, All rights reserved. Ping Tunnel NG is licensed under the BSD License. Please see the COPYING file for details. ``` @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for ptunnel-ng 1.3. +# Generated by GNU Autoconf 2.69 for ptunnel-ng 1.32. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='ptunnel-ng' PACKAGE_TARNAME='ptunnel-ng' -PACKAGE_VERSION='1.3' -PACKAGE_STRING='ptunnel-ng 1.3' +PACKAGE_VERSION='1.32' +PACKAGE_STRING='ptunnel-ng 1.32' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -622,6 +622,12 @@ ac_includes_default="\ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS +HAVE_ARC4RANDOM_FALSE +HAVE_ARC4RANDOM_TRUE +HAVE_RANDOM_FALSE +HAVE_RANDOM_TRUE +HAVE_ICMPFILTER_FALSE +HAVE_ICMPFILTER_TRUE IS_WINDOWS_FALSE IS_WINDOWS_TRUE HAVE_SELINUX_FALSE @@ -1291,7 +1297,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures ptunnel-ng 1.3 to adapt to many kinds of systems. +\`configure' configures ptunnel-ng 1.32 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1362,7 +1368,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ptunnel-ng 1.3:";; + short | recursive ) echo "Configuration of ptunnel-ng 1.32:";; esac cat <<\_ACEOF @@ -1455,7 +1461,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -ptunnel-ng configure 1.3 +ptunnel-ng configure 1.32 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2114,7 +2120,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ptunnel-ng $as_me 1.3, which was +It was created by ptunnel-ng $as_me 1.32, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2571,6 +2577,9 @@ case x"${host}" in use_msw=yes PROGRAM_EXT=".exe" ;; + *-androideabi) + use_android=yes + ;; esac # Check whether --enable-silent-rules was given. @@ -3059,7 +3068,7 @@ fi # Define the identity of the package. PACKAGE='ptunnel-ng' - VERSION='1.3' + VERSION='1.32' cat >>confdefs.h <<_ACEOF @@ -5266,7 +5275,19 @@ fi done if test x"${use_msw}" != x"yes"; then - for ac_header in sys/unistd.h sys/types.h sys/socket.h netinet/in.h arpa/inet.h netdb.h pthread.h errno.h net/ethernet.h syslog.h pwd.h grp.h + for ac_header in sys/unistd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/unistd.h" "ac_cv_header_sys_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_unistd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_UNISTD_H 1 +_ACEOF + +fi + +done + + for ac_header in sys/types.h sys/socket.h netinet/in.h arpa/inet.h netdb.h pthread.h errno.h net/ethernet.h syslog.h pwd.h grp.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -5339,6 +5360,21 @@ else as_fn_error $? "Missing pthread library." "$LINENO" 5 fi + if test x"${use_android}" != x"yes"; then + for ac_func in signal +do : + ac_fn_c_check_func "$LINENO" "signal" "ac_cv_func_signal" +if test "x$ac_cv_func_signal" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SIGNAL 1 +_ACEOF + +else + as_fn_error $? "Missing essential non-Android std functions." "$LINENO" 5 +fi +done + + fi for ac_func in pthread_mutex_init pthread_mutex_lock pthread_mutex_unlock syslog getaddrinfo freeaddrinfo gai_strerror do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -5759,7 +5795,7 @@ else fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -for ac_func in malloc calloc free memcpy memset signal printf sprintf vsnprintf strerror strlen strncmp strstr strtol strtoul fopen fprintf gettimeofday close fclose exit getopt_long +for ac_func in malloc calloc free memcpy memset printf sprintf vsnprintf strerror strlen strncmp strstr strtol strtoul fopen fprintf gettimeofday close fclose exit getopt_long do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -5892,6 +5928,106 @@ done fi +for ac_header in bsd/stdlib.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "bsd/stdlib.h" "ac_cv_header_bsd_stdlib_h" "$ac_includes_default" +if test "x$ac_cv_header_bsd_stdlib_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_BSD_STDLIB_H 1 +_ACEOF + +else + random_enabled=yes +fi + +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing arc4random" >&5 +$as_echo_n "checking for library containing arc4random... " >&6; } +if ${ac_cv_search_arc4random+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char arc4random (); +int +main () +{ +return arc4random (); + ; + return 0; +} +_ACEOF +for ac_lib in '' bsd; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_arc4random=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_arc4random+:} false; then : + break +fi +done +if ${ac_cv_search_arc4random+:} false; then : + +else + ac_cv_search_arc4random=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_arc4random" >&5 +$as_echo "$ac_cv_search_arc4random" >&6; } +ac_res=$ac_cv_search_arc4random +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + +for ac_func in arc4random +do : + ac_fn_c_check_func "$LINENO" "arc4random" "ac_cv_func_arc4random" +if test "x$ac_cv_func_arc4random" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_ARC4RANDOM 1 +_ACEOF + random_enabled= +fi +done + +if test x"${random_enabled}" != x; then + for ac_func in timespec_get srandom random +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + random_enabled=yes +else + random_enabled= +fi +done + +else + arc4random_enabled=yes +fi + if test x"${selinux_enabled}" != x; then for ac_header in selinux/selinux.h do : @@ -5967,6 +6103,99 @@ fi fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working ICMP_FILTER" >&5 +$as_echo_n "checking for working ICMP_FILTER... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include <netinet/in.h> +#include <sys/socket.h> +#include <linux/icmp.h> +void foo() { + struct icmp_filter filt; + int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + filt.data = ~((1<<ICMP_ECHO) | (1<<ICMP_ECHOREPLY)); + setsockopt(sockfd, SOL_RAW, ICMP_FILTER, &filt, sizeof filt); +} + +int +main () +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + with_icmp_filter="yes" + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + with_icmp_filter="no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing __android_log_vprint" >&5 +$as_echo_n "checking for library containing __android_log_vprint... " >&6; } +if ${ac_cv_search___android_log_vprint+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char __android_log_vprint (); +int +main () +{ +return __android_log_vprint (); + ; + return 0; +} +_ACEOF +for ac_lib in '' log; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search___android_log_vprint=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search___android_log_vprint+:} false; then : + break +fi +done +if ${ac_cv_search___android_log_vprint+:} false; then : + +else + ac_cv_search___android_log_vprint=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search___android_log_vprint" >&5 +$as_echo "$ac_cv_search___android_log_vprint" >&6; } +ac_res=$ac_cv_search___android_log_vprint +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + if test x"${pcap_enabled}" = xyes; then HAVE_PCAP_TRUE= HAVE_PCAP_FALSE='#' @@ -5991,6 +6220,30 @@ else IS_WINDOWS_FALSE= fi + if test x"${with_icmp_filter}" = xyes; then + HAVE_ICMPFILTER_TRUE= + HAVE_ICMPFILTER_FALSE='#' +else + HAVE_ICMPFILTER_TRUE='#' + HAVE_ICMPFILTER_FALSE= +fi + + if test x"${random_enabled}" = xyes; then + HAVE_RANDOM_TRUE= + HAVE_RANDOM_FALSE='#' +else + HAVE_RANDOM_TRUE='#' + HAVE_RANDOM_FALSE= +fi + + if test x"${arc4random_enabled}" = xyes; then + HAVE_ARC4RANDOM_TRUE= + HAVE_ARC4RANDOM_FALSE='#' +else + HAVE_ARC4RANDOM_TRUE='#' + HAVE_ARC4RANDOM_FALSE= +fi + ac_config_headers="$ac_config_headers src/config.h:src/config.h.in" @@ -6139,6 +6392,18 @@ if test -z "${IS_WINDOWS_TRUE}" && test -z "${IS_WINDOWS_FALSE}"; then as_fn_error $? "conditional \"IS_WINDOWS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_ICMPFILTER_TRUE}" && test -z "${HAVE_ICMPFILTER_FALSE}"; then + as_fn_error $? "conditional \"HAVE_ICMPFILTER\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_RANDOM_TRUE}" && test -z "${HAVE_RANDOM_FALSE}"; then + as_fn_error $? "conditional \"HAVE_RANDOM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_ARC4RANDOM_TRUE}" && test -z "${HAVE_ARC4RANDOM_FALSE}"; then + as_fn_error $? "conditional \"HAVE_ARC4RANDOM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 @@ -6536,7 +6801,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by ptunnel-ng $as_me 1.3, which was +This file was extended by ptunnel-ng $as_me 1.32, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6602,7 +6867,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -ptunnel-ng config.status 1.3 +ptunnel-ng config.status 1.32 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index f45d875..eeb6634 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.69) -AC_INIT([ptunnel-ng], [1.31], [], [], []) +AC_INIT([ptunnel-ng], [1.32], [], [], []) AC_CONFIG_SRCDIR([src/config.h.in]) AC_CONFIG_FILES([Makefile src/Makefile]) @@ -145,6 +145,18 @@ if test x"${pcap_enabled}" != x -a \ [pcap_enabled=]) fi +dnl Check for more secure randomization functions +AC_CHECK_HEADERS([bsd/stdlib.h],, [random_enabled=yes]) +AC_SEARCH_LIBS([arc4random], [bsd],,,) +AC_CHECK_FUNCS([arc4random], [random_enabled=],) +if test x"${random_enabled}" != x; then + AC_CHECK_FUNCS([timespec_get srandom random], + [random_enabled=yes], + [random_enabled=]) +else + arc4random_enabled=yes +fi + dnl Check for SELINUX if test x"${selinux_enabled}" != x; then AC_CHECK_HEADERS([selinux/selinux.h],, @@ -152,6 +164,25 @@ if test x"${selinux_enabled}" != x; then AC_SEARCH_LIBS([setcon], [selinux],,[selinux_enabled=],) fi +dnl Check for ICMP_FILTER +AC_MSG_CHECKING([for working ICMP_FILTER]) +AC_COMPILE_IFELSE( +[AC_LANG_PROGRAM([[ +#include <netinet/in.h> +#include <sys/socket.h> +#include <linux/icmp.h> +void foo() { + struct icmp_filter filt; + int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + filt.data = ~((1<<ICMP_ECHO) | (1<<ICMP_ECHOREPLY)); + setsockopt(sockfd, SOL_RAW, ICMP_FILTER, &filt, sizeof filt); +} +]], [])] +,[AC_MSG_RESULT([yes]) + with_icmp_filter="yes"] +,[AC_MSG_RESULT([no]) + with_icmp_filter="no"]) + dnl Check for Android liblog.so AC_SEARCH_LIBS([__android_log_vprint], [log],,,) @@ -159,6 +190,9 @@ dnl Set automake conf vars AM_CONDITIONAL([HAVE_PCAP], [test x"${pcap_enabled}" = xyes]) AM_CONDITIONAL([HAVE_SELINUX], [test x"${selinux_enabled}" = xyes]) AM_CONDITIONAL([IS_WINDOWS], [test x"${use_msw}" = xyes]) +AM_CONDITIONAL([HAVE_ICMPFILTER], [test x"${with_icmp_filter}" = xyes]) +AM_CONDITIONAL([HAVE_RANDOM], [test x"${random_enabled}" = xyes]) +AM_CONDITIONAL([HAVE_ARC4RANDOM], [test x"${arc4random_enabled}" = xyes]) dnl output config headers AC_CONFIG_HEADERS([src/config.h:src/config.h.in]) diff --git a/debian/copyright b/debian/copyright index b0e21c4..855cf53 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: ptunnel Source: http://www.cs.uit.no/~daniels/PingTunnel/index.html Files: * -Copyright: Copyright 2017 Toni Uhlig <matzeton@googlemail.com> +Copyright: Copyright 2017-2019 Toni Uhlig <matzeton@googlemail.com> License: BSD-3-Clause Files: md5.* diff --git a/src/Makefile.am b/src/Makefile.am index 6d37a49..1d161a2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,6 +14,18 @@ if HAVE_SELINUX ptunnel_ng_CFLAGS += -DHAVE_SELINUX=1 endif +if HAVE_ICMPFILTER +ptunnel_ng_CFLAGS += -DHAVE_ICMPFILTER=1 +endif + +if HAVE_ARC4RANDOM +ptunnel_ng_CFLAGS += -DHAVE_ARC4RANDOM=1 +else +if HAVE_RANDOM +ptunnel_ng_CFLAGS += -DHAVE_RANDOM=1 +endif +endif + ptunnel_ng_SOURCES = \ md5.c \ challenge.c \ diff --git a/src/Makefile.in b/src/Makefile.in index 2c84d72..4d3bc76 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -91,11 +91,14 @@ host_triplet = @host@ bin_PROGRAMS = ptunnel-ng$(EXEEXT) @HAVE_PCAP_TRUE@am__append_1 = -DHAVE_PCAP=1 @HAVE_SELINUX_TRUE@am__append_2 = -DHAVE_SELINUX=1 -@IS_WINDOWS_TRUE@am__append_3 = -I$(abs_srcdir)/win32/includes -@IS_WINDOWS_TRUE@am__append_4 = $(wpcap_IMP) -@IS_WINDOWS_TRUE@am__append_5 = $(wpcap_DEF) -@IS_WINDOWS_TRUE@am__append_6 = $(wpcap_IMP) -@IS_WINDOWS_TRUE@am__append_7 = $(wpcap_DEF) +@HAVE_ICMPFILTER_TRUE@am__append_3 = -DHAVE_ICMPFILTER=1 +@HAVE_ARC4RANDOM_TRUE@am__append_4 = -DHAVE_ARC4RANDOM=1 +@HAVE_ARC4RANDOM_FALSE@@HAVE_RANDOM_TRUE@am__append_5 = -DHAVE_RANDOM=1 +@IS_WINDOWS_TRUE@am__append_6 = -I$(abs_srcdir)/win32/includes +@IS_WINDOWS_TRUE@am__append_7 = $(wpcap_IMP) +@IS_WINDOWS_TRUE@am__append_8 = $(wpcap_DEF) +@IS_WINDOWS_TRUE@am__append_9 = $(wpcap_IMP) +@IS_WINDOWS_TRUE@am__append_10 = $(wpcap_DEF) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac @@ -118,7 +121,7 @@ am_ptunnel_ng_OBJECTS = ptunnel_ng-md5.$(OBJEXT) \ ptunnel_ng-pdesc.$(OBJEXT) ptunnel_ng-ptunnel.$(OBJEXT) \ $(am__objects_2) ptunnel_ng_OBJECTS = $(am_ptunnel_ng_OBJECTS) -ptunnel_ng_DEPENDENCIES = $(am__append_4) +ptunnel_ng_DEPENDENCIES = $(am__append_7) ptunnel_ng_LINK = $(CCLD) $(ptunnel_ng_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) @@ -312,12 +315,13 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ man8_MANS = ptunnel-ng.8 ptunnel_ng_CFLAGS = -Wall $(am__append_1) $(am__append_2) \ - $(am__append_3) -ptunnel_ng_LDADD = $(am__append_4) -CLEANFILES = $(am__append_6) -EXTTRA_DIST = $(am__append_7) + $(am__append_3) $(am__append_4) $(am__append_5) \ + $(am__append_6) +ptunnel_ng_LDADD = $(am__append_7) +CLEANFILES = $(am__append_9) +EXTTRA_DIST = $(am__append_10) ptunnel_ng_SOURCES = md5.c challenge.c options.c utils.c pkt.c pdesc.c \ - ptunnel.c $(am__append_5) + ptunnel.c $(am__append_8) @IS_WINDOWS_TRUE@wpcap_DEF = $(abs_srcdir)/win32/WPCAP.DEF @IS_WINDOWS_TRUE@wpcap_IMP = $(abs_srcdir)/win32/libwpcap_implib.a all: config.h diff --git a/src/challenge.c b/src/challenge.c index 24a13f7..f269313 100644 --- a/src/challenge.c +++ b/src/challenge.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -50,6 +50,7 @@ #include "challenge.h" #include "options.h" #include "md5.h" +#include "utils.h" /* generate_challenge: Generates a random challenge, incorporating the current * local timestamp to avoid replay attacks. @@ -62,9 +63,9 @@ challenge_t* generate_challenge(void) { c = (challenge_t *) calloc(1, sizeof(challenge_t)); gettimeofday(&tt, 0); c->sec = tt.tv_sec; - c->usec_rnd = tt.tv_usec + rand(); + c->usec_rnd = tt.tv_usec + pt_random(); for (i=0;i<6;i++) - c->random[i] = rand(); + c->random[i] = pt_random(); return c; } diff --git a/src/challenge.h b/src/challenge.h index 035a97e..18495cf 100644 --- a/src/challenge.h +++ b/src/challenge.h @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/config.h.in b/src/config.h.in index 609856e..0cd059a 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,11 +1,17 @@ /* src/config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the `arc4random' function. */ +#undef HAVE_ARC4RANDOM + /* Define to 1 if you have the <arpa/inet.h> header file. */ #undef HAVE_ARPA_INET_H /* Define to 1 if you have the <assert.h> header file. */ #undef HAVE_ASSERT_H +/* Define to 1 if you have the <bsd/stdlib.h> header file. */ +#undef HAVE_BSD_STDLIB_H + /* Define to 1 if you have the `calloc' function. */ #undef HAVE_CALLOC @@ -111,6 +117,9 @@ /* Define to 1 if you have the <pwd.h> header file. */ #undef HAVE_PWD_H +/* Define to 1 if you have the `random' function. */ +#undef HAVE_RANDOM + /* Define to 1 if you have the <selinux/selinux.h> header file. */ #undef HAVE_SELINUX_SELINUX_H @@ -123,6 +132,9 @@ /* Define to 1 if you have the `sprintf' function. */ #undef HAVE_SPRINTF +/* Define to 1 if you have the `srandom' function. */ +#undef HAVE_SRANDOM + /* Define to 1 if you have the <stdarg.h> header file. */ #undef HAVE_STDARG_H @@ -177,6 +189,9 @@ /* Define to 1 if you have the <sys/unistd.h> header file. */ #undef HAVE_SYS_UNISTD_H +/* Define to 1 if you have the `timespec_get' function. */ +#undef HAVE_TIMESPEC_GET + /* Define to 1 if you have the <time.h> header file. */ #undef HAVE_TIME_H diff --git a/src/options.c b/src/options.c index beecd39..4ae434f 100644 --- a/src/options.c +++ b/src/options.c @@ -2,7 +2,7 @@ * options.c * ptunnel is licensed under the BSD license: * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/options.h b/src/options.h index 3c42a19..176620a 100644 --- a/src/options.h +++ b/src/options.h @@ -2,7 +2,7 @@ * options.h * ptunnel is licensed under the BSD license: * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/pconfig.h b/src/pconfig.h index c85df14..6be141e 100644 --- a/src/pconfig.h +++ b/src/pconfig.h @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/pdesc.c b/src/pdesc.c index 906f35c..51fa3ab 100644 --- a/src/pdesc.c +++ b/src/pdesc.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -56,7 +56,7 @@ * the descriptor chain. If the sock argument is 0, the function will establish * a TCP connection to the ip and port given by dst_ip, dst_port. */ -proxy_desc_t* create_and_insert_proxy_desc(uint16_t id_no, uint16_t icmp_id, +proxy_desc_t *create_and_insert_proxy_desc(uint16_t id_no, uint16_t icmp_id, int sock, struct sockaddr_in *addr, uint32_t dst_ip, uint32_t dst_port, uint32_t init_state, uint32_t type) { diff --git a/src/pdesc.h b/src/pdesc.h index 35147db..d0767aa 100644 --- a/src/pdesc.h +++ b/src/pdesc.h @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -124,6 +124,10 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a pkt_flag = pt_pkt->state & kFlag_mask; pt_pkt->state &= ~kFlag_mask; + if (pt_pkt->state > (kNum_proto_types-1)) { + pt_log(kLog_error, "Dropping packet with invalid state.\n"); + return; + } pt_log(kLog_sendrecv, "Recv: %d [%d] bytes " "[seq = %d] [type = %s] " "[ack = %d] [icmp = %d] " @@ -166,10 +170,15 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a else init_state = kProto_data; - cur = create_and_insert_proxy_desc(pt_pkt->id_no, pkt->identifier, 0, + cur = (proxy_desc_t *) create_and_insert_proxy_desc(pt_pkt->id_no, pkt->identifier, 0, addr, pt_pkt->dst_ip, ntohl(pt_pkt->dst_port), init_state, kProxy_flag); + if (!cur) { + /* if failed, abort. Logging is done in create_insert_proxy_desc */ + pt_log(kLog_error, "Failed to create proxy descriptor!\n"); + return; + } if (init_state == kProto_authenticate) { pt_log(kLog_debug, "Sending authentication challenge..\n"); /* Send challenge */ @@ -189,7 +198,9 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a return; } else - pt_log(kLog_error, "Dropping duplicate proxy session request.\n"); + pt_log(kLog_error, "Dropping duplicate proxy session request " + "with ID %d and seq %d.\n", + pt_pkt->id_no, pt_pkt->seq_no); } else if (cur && pt_pkt->state == kProto_authenticate) { /* Sanity check packet length, and make sure it matches what we expect */ @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/ptunnel.c b/src/ptunnel.c index a4c5ae5..8fdf7a8 100644 --- a/src/ptunnel.c +++ b/src/ptunnel.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -62,7 +62,7 @@ #define errno GetLastError() /** Local error string storage */ static char errorstr[255]; -static char * print_last_windows_error() { +static char * print_last_windows_error() { char last_errorstr[255]; DWORD last_error = GetLastError(); @@ -126,10 +126,6 @@ int main(int argc, char *argv[]) { } #endif /* WIN32 */ - /* Seed random generator; it'll be used in combination with a timestamp - * when generating authentication challenges. - */ - srand(time(0)); memset(opts.password_digest, 0, kMD5_digest_size); /* The seq_expiry_tbl is used to prevent the remote ends from prematurely @@ -154,7 +150,7 @@ int main(int argc, char *argv[]) { #endif pt_log(kLog_info, "Starting %s.\n", PACKAGE_STRING); pt_log(kLog_info, "(c) 2004-2011 Daniel Stoedle, <daniels@cs.uit.no>\n"); - pt_log(kLog_info, "(c) 2017 Toni Uhlig, <matzeton@googlemail.com>\n"); + pt_log(kLog_info, "(c) 2017-2019 Toni Uhlig, <matzeton@googlemail.com>\n"); #ifdef WIN32 pt_log(kLog_info, "Windows version by Mike Miller, <mike@mikeage.net>\n"); #else @@ -184,7 +180,7 @@ int main(int argc, char *argv[]) { if (opts.chroot) { pt_log(kLog_info, "Restricting file access to %s\n", opts.root_dir); if (-1 == chdir(opts.root_dir) || -1 == chroot(opts.root_dir)) { - pt_log(kLog_error, "%s: %s\n", opts.root_dir, strerror(errno)); + pt_log(kLog_error, "chdir/chroot `%s': %s\n", opts.root_dir, strerror(errno)); exit(1); } } @@ -210,7 +206,7 @@ int main(int argc, char *argv[]) { if (! freopen("/dev/null", "r", stdin) || ! freopen("/dev/null", "w", stdout) || ! freopen("/dev/null", "w", stderr)) - pt_log(kLog_error, "freopen: %s\n", strerror(errno)); + pt_log(kLog_error, "freopen `%s': %s\n", "/dev/null", strerror(errno)); } } } @@ -323,7 +319,7 @@ void pt_forwarder(void) { } } addr = dest_addr; - rand_id = (uint16_t)rand(); + rand_id = (uint16_t) pt_random(); create_and_insert_proxy_desc(rand_id, rand_id, new_sock, &addr, opts.given_dst_ip, opts.given_dst_port, kProxy_start, kUser_flag); pthread_mutex_unlock(&num_threads_lock); } @@ -388,6 +384,9 @@ void* pt_proxy(void *args) { in_addr_t *adr; #endif struct in_addr in_addr; +#ifdef HAVE_ICMPFILTER + struct icmp_filter filt; +#endif /* Start the thread, initialize protocol and ring states. */ pt_log(kLog_debug, "Starting ping proxy..\n"); @@ -403,13 +402,24 @@ void* pt_proxy(void *args) { } } else { - if (opts.unprivileged) { + if (opts.unprivileged) + { pt_log(kLog_debug, "Attempting to create unprivileged ICMP datagram socket..\n"); fwd_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); - } - else { + } else { pt_log(kLog_debug, "Attempting to create privileged ICMP raw socket..\n"); fwd_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); +#ifdef HAVE_ICMPFILTER + if (opts.mode == kMode_forward) + filt.data = ~(1<<ICMP_ECHOREPLY); + else + filt.data = ~(1<<ICMP_ECHO); + if (fwd_sock >= 0 && + setsockopt(fwd_sock, SOL_RAW, ICMP_FILTER, &filt, sizeof filt) == -1) + { + pt_log(kLog_error, "setockopt for ICMP_FILTER: %s\n", strerror(errno)); + } +#endif } if (fwd_sock < 0) { pt_log(kLog_error, "Couldn't create %s socket: %s\n", diff --git a/src/ptunnel.h b/src/ptunnel.h index 7566f92..d93f997 100644 --- a/src/ptunnel.h +++ b/src/ptunnel.h @@ -45,6 +45,9 @@ #define PING_TUNNEL_H 1 #ifndef WIN32 +#ifdef HAVE_ICMPFILTER +#include <linux/icmp.h> +#endif #ifdef HAVE_SYS_UNISTD_H #include <sys/unistd.h> #endif diff --git a/src/utils.c b/src/utils.c index 66ed4c0..462d688 100644 --- a/src/utils.c +++ b/src/utils.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -43,8 +43,15 @@ * Note that the source code is best viewed with tabs set to 4 spaces. */ +#include <stdio.h> +#include <stdlib.h> #include <stdarg.h> #include <string.h> +#include <time.h> +#include <assert.h> +#ifdef HAVE_ARC4RANDOM +#include <bsd/stdlib.h> +#endif #ifndef WIN32 #include <syslog.h> @@ -142,3 +149,23 @@ void print_hexstr(unsigned char *buf, size_t siz) { free(out); } #endif + +int pt_random(void) { +#ifdef HAVE_ARC4RANDOM + return arc4random(); +#else +#ifdef HAVE_RANDOM +#ifndef TIME_UTC +#define TIME_UTC 1 +#endif + struct timespec ts; + + assert(timespec_get(&ts, TIME_UTC)); + srandom(ts.tv_nsec ^ ts.tv_sec); + return random(); +#else + srand(time(0)); + return rand(); +#endif +#endif +} diff --git a/src/utils.h b/src/utils.h index 8afa45c..0796848 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,7 +5,7 @@ * Copyright (c) 2004-2011, Daniel Stoedle <daniels@cs.uit.no>, * Yellow Lemon Software. All rights reserved. * - * Copyright (c) 2017 Toni Uhlig <matzeton@googlemail.com> + * Copyright (c) 2017-2019, Toni Uhlig <matzeton@googlemail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -60,4 +60,6 @@ int host_to_addr(const char *hostname, uint32_t *result); void print_hexstr(unsigned char *buf, size_t siz); #endif +int pt_random(void); + #endif diff --git a/test/fuzzer.py b/test/fuzzer.py new file mode 100755 index 0000000..766122c --- /dev/null +++ b/test/fuzzer.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python2.7 +""" + This is a python ICMP/Ping script, + which can be used to fuzz the proxy/forwarder + to detect security issues. + + This script was originally taken from: + https://gist.githubusercontent.com/pklaus/856268/raw/a4e295d0dbd1140bddc90616e93ab3b19718a87b/ping.py +""" + +import os +import time +import socket +import struct +import select +import random +import asyncore + +UINT_MAX=4294967295 +USHORT_MAX=65535 +ICMP_ECHO_REQUEST = 8 +ICMP_PROTO = socket.getprotobyname('icmp') +ERROR_DESCR = { + 1: ' - Note that ICMP messages can only be ' + 'sent from processes running as root.', + 10013: ' - Note that ICMP messages can only be sent by' + ' users or processes with administrator rights.' +} + +__all__ = ['create_packet', 'do_one', 'verbose_ping', 'PingQuery', + 'multi_ping_query'] + + +def checksum(source_string): + # I'm not too confident that this is right but testing seems to + # suggest that it gives the same answers as in_cksum in ping.c. + sum = 0 + count_to = (len(source_string) / 2) * 2 + count = 0 + while count < count_to: + this_val = ord(source_string[count + 1])*256+ord(source_string[count]) + sum = sum + this_val + sum = sum & 0xffffffff # Necessary? + count = count + 2 + if count_to < len(source_string): + sum = sum + ord(source_string[len(source_string) - 1]) + sum = sum & 0xffffffff # Necessary? + sum = (sum >> 16) + (sum & 0xffff) + sum = sum + (sum >> 16) + answer = ~sum + answer = answer & 0xffff + # Swap bytes. Bugger me if I know why. + answer = answer >> 8 | (answer << 8 & 0xff00) + return answer + + +def create_packet(id, data=None): + """Create a new echo request packet based on the given "id".""" + # Header is type (8), code (8), checksum (16), id (16), sequence (16) + header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, 0, id, 1) + if data is None: + data = 192 * 'Q' + # Calculate the checksum on the data and the dummy header. + my_checksum = checksum(header + data) + # Now that we have the right checksum, we put that in. It's just easier + # to make up a new header than to stuff it into the dummy. + header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, + socket.htons(my_checksum), id, 1) + return header + data + + +def do_one(dest_addr, timeout=1, data=None): + """ + Sends one ping to the given "dest_addr" which can be an ip or hostname. + "timeout" can be any integer or float except negatives and zero. + + Returns either the delay (in seconds) or None on timeout and an invalid + address, respectively. + + """ + try: + my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, ICMP_PROTO) + except socket.error as e: + if e.errno in ERROR_DESCR: + # Operation not permitted + raise socket.error(''.join((e.args[1], ERROR_DESCR[e.errno]))) + raise # raise the original error + try: + host = socket.gethostbyname(dest_addr) + except socket.gaierror: + return + # Maximum for an unsigned short int c object counts to 65535 so + # we have to sure that our packet id is not greater than that. + packet_id = int((id(timeout) * random.random()) % 65535) + packet = create_packet(packet_id, data) + while packet: + # The icmp protocol does not use a port, but the function + # below expects it, so we just give it a dummy port. + sent = my_socket.sendto(packet, (dest_addr, 1)) + packet = packet[sent:] + delay = receive_ping(my_socket, packet_id, time.time(), timeout) + my_socket.close() + return delay + + +def receive_ping(my_socket, packet_id, time_sent, timeout): + # Receive the ping from the socket. + time_left = timeout + while True: + started_select = time.time() + ready = select.select([my_socket], [], [], time_left) + how_long_in_select = time.time() - started_select + if ready[0] == []: # Timeout + return + time_received = time.time() + rec_packet, addr = my_socket.recvfrom(1024) + icmp_header = rec_packet[20:28] + type, code, checksum, p_id, sequence = struct.unpack( + 'bbHHh', icmp_header) + if p_id == packet_id: + return time_received - time_sent + time_left -= time_received - time_sent + if time_left <= 0: + return + + +def verbose_ping(dest_addr, data=None, timeout=2, count=4): + """ + Sends one ping to the given "dest_addr" which can be an ip or hostname. + + "timeout" can be any integer or float except negatives and zero. + "count" specifies how many pings will be sent. + + Displays the result on the screen. + + """ + for i in range(count): + print('ping {}...'.format(dest_addr)) + delay = do_one(dest_addr, timeout, data) + if delay == None: + print('failed. (Timeout within {} seconds.)'.format(timeout)) + else: + delay = round(delay * 1000.0, 4) + print('get ping in {} milliseconds.'.format(delay)) + print('') + + +class PingQuery(asyncore.dispatcher): + def __init__(self, host, p_id, timeout=0.5, ignore_errors=False, data=None): + """ + Derived class from "asyncore.dispatcher" for sending and + receiving an icmp echo request/reply. + + Usually this class is used in conjunction with the "loop" + function of asyncore. + + Once the loop is over, you can retrieve the results with + the "get_result" method. Assignment is possible through + the "get_host" method. + + "host" represents the address under which the server can be reached. + "timeout" is the interval which the host gets granted for its reply. + "p_id" must be any unique integer or float except negatives and zeros. + + If "ignore_errors" is True, the default behaviour of asyncore + will be overwritten with a function which does just nothing. + + """ + asyncore.dispatcher.__init__(self) + try: + self.create_socket(socket.AF_INET, socket.SOCK_RAW, ICMP_CODE) + except socket.error as e: + if e.errno in ERROR_DESCR: + # Operation not permitted + raise socket.error(''.join((e.args[1], ERROR_DESCR[e.errno]))) + raise # raise the original error + self.time_received = 0 + self.time_sent = 0 + self.timeout = timeout + # Maximum for an unsigned short int c object counts to 65535 so + # we have to sure that our packet id is not greater than that. + self.packet_id = int((id(timeout) / p_id) % 65535) + self.host = host + self.packet = create_packet(self.packet_id, data) + if ignore_errors: + # If it does not care whether an error occured or not. + self.handle_error = self.do_not_handle_errors + self.handle_expt = self.do_not_handle_errors + + def writable(self): + return self.time_sent == 0 + + def handle_write(self): + self.time_sent = time.time() + while self.packet: + # The icmp protocol does not use a port, but the function + # below expects it, so we just give it a dummy port. + sent = self.sendto(self.packet, (self.host, 1)) + self.packet = self.packet[sent:] + + def readable(self): + # As long as we did not sent anything, the channel has to be left open. + if (not self.writable() + # Once we sent something, we should periodically check if the reply + # timed out. + and self.timeout < (time.time() - self.time_sent)): + self.close() + return False + # If the channel should not be closed, we do not want to read something + # until we did not sent anything. + return not self.writable() + + def handle_read(self): + read_time = time.time() + packet, addr = self.recvfrom(1024) + header = packet[20:28] + type, code, checksum, p_id, sequence = struct.unpack("bbHHh", header) + if p_id == self.packet_id: + # This comparison is necessary because winsocks do not only get + # the replies for their own sent packets. + self.time_received = read_time + self.close() + + def get_result(self): + """Return the ping delay if possible, otherwise None.""" + if self.time_received > 0: + return self.time_received - self.time_sent + + def get_host(self): + """Return the host where to the request has or should been sent.""" + return self.host + + def do_not_handle_errors(self): + # Just a dummy handler to stop traceback printing, if desired. + pass + + def create_socket(self, family, type, proto): + # Overwritten, because the original does not support the "proto" arg. + sock = socket.socket(family, type, proto) + sock.setblocking(0) + self.set_socket(sock) + # Part of the original but is not used. (at least at python 2.7) + # Copied for possible compatiblity reasons. + self.family_and_type = family, type + + # If the following methods would not be there, we would see some very + # "useful" warnings from asyncore, maybe. But we do not want to, or do we? + def handle_connect(self): + pass + + def handle_accept(self): + pass + + def handle_close(self): + self.close() + +def build_pt_pkt(ip, port, state, ack, seq, rsv, data): + if type(ip) is int: + dst_ip = ip + elif type(ip) is str: + dst_ip = struct.unpack('<L', socket.inet_aton(ip))[0] + else: + raise Exception('ip is not of type str|int') + dst_port = int(port) + return struct.pack('!IIIIIIHH', + 0xdeadc0de, dst_ip, dst_port, state, ack, len(data), + seq, rsv) + data + + +if __name__ == '__main__': + # Testing + while True: + pt_pkt = build_pt_pkt('127.0.0.1', '22', 1, 2, + random.randint(0, USHORT_MAX), + random.randint(0, USHORT_MAX), 'blah') + + verbose_ping('127.0.0.1', pt_pkt, 1, 1) + random.seed(time.clock()) |