aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2019-01-24 14:18:17 +0100
committerToni Uhlig <matzeton@googlemail.com>2019-01-24 14:21:57 +0100
commit22a07d791bf74b7c6478058e8b1c91018b1bc2f7 (patch)
treed9900b963cf331a3a6cb69061576c76800a88501
parent476d6b28848f952c09a094e2227f20d69d4f1482 (diff)
parentcea2b50c81db45d3f0eb19c327d2cc04bc01e1d2 (diff)
Merge branch 'master' into releasev1.32
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
-rw-r--r--.travis.yml2
-rw-r--r--COPYING2
-rw-r--r--ChangeLog7
-rw-r--r--README2
-rw-r--r--README.md2
-rwxr-xr-xconfigure289
-rw-r--r--configure.ac36
-rw-r--r--debian/copyright2
-rw-r--r--src/Makefile.am12
-rw-r--r--src/Makefile.in26
-rw-r--r--src/challenge.c7
-rw-r--r--src/challenge.h2
-rw-r--r--src/config.h.in15
-rw-r--r--src/options.c2
-rw-r--r--src/options.h2
-rw-r--r--src/pconfig.h2
-rw-r--r--src/pdesc.c4
-rw-r--r--src/pdesc.h2
-rw-r--r--src/pkt.c17
-rw-r--r--src/pkt.h2
-rw-r--r--src/ptunnel.c36
-rw-r--r--src/ptunnel.h3
-rw-r--r--src/utils.c29
-rw-r--r--src/utils.h4
-rwxr-xr-xtest/fuzzer.py278
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
diff --git a/COPYING b/COPYING
index 8b4cb53..6918db2 100644
--- a/COPYING
+++ b/COPYING
@@ -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
diff --git a/ChangeLog b/ChangeLog
index fc40604..4c82b5c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/README b/README
index 97cd0d0..1a0712e 100644
--- a/README
+++ b/README
@@ -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.
diff --git a/README.md b/README.md
index 34c00c1..5d48190 100644
--- a/README.md
+++ b/README.md
@@ -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.
```
diff --git a/configure b/configure
index da12c42..a20a406 100755
--- a/configure
+++ b/configure
@@ -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:
diff --git a/src/pkt.c b/src/pkt.c
index 869cf51..d446ffd 100644
--- a/src/pkt.c
+++ b/src/pkt.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:
@@ -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 */
diff --git a/src/pkt.h b/src/pkt.h
index 9668d97..338bc65 100644
--- a/src/pkt.h
+++ b/src/pkt.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/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())