PingTunnel/0000755000076500007650000000000011217641661013000 5ustar danielsdanielsPingTunnel/CHANGELOG0000700000076500007650000000663011217641661014210 0ustar danielsdanielsPingTunnel Changelog ==================== 0.70 - 12. January 2009 - Added rudimentary support for tunneling over udp port 53. - Added built-in support for compiling on Windows, thanks to Mike Miller. - Added syslog support, also courtesy of Mike Miller. - Experimental support for spoofing the source IP address. The source address must be explicitly configured in the source code, and it might not work (which is why it's experimental). To enable, define kPT_add_iphdr to 1. 0.62 - [Never released] - Added many comments to the ptunnel header file. - Moved most include directives to the header file. - Fixed crash if proxy or destination hostnames could not be looked up. 0.61 - 26. May 2005 - Noted that ptunnel now works without packet capturing on Mac OS X 10.4 Tiger. - Log files are now opened in append-mode (i.e., not truncated). - Fixed a bug that could cause ptunnel to crash if passwords shorter than 16 characters were used (the overwhelming majority of passwords, most likely!). 0.60 - 15. Apr 2005 - Added authentication support, changing parts of the ptunnel protocol. - Added a manpage. - Added MD5 implementation by L. Peter Deutsch for authentication. - Moved declaration of a variable to allow ptunnel to compile cleanly on gcc 2.95 - thanks Choong! - Added error handling for proxy connections. - Fixed a problem with printing send/recv info. All verbosity levels should work now. - Updated contact info. 0.55 - 18. Feb 2005 - Fixed a locking bug that would manifest itself when the maximum connection count on the proxy was reached. - Fixed ptunnel to compile correctly on 64-bit architectures (thanks to smund Grammeltvedt for pointing out the necessary changes!) - Added a new logging level (level 5). Level 4 no longer outputs send and receive info; for this info level 5 is necessary. - When libpcap is in use, the interface is no longer put in promiscous mode. This has never been necessary, and also makes it impossible to use ptunnel with more than one client on the same network. - Discovered a problem with pcap that would cause ptunnel to hang when pcap was in use. This problem has not been resolved; the workaround is to simply re-execute the client, or disable pcap if possible. - Added support for specifying a log file on the command line, using the -f switch. 0.54 - 1. Feb 2005 - Fixed a byte-order bug (thanks Alfred!) 0.53 - 31. Jan 2005 - Fixed a threading bug, and implemented support for limiting the maximum number of tunnels. Thanks to Alfred Young for reporting and fixing the following bugs: - Fixed a bug with packets captured via libpcap, where the struct sockaddr wasn't properly zeroed. - Fixed a byte order bug in create_and_insert_proxy_desc. - Fixed a memory leak in remove_proxy_desc. 0.52 - 3. Jan 2005 - Fixed a problem introduced by the ICMP ID field changes. The proxy didn't retain the ID of the packets received from clients, resulting in dropped ICMP packets at the router. This in turn lead to the tunnel no longer working, depending on the strictness of the router. This version fixes that problem. - Makefile is better now, thanks to Dries Verachtert. 0.51 - 2. Jan 2005 - Fixed incorrect checksum calculation for resent packets. - Stopped relying on the ICMP packet's "built-in" ID field, and moved the information to the data portion of the packet. - Changed default verbosity level from debug to verbose. 0.50 - 7. Dec 2004 Initial public release. PingTunnel/LICENSE0000700000076500007650000000301111217641661013771 0ustar danielsdanielsCopyright (c) 2004-2009, Daniel Stoedle , Yellow Lemon Software. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Yellow Lemon Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PingTunnel/Makefile0000700000076500007650000000237211217641661014435 0ustar danielsdaniels# Makefile for the pingtunnel utility # (c) 2004-2009 Daniel Stoedle, daniels@cs.uit.no # ptunnel.exe target added by Mike Miller, mike@mikeage.net CC = gcc CFLAGS = -Wall -g LDOPTS = -lpthread -lpcap PT_OBJS = ptunnel.o md5.o WIN32_CC = mingw32-gcc WIN32_CFLAGS = -g -Wall -DWIN32 -I"c:\Program Files\WpdPack\Include" WIN32_LDOPTS = -lwpcap -lwsock32 -L"c:\Program Files\WpdPack\Lib" WIN32_PT_OBJS = ptunnel.obj md5.obj prefix = /usr bindir = $(prefix)/bin mandir = $(prefix)/share/man/man8 all: ptunnel dist: rm -rf PingTunnel/ mkdir PingTunnel cp ptunnel.c ptunnel.h Makefile.dist PingTunnel/ mv PingTunnel/Makefile.dist PingTunnel/Makefile install: ptunnel install -d $(bindir)/ install -d $(mandir)/ install ./ptunnel $(bindir)/ptunnel install ./ptunnel.8 $(mandir)/ptunnel.8 ptunnel: $(PT_OBJS) $(CC) -o $@ $^ $(LDOPTS) `[ -e /usr/include/selinux/selinux.h ] && echo -lselinux` ptunnel.exe: $(WIN32_PT_OBJS) $(CC) -o $@ $^ $(WIN32_LDOPTS) clean: -rm -f *.o ptunnel -rm -f *.obj ptunnel.exe -rm -f .depend depend: .depend .depend: $(CC) $(CFLAGS) -MM *.c > $@ %.o:%.c $(CC) $(CFLAGS) `[ -e /usr/include/selinux/selinux.h ] && echo -DHAVE_SELINUX` -c -o $@ $< %.obj:%.c $(WIN32_CC) $(WIN32_CFLAGS) -c -o $@ $< -include .depend PingTunnel/md5.c0000700000076500007650000003022611217641661013625 0ustar danielsdaniels/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.1 2005/04/15 07:37:22 daniels Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } PingTunnel/md5.h0000700000076500007650000000650411217641661013634 0ustar danielsdaniels/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.1 2005/04/15 07:37:22 daniels Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ PingTunnel/ptunnel.80000700000076500007650000001447711217641661014564 0ustar danielsdaniels.TH ptunnel 8 "June 22, 2009" "Version 0.71" "Yellow Lemon Software" .SH NAME ptunnel \- tunnel TCP connections over ICMP echo request/reply packets. .SH SYNOPSIS .B ptunnel \-p proxy_address \-lp listen_port \-da destination_address \-dp dest_port [\-c network_device] [\-v verbosity] [-f file] [-udp] [-syslog] .B ptunnel [\-c network_device] [\-v verbosity] [\-f file] [\-udp] [\-syslog] [\-daemon file] .B ptunnel [\-u] [\-x password] [\-setuid user] [\-setgid group] [\-chroot dir] [\-setcon context] .B ptunnel \-h .SH DESCRIPTION ptunnel is an application that allows you to reliably tunnel TCP connections to a remote host using ICMP echo request and reply packets, commonly known as ping requests and replies. At first glance, this might seem like a rather useless thing to do, but it can actually come in handy in some cases. The following example illustrates the main motivation in creating ptunnel: .PP Setting: You're on the go, and stumble across an open wireless network. The network gives you an IP address, but won't let you send TCP or UDP packets out to the rest of the internet, for instance to check your mail. What to do? By chance, you discover that the network will allow you to ping any computer on the rest of the internet. With ptunnel, you can utilize this feature to check your mail, or do other things that require TCP. .SH OPTIONS .TP .SH Client options: .TP .B \-p proxy_address Specify the host on which the proxy is running. .TP .B \-lp listen_port Specifies the port on which the client will listen for incoming TCP connections. .TP .B \-da destination_addr Specifies the address to which you want your packets tunneled after reaching the proxy when in client mode, or restricts the destination packets can be forwarded to when in server mode. .TP .B \-dp destination_port Specifies/restrict the port that the proxy should tunnel the TCP connection to. .TP .SH Shared options: .TP .B \-c network_device Specify the network interface to capture packets from. Note that packet capturing isn't always necessary, but you should try this if you experience problems with ptunnel. .TP .B \-v verbosity Controls the verbosity level. -1 is no output, 0 shows errors only, 1 shows info messages, 2 gives more output, 3 provides even more output, level 4 displays debug info and level 5 displays absolutely everything, including the nasty details of sends and receives. .TP .B \-udp Enables tunneling over UDP port 53 (DNS) instead of using ICMP. This will only work if your proxy can accept incoming traffic on port 53, and the client is able to send data to the proxy on port 53. Note that this option does not wrap ptunnel's data in DNS-compliant packets. This option must be given on both the proxy and client side for things to work correctly. .TP .B \-syslog (Not available on Windows.) Changes logging to use the built-in syslog fascility. .TP .B \-daemon file (Not available on Windows.) Run in background, writing PID in file. .TP .B \-u Attempts to run ptunnel without privileges. This doesn't usually work! On UNIX systems please consider using the following three options instead: .TP .B \-setuid user (Not available on Windows.) When started in privileged mode, drop down to user's rights as soon as possible. .TP .B \-setgid group (Not available on Windows.) When started in privileged mode, drop down to group's rights as soon as possible. .TP .B \-chroot dir (Not available on Windows.) When started in privileged mode, restrict file access to the specified directory. .TP .B \-setcon context (Not available on Windows.) Set SELinux context when all there is left to do are network I/O operations. In order to be able to combine with -chroot you will have to `mount --bind /proc /chrootdir/proc` .TP .B \-x password Specifies a password or passphrase to use. This will allow you to protect the proxy from use by others who don't know the password. It needs to be specified on both proxy and client. .TP .B \-f file Specifies a log file. If you specify -syslog, syslog is always used instead. .TP .B \-h Displays brief usage information. .SH EXAMPLES The following assumes that ptunnel is run as root, both on the proxy and client. To tunnel ssh connections from the client machine via a proxy running on proxy.pingtunnel.com to the computer login.domain.com, the following command line would be used: .TP .B ptunnel \-p proxy.pingtunnel.com \-lp 8000 \-da login.domain.com \-dp 22 .PP An ssh connection to login.domain.com can now be established as follows: .TP .B ssh \-p 8000 localhost .PP If ssh complains about potential man-in-the-middle attacks, simply remove the offending key from the known_hosts file. The warning/error is expected if you have previously ssh'd to your local computer (i.e., ssh localhost), or you have used ptunnel to forward ssh connections to different hosts. Of course, for all of this to work, you need to start the proxy on your proxy-computer (proxy.pingtunnel.com). Doing this is very simple: .B ptunnel If you find that the proxy isn't working, you will need to enable packet capturing on the main network device. Currently this device is assumed to be an ethernet-device (i.e., ethernet or wireless). Packet capturing is enabled by giving the -c switch, and supplying the device name to capture packets on (for instance eth0 or en1). The same goes for the client. On Mac OS X, packet capturing must always be enabled (both for proxy and client), as resent packets won't be received otherwise. To protect yourself from others using your proxy, you can protect access to it with a password using the -x switch. The password is never sent in the clear, but keep in mind that it may be visible from tools like top or ps, which can display the command line used to start an application. .SH EXIT STATUS .B ptunnel does not exit until forced to do so by an interrupt (Ctrl-C) or if it crashes. .SH BUGS .B ptunnel currently does not handle packet capturing on network interfaces other than ethernet or wireless correctly. .SH AUTHORS Daniel Stoedle (daniels@cs.uit.no) Mike Miller (mike@mikeage.net) Sebastien Raveau (sebastien.raveau@epita.fr) .SH LICENSE .B ptunnel is licensed under the BSD License. .SH AVAILABILITY .TP The ptunnel homepage is currently located here: http://www.cs.uit.no/~daniels/PingTunnel/ .TP The freshmeat project page is located here: http://freshmeat.net/projects/ptunnel/ .PP Please take the time to rate ptunnel if you find it useful. Thanks! PingTunnel/ptunnel.c0000700000076500007650000016107611217641661014635 0ustar danielsdaniels/* ptunnel.c ptunnel is licensed under the BSD license: Copyright (c) 2004-2009, Daniel Stoedle , Yellow Lemon Software. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Yellow Lemon Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Contacting the author: You can get in touch with me, Daniel Stdle (that's the Norwegian letter oe, in case your text editor didn't realize), here: The official ptunnel website is here: Note that the source code is best viewed with tabs set to 4 spaces. */ #include "ptunnel.h" #include "md5.h" #ifdef WIN32 /* pthread porting to windows */ typedef CRITICAL_SECTION pthread_mutex_t; typedef unsigned long pthread_t; #define pthread_mutex_init InitializeCriticalSectionAndSpinCount #define pthread_mutex_lock EnterCriticalSection #define pthread_mutex_unlock LeaveCriticalSection #include /* Map errno (which Winsock doesn't use) to GetLastError; include the code in the strerror */ #ifdef errno #undef errno #endif /* errno */ #define errno GetLastError() /* Local error string storage */ static char errorstr[255]; static char * print_last_windows_error() { DWORD last_error = GetLastError(); memset(errorstr, 0, sizeof(errorstr)); FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, 0, errorstr, sizeof(errorstr), NULL); snprintf(errorstr, sizeof(errorstr), "%s (%d)", errorstr, last_error); return errorstr; } #define strerror(x) print_last_windows_error() #else #ifdef HAVE_SELINUX #include static char *selinux_context = NULL; #endif static uid_t uid = 0; static gid_t gid = 0; static char *root_dir = NULL; static bool daemonize = false; static FILE *pid_file = NULL; #endif /* WIN32 */ // Lots of globals pthread_mutex_t chain_lock, // Lock protecting the chain of connections num_threads_lock; // Lock protecting the num_threads variable bool unprivileged = false, // True if user wants to run without root pcap = false, // True if user wants packet capturing print_stats = false, // True if user wants continuous statistics printed. use_syslog = false; // True if user wants to log to syslog FILE *log_file = 0; // Usually stdout, but can be altered by the user int tcp_port = -1, // Port to send data to from the proxy tcp_listen_port = -1, // Port the client listens on log_level = kLog_event, // Default log level mode = kMode_proxy, // Default mode (proxy) num_threads = 0, // Current thread count max_tunnels = kMax_tunnels, // Default maximum number of tunnels to support at once num_tunnels = 0, // Current tunnel count use_udp = 0; // True if UDP should be used for transport (proxy runs on port 53) uint32_t *seq_expiry_tbl = 0, // Table indicating when a connection ID is allowable (used by proxy) given_proxy_ip = 0, // Proxy's internet address given_dst_ip = 0; // Destination client wants data forwarded to char *password = 0, // Password (must be the same on proxy and client for authentication to succeed) password_digest[kMD5_digest_size], // MD5 digest of password *pcap_device = 0; // Device to capture packets from // Some buffer constants const int tcp_receive_buf_len = kDefault_buf_size, icmp_receive_buf_len = kDefault_buf_size + kIP_header_size + kICMP_header_size + sizeof(ping_tunnel_pkt_t), pcap_buf_size = (kDefault_buf_size + kIP_header_size + kICMP_header_size + sizeof(ping_tunnel_pkt_t)+64)*64; char pcap_filter_program[] = "icmp"; // && (icmp[icmptype] = icmp-echo || icmp[icmptype] = icmp-echoreply)"; // The chain of client/proxy connections proxy_desc_t *chain = 0; const char *state_name[kNum_proto_types] = { "start", "ack", "data", "close", "authenticate" }; // Let the fun begin! int main(int argc, char *argv[]) { int i, opt; md5_state_t state; struct hostent *host_ent; #ifndef WIN32 struct passwd *pwnam; struct group *grnam; pid_t pid; #endif // Seed random generator; it'll be used in combination with a timestamp // when generating authentication challenges. srand(time(0)); memset(password_digest, 0, kMD5_digest_size); /* The seq_expiry_tbl is used to prevent the remote ends from prematurely re-using a sequence number. */ seq_expiry_tbl = calloc(65536, sizeof(uint32_t)); log_file = stdout; // Parse options opt = kOpt_undefined; mode = kMode_proxy; for (i=1;ih_addr_list[0]; break; case kOpt_set_password: password = argv[i]; pt_log(kLog_debug, "Password set - unauthenicated connections will be refused.\n"); // Compute the password digest md5_init(&state); md5_append(&state, (md5_byte_t*)password, strlen(password)); md5_finish(&state, (md5_byte_t*)password_digest); // Hide the password in process listing memset(argv[i], ' ', strlen(argv[i])); break; case kOpt_set_tcp_port: tcp_listen_port = atoi(argv[i]); break; case kOpt_set_tcp_dest_addr: if (NULL == (host_ent = gethostbyname(argv[i]))) { pt_log(kLog_error, "Failed to look up %s as destination address\n", argv[i]); return 1; } given_dst_ip = *(uint32_t*)host_ent->h_addr_list[0]; break; case kOpt_set_tcp_dest_port: tcp_port = atoi(argv[i]); break; case kOpt_set_max_tunnels: max_tunnels = atoi(argv[i]); if (max_tunnels <= 0) max_tunnels = kMax_tunnels; break; case kOpt_set_verbosity: log_level = atoi(argv[i]); break; case kOpt_set_pcap_device: pcap_device = argv[i]; pcap = 1; break; case kOpt_set_log_file: log_file = fopen(argv[i], "a"); if (!log_file) { log_file = stdout; pt_log(kLog_error, "Failed to open log file: '%s'. Cause: %s\n", argv[i], strerror(errno)); pt_log(kLog_error, "Reverting log to standard out.\n"); } break; #ifndef WIN32 case kOpt_set_unpriv_user: errno = 0; if (NULL == (pwnam = getpwnam(argv[i]))) { pt_log(kLog_error, "%s: %s\n", argv[i], errno ? strerror(errno) : "unknown user"); exit(1); } uid = pwnam->pw_uid; if (!gid) gid = pwnam->pw_gid; break; case kOpt_set_unpriv_group: errno = 0; if (NULL == (grnam = getgrnam(argv[i]))) { pt_log(kLog_error, "%s: %s\n", argv[i], errno ? strerror(errno) : "unknown group"); exit(1); } gid = grnam->gr_gid; break; case kOpt_set_root_dir: root_dir = strdup(argv[i]); break; case kOpt_set_selinux_context: #ifdef HAVE_SELINUX selinux_context = strdup(argv[i]); #else pt_log(kLog_error, "Sorry: SELinux support missing, please recompile with libselinux.\n"); return 1; #endif break; case kOpt_daemonize: daemonize = true; if (NULL == (pid_file = fopen(argv[i], "w"))) pt_log(kLog_error, "%s: %s\n", argv[i], strerror(errno)); break; #endif /* !WIN32 */ case kOpt_undefined: usage(argv[0]); return 1; } opt = kOpt_undefined; } } if (opt != kOpt_undefined) { usage(argv[0]); exit(1); } if (pcap && use_udp) { pt_log(kLog_error, "Packet capture is not supported (or needed) when using UDP for transport.\n"); pcap = 0; } pt_log(kLog_info, "Starting ptunnel v %d.%.2d.\n", kMajor_version, kMinor_version); pt_log(kLog_info, "(c) 2004-2009 Daniel Stoedle, \n"); #ifdef WIN32 pt_log(kLog_info, "Windows version by Mike Miller, \n"); #else pt_log(kLog_info, "Security features by Sebastien Raveau, \n"); #endif pt_log(kLog_info, "%s.\n", (mode == kMode_forward ? "Relaying packets from incoming TCP streams" : "Forwarding incoming ping packets over TCP")); if (use_udp) pt_log(kLog_info, "UDP transport enabled.\n"); #ifndef WIN32 signal(SIGPIPE, SIG_IGN); if (use_syslog) { if (log_file != stdout) { pt_log(kLog_error, "Logging using syslog overrides the use of a specified logfile (using -f).\n"); fclose(log_file); log_file = stdout; } openlog("ptunnel", LOG_PID, LOG_USER); } if (NULL != root_dir) { pt_log(kLog_info, "Restricting file access to %s\n", root_dir); if (-1 == chdir(root_dir) || -1 == chroot(root_dir)) { pt_log(kLog_error, "%s: %s\n", root_dir, strerror(errno)); exit(1); } } if (daemonize) { pt_log(kLog_info, "Going to the background.\n"); if (0 < (pid = fork())) exit(0); if (0 > pid) pt_log(kLog_error, "fork: %s\n", strerror(errno)); else if (-1 == setsid()) pt_log(kLog_error, "setsid: %s\n", strerror(errno)); else { if (0 < (pid = fork())) exit(0); if (0 > pid) pt_log(kLog_error, "fork: %s\n", strerror(errno)); else { if (NULL != pid_file) { fprintf(pid_file, "%d\n", getpid()); fclose(pid_file); } freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); } } } #endif /* !WIN32 */ #ifdef WIN32 WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return -1; } if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { WSACleanup(); return -1; } #endif /* WIN32 */ pthread_mutex_init(&chain_lock, 0); pthread_mutex_init(&num_threads_lock, 0); // Check mode, validate arguments and start either client or proxy. if (mode == kMode_forward) { if (!given_proxy_ip || !given_dst_ip || !tcp_port || !tcp_listen_port) { printf("One of the options are missing or invalid.\n"); usage(argv[0]); return -1; } pt_forwarder(); } else pt_proxy(0); // Clean up if (log_file != stdout) fclose(log_file); #ifdef WIN32 WSACleanup(); #else if (NULL != root_dir) free(root_dir); #ifdef HAVE_SELINUX if (NULL != selinux_context) free(selinux_context); #endif #endif /* WIN32 */ pt_log(kLog_info, "ptunnel is exiting.\n"); return 0; } void usage(char *exec_name) { printf("ptunnel v %d.%.2d.\n", kMajor_version, kMinor_version); printf("Usage: %s -p -lp -da -dp [-m max_tunnels] [-v verbosity] [-f logfile]\n", exec_name); printf(" %s [-m max_threads] [-v verbosity] [-c ]\n", exec_name); printf(" -p: Set address of peer running packet forwarder. This causes\n"); printf(" ptunnel to operate in forwarding mode - the absence of this\n"); printf(" option causes ptunnel to operate in proxy mode.\n"); printf(" -lp: Set TCP listening port (only used when operating in forward mode)\n"); printf(" -da: Set remote proxy destination address if client\n"); printf(" Restrict to only this destination address if server\n"); printf(" -dp: Set remote proxy destionation port if client\n"); printf(" Restrict to only this destination port if server\n"); printf(" -m: Set maximum number of concurrent tunnels\n"); printf(" -v: Verbosity level (-1 to 4, where -1 is no output, and 4 is all output)\n"); printf(" -c: Enable libpcap on the given device.\n"); printf(" -f: Specify a file to log to, rather than printing to standard out.\n"); printf(" -s: Client only. Enables continuous output of statistics (packet loss, etc.)\n"); #ifndef WIN32 printf("-daemon: Run in background, the PID will be written in the file supplied as argument\n"); printf("-syslog: Output debug to syslog instead of standard out.\n"); #endif /* !WIN32 */ printf(" -udp: Toggle use of UDP instead of ICMP. Proxy will listen on port 53 (must be root).\n\n"); printf("Security features: [-x password] [-u] [-setuid user] [-setgid group] [-chroot dir]\n"); printf(" -x: Set password (must be same on client and proxy)\n"); printf(" -u: Run proxy in unprivileged mode. This causes the proxy to forward\n"); printf(" packets using standard echo requests, instead of crafting custom echo replies.\n"); printf(" Unprivileged mode will only work on some systems, and is in general less reliable\n"); printf(" than running in privileged mode.\n"); #ifndef WIN32 printf(" Please consider combining the following three options instead:\n"); printf("-setuid: When started in privileged mode, drop down to user's rights as soon as possible\n"); printf("-setgid: When started in privileged mode, drop down to group's rights as soon as possible\n"); printf("-chroot: When started in privileged mode, restrict file access to the specified directory\n"); printf("-setcon: Set SELinux context when all there is left to do are network I/O operations\n"); printf(" To combine with -chroot you will have to `mount --bind /proc /chrootdir/proc`\n"); #endif /* !WIN32 */ printf("\nStarting the proxy (needs to run as root):\n"); printf(" [root #] %s\n", exec_name); printf("Starting a client (also needs root):\n"); printf(" [root #] %s -p proxy.pingtunnel.com -lp 8000 -da login.domain.com -dp 22 -c eth0\n", exec_name); printf("And then using the tunnel to ssh to login.domain.com:\n"); printf(" [user $] ssh -p 8000 localhost\n"); printf("And that's it. Enjoy your tunnel!\n\n"); } /* pt_forwarder: Sets up a listening TCP socket, and forwards incoming connections over ping packets. */ void pt_forwarder(void) { int server_sock, new_sock, sock, yes = 1; fd_set set; struct timeval time; struct sockaddr_in addr, dest_addr; socklen_t addr_len; pthread_t pid; uint16_t rand_id; pt_log(kLog_debug, "Starting forwarder..\n"); // Open our listening socket sock = socket(AF_INET, SOCK_STREAM, 0); if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(int)) == -1) { pt_log(kLog_error, "Failed to set SO_REUSEADDR option on listening socket: %s\n", strerror(errno)); close(sock); return; } addr.sin_family = AF_INET; addr.sin_port = htons(tcp_listen_port); addr.sin_addr.s_addr = INADDR_ANY; memset(&(addr.sin_zero), 0, 8); if (bind(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == -1) { pt_log(kLog_error, "Failed to bind listening socket: %s\n", strerror(errno)); close(sock); return; } server_sock = sock; // Fill out address structure memset(&dest_addr, 0, sizeof(struct sockaddr_in)); dest_addr.sin_family = AF_INET; if (use_udp) dest_addr.sin_port = htons(kDNS_port /* dns port.. */); else dest_addr.sin_port = 0; dest_addr.sin_addr.s_addr = given_proxy_ip; pt_log(kLog_verbose, "Proxy IP address: %s\n", inet_ntoa(*((struct in_addr*)&given_proxy_ip))); listen(server_sock, 10); while (1) { FD_ZERO(&set); FD_SET(server_sock, &set); time.tv_sec = 1; time.tv_usec = 0; if (select(server_sock+1, &set, 0, 0, &time) > 0) { pt_log(kLog_info, "Incoming connection.\n"); addr_len = sizeof(struct sockaddr_in); new_sock = accept(server_sock, (struct sockaddr*)&addr, &addr_len); if (new_sock < 0) { pt_log(kLog_error, "Accepting incoming connection failed.\n"); continue; } pthread_mutex_lock(&num_threads_lock); if (num_threads <= 0) { pt_log(kLog_event, "No running proxy thread - starting it.\n"); #ifndef WIN32 if (pthread_create(&pid, 0, pt_proxy, 0) != 0) #else if (0 == (pid = _beginthreadex(0, 0, (unsigned int (__stdcall *)(void *))pt_proxy, 0, 0, 0))) #endif { pt_log(kLog_error, "Couldn't create thread! Dropping incoming connection.\n"); close(new_sock); pthread_mutex_unlock(&num_threads_lock); continue; } } addr = dest_addr; rand_id = (uint16_t)rand(); create_and_insert_proxy_desc(rand_id, rand_id, new_sock, &addr, given_dst_ip, tcp_port, kProxy_start, kUser_flag); pthread_mutex_unlock(&num_threads_lock); } } } int pt_create_udp_socket(int port) { struct sockaddr_in addr; int sock, yes = 1; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { pt_log(kLog_error, "Failed to set create UDP socket..\n"); return 0; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void*)&yes, sizeof(int)) < 0) { pt_log(kLog_error, "Failed to set UDP REUSEADDR socket option. (Not fatal, hopefully.)\n"); close(sock); return 0; } #ifdef SO_REUSEPORT yes = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const void*)&yes, sizeof(int)) < 0) pt_log(kLog_error, "Failed to set UDP REUSEPORT socket option. (Not fatal, hopefully.)\n"); #endif //SO_REUSEPORT memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if (bind(sock, (struct sockaddr*) &addr, sizeof(struct sockaddr_in)) < 0) { pt_log(kLog_error, "Failed to bind UDP socket to port %d (try running as root).\n", port); close(sock); return 0; } return sock; } #define kPT_add_iphdr 0 /* pt_proxy: This function does all the client and proxy stuff. */ void* pt_proxy(void *args) { fd_set set; struct timeval timeout; int bytes; struct sockaddr_in addr; socklen_t addr_len; int fwd_sock = 0, max_sock = 0, idx; char *buf; double now, last_status_update = 0.0; proxy_desc_t *cur, *prev, *tmp; pcap_info_t pc; xfer_stats_t xfer; // Start the thread, initialize protocol and ring states. pt_log(kLog_debug, "Starting ping proxy..\n"); if (use_udp) { pt_log(kLog_debug, "Creating UDP socket..\n"); if (mode == kMode_proxy) fwd_sock = pt_create_udp_socket(kDNS_port); else fwd_sock = pt_create_udp_socket(0); if (!fwd_sock) { pt_log(kLog_error, "Failed to create UDP socket.\n"); return 0; } } else { if (unprivileged) { pt_log(kLog_debug, "Attempting to create unprivileged ICMP datagram socket..\n"); fwd_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); } else { #if kPT_add_iphdr int opt = 1; #endif pt_log(kLog_debug, "Attempting to create privileged ICMP raw socket..\n"); #if kPT_add_iphdr // experimental fwd_sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP); printf("Set ip-hdr-inc; result = %d\n", setsockopt(fwd_sock, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt))); #else fwd_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); #endif } if (fwd_sock < 0) { pt_log(kLog_error, "Couldn't create %s socket: %s\n", (unprivileged ? "unprivileged datagram" : "privileged raw"), strerror(errno)); return 0; } } max_sock = fwd_sock+1; if (pcap) { if (use_udp) { pt_log(kLog_error, "Packet capture is not useful with UDP [should not get here!]!\n"); close(fwd_sock); return 0; } if (!unprivileged) { pt_log(kLog_info, "Initializing pcap.\n"); pc.pcap_err_buf = malloc(PCAP_ERRBUF_SIZE); pc.pcap_data_buf = malloc(pcap_buf_size); pc.pcap_desc = pcap_open_live(pcap_device, pcap_buf_size, 0 /* promiscous */, 50 /* ms */, pc.pcap_err_buf); if (pc.pcap_desc) { pcap_lookupnet(pcap_device, &pc.netp, &pc.netmask, pc.pcap_err_buf); pt_log(kLog_verbose, "Network: %s\n", inet_ntoa(*(struct in_addr*)&pc.netp)); pt_log(kLog_verbose, "Netmask: %s\n", inet_ntoa(*(struct in_addr*)&pc.netmask)); if (pcap_compile(pc.pcap_desc, &pc.fp, pcap_filter_program, 0, pc.netp) == -1) { pt_log(kLog_error, "Failed to compile pcap filter program.\n"); pcap_close(pc.pcap_desc); pcap = 0; } else if (pcap_setfilter(pc.pcap_desc, &pc.fp) == -1) { pt_log(kLog_error, "Failed to set pcap filter program.\n"); pcap_close(pc.pcap_desc); pcap = 0; } } pc.pkt_q.head = 0; pc.pkt_q.tail = 0; pc.pkt_q.elems = 0; // Check if we have succeeded, and free stuff if not if (!pcap) { pt_log(kLog_error, "There were errors enabling pcap - pcap has been disabled.\n"); free(pc.pcap_err_buf); free(pc.pcap_data_buf); } } else pt_log(kLog_info, "pcap disabled since we're running in unprivileged mode.\n"); } pthread_mutex_lock(&num_threads_lock); num_threads++; pthread_mutex_unlock(&num_threads_lock); // Allocate icmp receive buffer buf = malloc(icmp_receive_buf_len); // Start forwarding :) pt_log(kLog_info, "Ping proxy is listening in %s mode.\n", (unprivileged ? "unprivileged" : "privileged")); #ifndef WIN32 #ifdef HAVE_SELINUX if (uid || gid || selinux_context) #else if (uid || gid) #endif pt_log(kLog_info, "Dropping privileges now.\n"); if (gid && -1 == setgid(gid)) pt_log(kLog_error, "setgid(%d): %s\n", gid, strerror(errno)); if (uid && -1 == setuid(uid)) pt_log(kLog_error, "setuid(%d): %s\n", uid, strerror(errno)); #ifdef HAVE_SELINUX if (NULL != selinux_context && -1 == setcon(selinux_context)) pt_log(kLog_error, "setcon(%s) failed: %s\n", selinux_context, strerror(errno)); #endif #endif while (1) { FD_ZERO(&set); FD_SET(fwd_sock, &set); max_sock = fwd_sock+1; pthread_mutex_lock(&chain_lock); for (cur=chain;cur;cur=cur->next) { if (cur->sock) { FD_SET(cur->sock, &set); if (cur->sock >= max_sock) max_sock = cur->sock+1; } } pthread_mutex_unlock(&chain_lock); timeout.tv_sec = 0; timeout.tv_usec = 10000; select(max_sock, &set, 0, 0, &timeout); // Don't care about return val, since we need to check for new states anyway.. pthread_mutex_lock(&chain_lock); for (prev=0,cur=chain;cur && cur->sock;cur=tmp) { // Client: If we're starting up, send a message to the remote end saying so, // causing him to connect to our desired endpoint. if (cur->state == kProxy_start) { pt_log(kLog_verbose, "Sending proxy request.\n"); cur->last_ack = time_as_double(); queue_packet(fwd_sock, cur->pkt_type, 0, 0, cur->id_no, cur->id_no, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, cur->dst_ip, cur->dst_port, cur->state | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); cur->xfer.icmp_out++; cur->state = kProto_data; } if (cur->should_remove) { pt_log(kLog_info, "\nSession statistics:\n"); print_statistics(&cur->xfer, 0); pt_log(kLog_info, "\n"); tmp = cur->next; remove_proxy_desc(cur, prev); continue; } // Only handle traffic if there is traffic on the socket, we have // room in our send window AND we either don't use a password, or // have been authenticated. if (FD_ISSET(cur->sock, &set) && cur->send_wait_ack < kPing_window_size && (!password || cur->authenticated || cur->type_flag == kUser_flag)) { bytes = recv(cur->sock, cur->buf, tcp_receive_buf_len, 0); if (bytes <= 0) { pt_log(kLog_info, "Connection closed or lost.\n"); tmp = cur->next; send_termination_msg(cur, fwd_sock); pt_log(kLog_info, "Session statistics:\n"); print_statistics(&cur->xfer, 0); remove_proxy_desc(cur, prev); // No need to update prev continue; } cur->xfer.bytes_out += bytes; cur->xfer.icmp_out++; queue_packet(fwd_sock, cur->pkt_type, cur->buf, bytes, cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, 0, 0, cur->state | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); } prev = cur; tmp = cur->next; } pthread_mutex_unlock(&chain_lock); if (FD_ISSET(fwd_sock, &set)) { // Handle ping traffic addr_len = sizeof(struct sockaddr); bytes = recvfrom(fwd_sock, buf, icmp_receive_buf_len, 0, (struct sockaddr*)&addr, &addr_len); if (bytes < 0) { pt_log(kLog_error, "Error receiving packet on ICMP socket: %s\n", strerror(errno)); break; } handle_packet(buf, bytes, 0, &addr, fwd_sock); } // Check for packets needing resend, and figure out if any connections // should be closed down due to inactivity. pthread_mutex_lock(&chain_lock); now = time_as_double(); for (cur=chain;cur;cur=cur->next) { if (cur->last_activity + kAutomatic_close_timeout < now) { pt_log(kLog_info, "Dropping tunnel to %s:%d due to inactivity.\n", inet_ntoa(*(struct in_addr*)&cur->dst_ip), cur->dst_port, cur->id_no); cur->should_remove = 1; continue; } if (cur->recv_wait_send && cur->sock) cur->xfer.bytes_in += send_packets(cur->recv_ring, &cur->recv_xfer_idx, &cur->recv_wait_send, &cur->sock); // Check for any icmp packets requiring resend, and resend _only_ the first packet. idx = cur->send_first_ack; if (cur->send_ring[idx].pkt && cur->send_ring[idx].last_resend+kResend_interval < now) { pt_log(kLog_debug, "Resending packet with seq-no %d.\n", cur->send_ring[idx].seq_no); cur->send_ring[idx].last_resend = now; cur->send_ring[idx].pkt->seq = htons(cur->ping_seq); cur->ping_seq++; cur->send_ring[idx].pkt->checksum = 0; cur->send_ring[idx].pkt->checksum = htons(calc_icmp_checksum((uint16_t*)cur->send_ring[idx].pkt, cur->send_ring[idx].pkt_len)); //printf("ID: %d\n", htons(cur->send_ring[idx].pkt->identifier)); sendto(fwd_sock, (const void*)cur->send_ring[idx].pkt, cur->send_ring[idx].pkt_len, 0, (struct sockaddr*)&cur->dest_addr, sizeof(struct sockaddr)); cur->xfer.icmp_resent++; } // Figure out if it's time to send an explicit acknowledgement if (cur->last_ack+1.0 < now && cur->send_wait_ack < kPing_window_size && cur->remote_ack_val+1 != cur->next_remote_seq) { cur->last_ack = now; queue_packet(fwd_sock, cur->pkt_type, 0, 0, cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, cur->dst_ip, cur->dst_port, kProto_ack | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); cur->xfer.icmp_ack_out++; } } pthread_mutex_unlock(&chain_lock); if (pcap) { if (pcap_dispatch(pc.pcap_desc, 32, pcap_packet_handler, (u_char*)&pc.pkt_q) > 0) { pqueue_elem_t *cur; //pt_log(kLog_verbose, "pcap captured %d packets - handling them..\n", pc.pkt_q.elems); while (pc.pkt_q.head) { cur = pc.pkt_q.head; memset(&addr, sizeof(struct sockaddr), 0); addr.sin_family = AF_INET; addr.sin_addr.s_addr = *(in_addr_t*)&(((ip_packet_t*)(cur->data))->src_ip); handle_packet(cur->data, cur->bytes, 1, &addr, fwd_sock); pc.pkt_q.head = cur->next; free(cur); pc.pkt_q.elems--; } pc.pkt_q.tail = 0; pc.pkt_q.head = 0; } } // Update running statistics, if requested (only once every second) if (print_stats && mode == kMode_forward && now > last_status_update+1) { pthread_mutex_lock(&chain_lock); memset(&xfer, 0, sizeof(xfer_stats_t)); for (cur=chain;cur;cur=cur->next) { xfer.bytes_in += cur->xfer.bytes_in; xfer.bytes_out += cur->xfer.bytes_out; xfer.icmp_in += cur->xfer.icmp_in; xfer.icmp_out += cur->xfer.icmp_out; xfer.icmp_resent += cur->xfer.icmp_resent; } pthread_mutex_unlock(&chain_lock); print_statistics(&xfer, 1); last_status_update = now; } } pt_log(kLog_debug, "Proxy exiting..\n"); if (fwd_sock) close(fwd_sock); // TODO: Clean up the other descs. Not really a priority since there's no // real way to quit ptunnel in the first place.. free(buf); pt_log(kLog_debug, "Ping proxy done\n"); return 0; } /* print_statistics: Prints transfer statistics for the given xfer block. The is_continuous variable controls the output mode, either printing a new line or overwriting the old line. */ void print_statistics(xfer_stats_t *xfer, int is_continuous) { const double mb = 1024.0*1024.0; double loss = 0.0; if (xfer->icmp_out > 0) loss = (double)xfer->icmp_resent/(double)xfer->icmp_out; if (is_continuous) printf("\r"); printf("[inf]: I/O: %6.2f/%6.2f mb ICMP I/O/R: %8d/%8d/%8d Loss: %4.1f%%", xfer->bytes_in/mb, xfer->bytes_out/mb, xfer->icmp_in, xfer->icmp_out, xfer->icmp_resent, loss); if (!is_continuous) printf("\n"); else fflush(stdout); } /* pcap_packet_handler: This is our callback function handling captured packets. We already know that the packets are ICMP echo or echo-reply messages, so all we need to do is strip off the ethernet header and append it to the queue descriptor (the refcon argument). Ok, the above isn't entirely correct (we can get other ICMP types as well). This function also has problems when it captures packets on the loopback interface. The moral of the story: Don't do ping forwarding over the loopback interface. Also, we currently don't support anything else than ethernet when in pcap mode. The reason is that I haven't read up on yet on how to remove the frame header from the packet.. */ void pcap_packet_handler(u_char *refcon, const struct pcap_pkthdr *hdr, const u_char* pkt) { pqueue_t *q; pqueue_elem_t *elem; ip_packet_t *ip; //pt_log(kLog_verbose, "Packet handler: %d =? %d\n", hdr->caplen, hdr->len); q = (pqueue_t*)refcon; elem = malloc(sizeof(pqueue_elem_t)+hdr->caplen-sizeof(struct ether_header)); memcpy(elem->data, pkt+sizeof(struct ether_header), hdr->caplen-sizeof(struct ether_header)); ip = (ip_packet_t*)elem->data; // TODO: Add fragment support elem->bytes = ntohs(ip->pkt_len); if (elem->bytes > hdr->caplen-sizeof(struct ether_header)) { pt_log(kLog_error, "Received fragmented packet - unable to reconstruct!\n"); pt_log(kLog_error, "This error usually occurs because pcap is used on devices that are not wlan or ethernet.\n"); free(elem); return; } //elem->bytes = hdr->caplen-sizeof(struct ether_header); elem->next = 0; if (q->tail) { q->tail->next = elem; q->tail = elem; } else { q->head = elem; q->tail = elem; } q->elems++; } /* handle_proxy_packet: Processes incoming ICMP packets for the proxy. The packet can come either from the packet capture lib, or from the actual socket or both. Input: A buffer pointing at the start of an IP header, the buffer length and the proxy descriptor chain. */ void handle_packet(char *buf, int bytes, int is_pcap, struct sockaddr_in *addr, int icmp_sock) { ip_packet_t *ip_pkt; icmp_echo_packet_t *pkt; ping_tunnel_pkt_t *pt_pkt; proxy_desc_t *cur; uint32_t type_flag, pkt_flag, init_state; challenge_t *challenge; struct timeval tt; if (bytes < sizeof(icmp_echo_packet_t)+sizeof(ping_tunnel_pkt_t)) pt_log(kLog_verbose, "Skipping this packet - too short. Expect: %d+%d = %d ; Got: %d\n", sizeof(icmp_echo_packet_t), sizeof(ping_tunnel_pkt_t), sizeof(icmp_echo_packet_t)+sizeof(ping_tunnel_pkt_t), bytes); else { if (use_udp) { ip_pkt = 0; pkt = (icmp_echo_packet_t*)buf; pt_pkt = (ping_tunnel_pkt_t*)pkt->data; } else { ip_pkt = (ip_packet_t*)buf; pkt = (icmp_echo_packet_t*)ip_pkt->data; pt_pkt = (ping_tunnel_pkt_t*)pkt->data; } if (ntohl(pt_pkt->magic) == kPing_tunnel_magic) { pt_pkt->state = ntohl(pt_pkt->state); pkt->identifier = ntohs(pkt->identifier); pt_pkt->id_no = ntohs(pt_pkt->id_no); pt_pkt->seq_no = ntohs(pt_pkt->seq_no); // Find the relevant connection, if it exists pthread_mutex_lock(&chain_lock); for (cur=chain;cur;cur=cur->next) { if (cur->id_no == pt_pkt->id_no) break; } pthread_mutex_unlock(&chain_lock); /* Handle the packet if it comes from "the other end." This is a bit tricky to get right, since we receive both our own and the other end's packets. Basically, a proxy will accept any packet from a user, regardless if it has a valid connection or not. A user will only accept the packet if there exists a connection to handle it. */ if (cur) { type_flag = cur->type_flag; if (type_flag == kProxy_flag) cur->icmp_id = pkt->identifier; if (!is_pcap) cur->xfer.icmp_in++; } else type_flag = kProxy_flag; pkt_flag = pt_pkt->state & kFlag_mask; pt_pkt->state &= ~kFlag_mask; pt_log(kLog_sendrecv, "Recv: %d [%d] bytes [seq = %d] [type = %s] [ack = %d] [icmp = %d] [user = %s] [pcap = %d]\n", bytes, ntohl(pt_pkt->data_len), pt_pkt->seq_no, state_name[pt_pkt->state & (~kFlag_mask)], ntohl(pt_pkt->ack), pkt->type, (pkt_flag == kUser_flag ? "yes" : "no"), is_pcap); // This test essentially verifies that the packet comes from someone who isn't us. if ((pkt_flag == kUser_flag && type_flag == kProxy_flag) || (pkt_flag == kProxy_flag && type_flag == kUser_flag)) { pt_pkt->data_len = ntohl(pt_pkt->data_len); pt_pkt->ack = ntohl(pt_pkt->ack); if (pt_pkt->state == kProxy_start) { if (!cur && type_flag == kProxy_flag) { pt_log(kLog_info, "Incoming tunnel request from %s.\n", inet_ntoa(*(struct in_addr*)&addr->sin_addr)); gettimeofday(&tt, 0); if (tt.tv_sec < seq_expiry_tbl[pt_pkt->id_no]) { pt_log(kLog_verbose, "Dropping request: ID was recently in use.\n"); return; } pt_log(kLog_info, "Starting new session to %s:%d with ID %d\n", inet_ntoa(*(struct in_addr*)&pt_pkt->dst_ip), ntohl(pt_pkt->dst_port), pt_pkt->id_no); if ((given_dst_ip && given_dst_ip != pt_pkt->dst_ip) || (-1 != tcp_port && tcp_port != ntohl(pt_pkt->dst_port))) { pt_log(kLog_info, "Destination administratively prohibited!\n"); return; } if (password) init_state = kProto_authenticate; else init_state = kProto_data; cur = 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 (init_state == kProto_authenticate) { pt_log(kLog_debug, "Sending authentication challenge..\n"); // Send challenge cur->challenge = generate_challenge(); memcpy(cur->buf, cur->challenge, sizeof(challenge_t)); queue_packet(icmp_sock, cur->pkt_type, cur->buf, sizeof(challenge_t), cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, 0, 0, kProto_authenticate | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); } } else if (type_flag == kUser_flag) { pt_log(kLog_error, "Dropping proxy session request - we are not a proxy!\n"); return; } else pt_log(kLog_error, "Dropping duplicate proxy session request.\n"); } else if (cur && pt_pkt->state == kProto_authenticate) { // Sanity check packet length, and make sure it matches what we expect if (pt_pkt->data_len != sizeof(challenge_t)) { pt_log(kLog_error, "Received challenge packet, but data length is not as expected.\n"); pt_log(kLog_debug, "Data length: %d Expected: %d\n", pt_pkt->data_len, sizeof(challenge_t)); cur->should_remove = 1; return; } // Prevent packet data from being forwarded over TCP! pt_pkt->data_len = 0; challenge = (challenge_t*)pt_pkt->data; // If client: Compute response to challenge if (type_flag == kUser_flag) { if (!password) { pt_log(kLog_error, "This proxy requires a password! Please supply one using the -x switch.\n"); send_termination_msg(cur, icmp_sock); cur->should_remove = 1; return; } pt_log(kLog_debug, "Got authentication challenge - sending response\n"); generate_response(challenge); queue_packet(icmp_sock, cur->pkt_type, (char*)challenge, sizeof(challenge_t), cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, 0, 0, kProto_authenticate | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); // We have authenticated locally. It's up to the proxy now if it accepts our response or not.. cur->authenticated = 1; handle_data(pkt, bytes, cur->recv_ring, &cur->recv_wait_send, &cur->recv_idx, &cur->next_remote_seq); return; } // If proxy: Handle client's response to challenge else if (type_flag == kProxy_flag) { pt_log(kLog_debug, "Received remote challenge response.\n"); if (validate_challenge(cur->challenge, challenge) || cur->authenticated) { pt_log(kLog_verbose, "Remote end authenticated successfully.\n"); // Authentication has succeeded, so now we can proceed to handle incoming TCP data. cur->authenticated = 1; cur->state = kProto_data; // Insert the packet into the receive ring, to avoid confusing the reliability mechanism. handle_data(pkt, bytes, cur->recv_ring, &cur->recv_wait_send, &cur->recv_idx, &cur->next_remote_seq); } else { pt_log(kLog_info, "Remote end failed authentication.\n"); send_termination_msg(cur, icmp_sock); cur->should_remove = 1; } return; } } // Handle close-messages for connections we know about if (cur && pt_pkt->state == kProto_close) { pt_log(kLog_info, "Received session close from remote peer.\n"); cur->should_remove = 1; return; } // The proxy will ignore any other packets from the client // until it has been authenticated. The packet resend mechanism // insures that this isn't problematic. if (type_flag == kProxy_flag && password && cur && !cur->authenticated) { pt_log(kLog_debug, "Ignoring packet with seq-no %d - not authenticated yet.\n", pt_pkt->seq_no); return; } if (cur && cur->sock) { if (pt_pkt->state == kProto_data || pt_pkt->state == kProxy_start || pt_pkt->state == kProto_ack) handle_data(pkt, bytes, cur->recv_ring, &cur->recv_wait_send, &cur->recv_idx, &cur->next_remote_seq); handle_ack((uint16_t)pt_pkt->ack, cur->send_ring, &cur->send_wait_ack, 0, cur->send_idx, &cur->send_first_ack, &cur->remote_ack_val, is_pcap); cur->last_activity = time_as_double(); } } } else pt_log(kLog_verbose, "Ignored incoming packet.\n"); } } /* create_and_insert_proxy_desc: Creates a new proxy descriptor, linking it into 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, int sock, struct sockaddr_in *addr, uint32_t dst_ip, uint32_t dst_port, uint32_t init_state, uint32_t type) { proxy_desc_t *cur; pthread_mutex_lock(&chain_lock); if (num_tunnels >= max_tunnels) { pt_log(kLog_info, "Discarding incoming connection - too many tunnels! Maximum count is %d (adjust with the -m switch).\n", max_tunnels); if (sock) close(sock); pthread_mutex_unlock(&chain_lock); return 0; } num_tunnels++; pthread_mutex_unlock(&chain_lock); pt_log(kLog_debug, "Adding proxy desc to run loop. Type is %s. Will create socket: %s\n", (type == kUser_flag ? "user" : "proxy"), (sock ? "No" : "Yes")); cur = calloc(1, sizeof(proxy_desc_t)); cur->id_no = id_no; cur->dest_addr = *addr; cur->dst_ip = dst_ip; cur->dst_port = dst_port; cur->icmp_id = icmp_id; if (!sock) { cur->sock = socket(AF_INET, SOCK_STREAM, 0); memset(addr, 0, sizeof(struct sockaddr_in)); addr->sin_port = htons((uint16_t)dst_port); addr->sin_addr.s_addr = dst_ip; addr->sin_family = AF_INET; // Let's just assume success, shall we? if (connect(cur->sock, (struct sockaddr*)addr, sizeof(struct sockaddr_in)) < 0) { pt_log(kLog_error, "Connect to %s:%d failed: %s\n", inet_ntoa(*(struct in_addr*)&addr->sin_addr.s_addr), ntohs(addr->sin_port), strerror(errno)); } } else cur->sock = sock; cur->state = init_state; cur->type_flag = type; if (cur->type_flag == kUser_flag) cur->pkt_type = 8; else cur->pkt_type = (unprivileged ? 8 : 0); cur->buf = malloc(icmp_receive_buf_len); cur->last_activity = time_as_double(); cur->authenticated = 0; pthread_mutex_lock(&chain_lock); cur->next = chain; chain = cur; pthread_mutex_unlock(&chain_lock); cur->xfer.bytes_in = 0.0; cur->xfer.bytes_out = 0.0; return cur; } /* remove_proxy_desc: Removes the given proxy desc, freeing its resources. Assumes that we hold the chain_lock. */ void remove_proxy_desc(proxy_desc_t *cur, proxy_desc_t *prev) { int i; struct timeval tt; pt_log(kLog_debug, "Removing proxy descriptor.\n"); // Get a timestamp, for making an entry in the seq_expiry_tbl gettimeofday(&tt, 0); seq_expiry_tbl[cur->id_no] = tt.tv_sec+(2*kAutomatic_close_timeout); // Free resources associated with connection if (cur->buf) free(cur->buf); cur->buf = 0; for (i=0;isend_ring[i].pkt) free(cur->send_ring[i].pkt); cur->send_ring[i].pkt = 0; if (cur->recv_ring[i]) free(cur->recv_ring[i]); cur->recv_ring[i] = 0; } close(cur->sock); cur->sock = 0; // Keep list up-to-date if (prev) prev->next = cur->next; else chain = cur->next; if (cur->challenge) free(cur->challenge); free(cur); num_tunnels--; } #if kPT_add_iphdr static int ip_id_counter = 1; #endif /* queue_packet: Creates an ICMP packet descriptor, and sends it. The packet descriptor is added to the given send ring, for potential resends later on. */ int queue_packet(int icmp_sock, uint8_t type, char *buf, int num_bytes, uint16_t id_no, uint16_t icmp_id, uint16_t *seq, icmp_desc_t ring[], int *insert_idx, int *await_send, uint32_t ip, uint32_t port, uint32_t state, struct sockaddr_in *dest_addr, uint16_t next_expected_seq, int *first_ack, uint16_t *ping_seq) { #if kPT_add_iphdr ip_packet_t *ip_pkt = 0; int pkt_len = sizeof(ip_packet_t)+sizeof(icmp_echo_packet_t)+sizeof(ping_tunnel_pkt_t)+num_bytes, #else int pkt_len = sizeof(icmp_echo_packet_t)+sizeof(ping_tunnel_pkt_t)+num_bytes, #endif err = 0; icmp_echo_packet_t *pkt = 0; ping_tunnel_pkt_t *pt_pkt = 0; uint16_t ack_val = next_expected_seq-1; if (pkt_len % 2) pkt_len++; #if kPT_add_iphdr printf("add header\n"); ip_pkt = malloc(pkt_len); pkt = (icmp_echo_packet_t*)ip_pkt->data; memset(ip_pkt, 0, sizeof(ip_packet_t)); ip_pkt->vers_ihl = 0x45;//|(pkt_len>>2);//5;//(IPVERSION << 4) | (sizeof(ip_packet_t) >> 2); ip_pkt->tos = IPTOS_LOWDELAY; ip_pkt->pkt_len = pkt_len; ip_pkt->id = 0; //kernel sets proper value htons(ip_id_counter); ip_pkt->flags_frag_offset = 0; ip_pkt->ttl = IPDEFTTL; // default time to live (64) ip_pkt->proto = 1; // ICMP ip_pkt->checksum = 0; // maybe the kernel helps us out..? ip_pkt->src_ip = htonl(0x0); // insert source IP address here ip_pkt->dst_ip = dest_addr->sin_addr.s_addr;//htonl(0x7f000001); // localhost.. #else pkt = malloc(pkt_len); #endif pkt->type = type; // ICMP Echo request or reply pkt->code = 0; // Must be zero (non-zero requires root) pkt->identifier = htons(icmp_id); pkt->seq = htons(*ping_seq); pkt->checksum = 0; (*ping_seq)++; // Add our information pt_pkt = (ping_tunnel_pkt_t*)pkt->data; pt_pkt->magic = htonl(kPing_tunnel_magic); pt_pkt->dst_ip = ip; pt_pkt->dst_port = htonl(port); pt_pkt->ack = htonl(ack_val); pt_pkt->data_len = htonl(num_bytes); pt_pkt->state = htonl(state); pt_pkt->seq_no = htons(*seq); pt_pkt->id_no = htons(id_no); // Copy user data if (buf && num_bytes > 0) memcpy(pt_pkt->data, buf, num_bytes); #if kPT_add_iphdr pkt->checksum = htons(calc_icmp_checksum((uint16_t*)pkt, pkt_len-sizeof(ip_packet_t))); ip_pkt->checksum = htons(calc_icmp_checksum((uint16_t*)ip_pkt, sizeof(ip_packet_t))); #else pkt->checksum = htons(calc_icmp_checksum((uint16_t*)pkt, pkt_len)); #endif // Send it! pt_log(kLog_sendrecv, "Send: %d [%d] bytes [seq = %d] [type = %s] [ack = %d] [icmp = %d] [user = %s]\n", pkt_len, num_bytes, *seq, state_name[state & (~kFlag_mask)], ack_val, type, ((state & kUser_flag) == kUser_flag ? "yes" : "no")); #if kPT_add_iphdr err = sendto(icmp_sock, (const void*)ip_pkt, pkt_len, 0, (struct sockaddr*)dest_addr, sizeof(struct sockaddr)); #else err = sendto(icmp_sock, (const void*)pkt, pkt_len, 0, (struct sockaddr*)dest_addr, sizeof(struct sockaddr)); #endif if (err < 0) { pt_log(kLog_error, "Failed to send ICMP packet: %s\n", strerror(errno)); return -1; } else if (err != pkt_len) pt_log(kLog_error, "WARNING WARNING, didn't send entire packet\n"); // Update sequence no's and so on #if kPT_add_iphdr // NOTE: Retry mechanism needs update for PT_add_ip_hdr ring[*insert_idx].pkt = ip_pkt; #else ring[*insert_idx].pkt = pkt; #endif ring[*insert_idx].pkt_len = pkt_len; ring[*insert_idx].last_resend = time_as_double(); ring[*insert_idx].seq_no = *seq; ring[*insert_idx].icmp_id = icmp_id; (*seq)++; if (!ring[*first_ack].pkt) *first_ack = *insert_idx; (*await_send)++; (*insert_idx)++; if (*insert_idx >= kPing_window_size) *insert_idx = 0; return 0; } /* send_packets: Examines the passed-in ring, and forwards data in it over TCP. */ uint32_t send_packets(forward_desc_t *ring[], int *xfer_idx, int *await_send, int *sock) { forward_desc_t *fwd_desc; int bytes, total = 0; while (*await_send > 0) { fwd_desc = ring[*xfer_idx]; if (!fwd_desc) // We haven't got this packet yet.. break; if (fwd_desc->length > 0) { bytes = send(*sock, &fwd_desc->data[fwd_desc->length - fwd_desc->remaining], fwd_desc->remaining, 0); if (bytes < 0) { printf("Weirdness.\n"); // TODO: send close stuff close(*sock); *sock = 0; break; } fwd_desc->remaining -= bytes; total += bytes; } if (!fwd_desc->remaining) { ring[*xfer_idx] = 0; free(fwd_desc); (*xfer_idx)++; (*await_send)--; if (*xfer_idx >= kPing_window_size) *xfer_idx = 0; } else break; } return total; } /* handle_data: Utility function for handling kProto_data packets, and place the data it contains onto the passed-in receive ring. */ void handle_data(icmp_echo_packet_t *pkt, int total_len, forward_desc_t *ring[], int *await_send, int *insert_idx, uint16_t *next_expected_seq) { ping_tunnel_pkt_t *pt_pkt = (ping_tunnel_pkt_t*)pkt->data; int expected_len = sizeof(ip_packet_t) + sizeof(icmp_echo_packet_t) + sizeof(ping_tunnel_pkt_t); // 20+8+28 /* Place packet in the receive ring, in its proper place. This works as follows: -1. Packet == ack packet? Perform ack, and continue. 0. seq_no < next_remote_seq, and absolute difference is bigger than w size => discard 1. If seq_no == next_remote_seq, we have no problems; just put it in the ring. 2. If seq_no > next_remote_seq + remaining window size, discard packet. Send resend request for missing packets. 3. Else, put packet in the proper place in the ring (don't overwrite if one is already there), but don't increment next_remote_seq_no 4. If packed was not discarded, process ack info in packet. */ expected_len += pt_pkt->data_len; expected_len += expected_len % 2; if (use_udp) expected_len -= sizeof(ip_packet_t); if (total_len < expected_len) { pt_log(kLog_error, "Packet not completely received: %d Should be: %d. For some reason, this error is fatal.\n", total_len, expected_len); pt_log(kLog_debug, "Data length: %d Total length: %d\n", pt_pkt->data_len, total_len); // TODO: This error isn't fatal, so it should definitely be handled in some way. We could simply discard it. exit(0); } if (pt_pkt->seq_no == *next_expected_seq) { // hmm, what happens if this test is true? if (!ring[*insert_idx]) { // && pt_pkt->state == kProto_data // pt_log(kLog_debug, "Queing data packet: %d\n", pt_pkt->seq_no); ring[*insert_idx] = create_fwd_desc(pt_pkt->seq_no, pt_pkt->data_len, pt_pkt->data); (*await_send)++; (*insert_idx)++; } else if (ring[*insert_idx]) pt_log(kLog_debug, "Dup packet?\n"); (*next_expected_seq)++; if (*insert_idx >= kPing_window_size) *insert_idx = 0; // Check if we have already received some of the next packets while (ring[*insert_idx]) { if (ring[*insert_idx]->seq_no == *next_expected_seq) { (*next_expected_seq)++; (*insert_idx)++; if (*insert_idx >= kPing_window_size) *insert_idx = 0; } else break; } } else { int r, s, d, pos; pos = -1; // If pos ends up staying -1, packet is discarded. r = *next_expected_seq; s = pt_pkt->seq_no; d = s - r; if (d < 0) { // This packet _may_ be old, or seq_no may have wrapped around d = (s+0xFFFF) - r; if (d < kPing_window_size) { // Counter has wrapped, so we should add this packet to the recv ring pos = ((*insert_idx)+d) % kPing_window_size; } } else if (d < kPing_window_size) pos = ((*insert_idx)+d) % kPing_window_size; if (pos != -1) { if (!ring[pos]) { pt_log(kLog_verbose, "Out of order. Expected: %d Got: %d Inserted: %d (cur = %d)\n", *next_expected_seq, pt_pkt->seq_no, pos, (*insert_idx)); ring[pos] = create_fwd_desc(pt_pkt->seq_no, pt_pkt->data_len, pt_pkt->data); (*await_send)++; } } //else // pt_log(kLog_debug, "Packet discarded - outside receive window.\n"); } } void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, int one_ack_only, int insert_idx, int *first_ack, uint16_t *remote_ack, int is_pcap) { int i, j, k; ping_tunnel_pkt_t *pt_pkt; if (*packets_awaiting_ack > 0) { if (one_ack_only) { for (i=0;idata; *remote_ack = (uint16_t)ntohl(pt_pkt->ack); // WARNING: We make the dangerous assumption here that packets arrive in order! free(ring[i].pkt); ring[i].pkt = 0; (*packets_awaiting_ack)--; if (i == *first_ack) { for (j=1;jseq_no = seq_no; fwd_desc->length = data_len; fwd_desc->remaining = data_len; if (data_len > 0) memcpy(fwd_desc->data, data, data_len); return fwd_desc; } uint16_t calc_icmp_checksum(uint16_t *data, int bytes) { uint32_t sum; int i; sum = 0; for (i=0;i> 16); sum = htons(0xFFFF - sum); return sum; } /* generate_challenge: Generates a random challenge, incorporating the current local timestamp to avoid replay attacks. */ challenge_t* generate_challenge(void) { struct timeval tt; challenge_t *c; int i; c = calloc(1, sizeof(challenge_t)); gettimeofday(&tt, 0); c->sec = tt.tv_sec; c->usec_rnd = tt.tv_usec + rand(); for (i=0;i<6;i++) c->random[i] = rand(); return c; } /* generate_response: Generates a response to the given challenge. The response is generated by combining the concatenating the challenge data with the md5 digest of the password, and then calculating the MD5 digest of the entire buffer. The result is stored in the passed-in challenge, overwriting the challenge data. */ void generate_response(challenge_t *challenge) { md5_byte_t *buf; md5_state_t state; buf = malloc(sizeof(challenge_t)+kMD5_digest_size); memcpy(buf, challenge, sizeof(challenge_t)); memcpy(&buf[sizeof(challenge_t)], password_digest, kMD5_digest_size); memset(challenge, 0, sizeof(challenge_t)); md5_init(&state); md5_append(&state, buf, sizeof(challenge_t)+kMD5_digest_size); md5_finish(&state, (md5_byte_t*)challenge); } /* validate_challenge: Checks whether a given response matches the expected response, returning 1 if validation succeeded, and 0 otherwise. Note that overwriting the local challenge with the challenge result is not a problem, as the data will not be used again anyway (authentication either succeeds, or the connection is closed down). */ int validate_challenge(challenge_t *local, challenge_t *remote) { generate_response(local); if (memcmp(local, remote, sizeof(challenge_t)) == 0) return 1; return 0; } /* send_termination_msg: Sends two packets to the remote end, informing it that the tunnel is being closed down. */ void send_termination_msg(proxy_desc_t *cur, int icmp_sock) { // Send packet twice, hoping at least one of them makes it through.. queue_packet(icmp_sock, cur->pkt_type, 0, 0, cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, 0, 0, kProto_close | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); queue_packet(icmp_sock, cur->pkt_type, 0, 0, cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, &cur->send_wait_ack, 0, 0, kProto_close | cur->type_flag, &cur->dest_addr, cur->next_remote_seq, &cur->send_first_ack, &cur->ping_seq); cur->xfer.icmp_out += 2; } void pt_log(int level, char *fmt, ...) { va_list args; const char *header[] = { "[err]: ", "[inf]: ", "[evt]: ", "[vbs]: ", "[dbg]: ", "[xfr]: " }; #ifndef WIN32 int syslog_levels[] = {LOG_ERR, LOG_NOTICE, LOG_NOTICE, LOG_INFO, LOG_DEBUG, LOG_DEBUG}; #endif /* !WIN32 */ if (level <= log_level) { va_start(args, fmt); #ifndef WIN32 if (use_syslog) { char log[255]; int header_len; header_len = snprintf(log,sizeof(log),"%s",header[level]); vsnprintf(log+header_len,sizeof(log)-header_len,fmt,args); syslog(syslog_levels[level], "%s", log); } else #endif /* !WIN32 */ fprintf(log_file, "%s", header[level]), vfprintf(log_file, fmt, args); va_end(args); #ifndef WIN32 if (log_file != stdout && !use_syslog) #else if (log_file != stdout) #endif fflush(log_file); } } double time_as_double(void) { double result; struct timeval tt; gettimeofday(&tt, 0); result = (double)tt.tv_sec + ((double)tt.tv_usec / (double)10e5); return result; } PingTunnel/ptunnel.h0000700000076500007650000003535711217641661014644 0ustar danielsdaniels/* ptunnel.h ptunnel is licensed under the BSD license: Copyright (c) 2004-2009, Daniel Stoedle , Yellow Lemon Software. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Yellow Lemon Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Contacting the author: You can get in touch with me, Daniel Stdle (that's the Norwegian letter oe, in case your text editor didn't realize), here: The official ptunnel website is here: Note that the source code is best viewed with tabs set to 4 spaces. */ #ifndef PING_TUNNEL_H #define PING_TUNNEL_H // Includes #ifndef WIN32 #include #include #include #include #include #include #include #include #include #include #include #include #endif /* !WIN32 */ #include #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include typedef int socklen_t; typedef uint32_t in_addr_t; #define ETH_ALEN 6 /* Octets in one ethernet addr */ struct ether_header { u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */ u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */ u_int16_t ether_type; /* packet type ID field */ }; #endif /* WIN32 */ // Constants #define false 0 #define true 1 #define bool char enum { kOpt_undefined = 0, // Constants for parsing options kOpt_set_proxy_addr, kOpt_set_mode, kOpt_set_password, kOpt_set_tcp_port, kOpt_set_tcp_dest_addr, kOpt_set_tcp_dest_port, kOpt_set_verbosity, kOpt_set_max_tunnels, kOpt_set_non_privileged, kOpt_set_pcap_device, kOpt_set_log_file, kOpt_set_unpriv_user, kOpt_set_unpriv_group, kOpt_set_root_dir, kOpt_set_selinux_context, kOpt_daemonize, kMode_forward = 0, // Ping tunnel's operating mode (client or kMode_proxy, // proxy) kMax_tunnels = 10,/* Set this constant to the number of concurrent connections you wish to handle by default. */ kNo_log = -1, // Different verbosity levels. kLog_error = 0, kLog_info, kLog_event, kLog_verbose, kLog_debug, kLog_sendrecv, kMajor_version = 0, // Major (0.xx) and minor (x.70) version kMinor_version = 71, // numbers. kIP_packet_max_size = 576, kIP_header_size = 20, // In bytes, mind you kIP_actual_size = (kIP_packet_max_size - kIP_header_size) - ((kIP_packet_max_size - kIP_header_size) % 8), kICMP_header_size = 8, // Also in bytes kDefault_buf_size = 1024, /* This constant control the maximum size of the payload-portion of the ICMP packets we send. Note that this does not include the IP or ICMP headers! */ kICMP_echo_request = 8, // Type code for echo request and replies kICMP_echo_reply = 0, kPing_window_size = 64, // number of packets we can have in our send/receive ring /* Tunnels are automatically closed after one minute of inactivity. Since we continously send acknowledgements between the two peers, this mechanism won't disconnect "valid" connections. */ kAutomatic_close_timeout = 60, // Seconds! kMD5_digest_size = 16, // size of md5 digest in bytes /* These constants are used to indicate the protocol state. The protocol works as follows: - The identifier is used by both the proxy and the forwarder to identify the session (and thus the relevant sockets). - The seq-no of the ping packet is used in a sliding-window-esque way, and to identify the order of data. The protocol can be in any of the following states: kProxy_start Causes the proxy to open a connection to the given host and port, associating the ID with the socket, before the data on the socket are transmitted. kProxy_data Indicates that the packet contains data from the proxy. Data ordering is indicated by the seq-no, which will start at 0. (The proxy and forwarder maintain different seq-nos.) kUser_data This packet contains user data. kConnection_close Indicates that the connection is being closed. kProxy_ack and Acknowledges the packet (and all packets before it) with seq_no = ack. kUser_ack This is used if there are no implicit acknowledgements due to data being sent. Acknowledgements work by the remote peer acknowledging the last continuous seq no it has received. Note: A proxy receiving a kProxy_data packet, or a user receiving a kUser_data packet, should ignore it, as it is the host operating system actually returning the ping. This is mostly relevant for users, and for proxies running in unprivileged mode. */ kProxy_start = 0, kProto_ack, kProto_data, kProto_close, kProto_authenticate, kNum_proto_types, kUser_flag = 1 << 30, // set when packet comes from a user kProxy_flag = 1 << 31, // set when packet comes from the proxy kFlag_mask = kUser_flag | kProxy_flag, kDNS_port = 53, }; #define kPing_tunnel_magic 0xD5200880 // Resend packets after this interval (in seconds) #define kResend_interval 1.5 /* ping_tunnel_pkt_t: This data structure represents the header of a ptunnel packet, consisting of a magic number, the tunnel's destination IP and port, as well as some other fields. Note that the dest IP and port is only valid in packets from the client to the proxy. */ typedef struct { uint32_t magic, // magic number, used to identify ptunnel packets. dst_ip, // destination IP and port (used by proxy to figure dst_port, // out where to tunnel to) state, // current connection state; see constants above. ack, // sequence number of last packet received from other end data_len; // length of data buffer uint16_t seq_no, // sequence number of this packet id_no; // id number, used to separate different tunnels from each other char data[0]; // optional data buffer } __attribute__ ((packed)) ping_tunnel_pkt_t; /* ip_packet_t: This is basically my own definition of the IP packet, which of course complies with the official definition ;) See any good book on IP (or even the RFC) for info on the contents of this packet. */ typedef struct { uint8_t vers_ihl, tos; uint16_t pkt_len, id, flags_frag_offset; uint8_t ttl, proto; // 1 for ICMP uint16_t checksum; uint32_t src_ip, dst_ip; char data[0]; } __attribute__ ((packed)) ip_packet_t; /* icmp_echo_packet_t: This is the definition of a standard ICMP header. The ptunnel packets are constructed as follows: [ ip header (20 bytes) ] [ icmp header (8 bytes) ] [ ptunnel header (28 bytes) ] We actually only create the ICMP and ptunnel headers, the IP header is taken care of by the OS. */ typedef struct { uint8_t type, code; uint16_t checksum, identifier, seq; char data[0]; } __attribute__ ((packed)) icmp_echo_packet_t; /* pt_thread_info_t: A simple (very simple, in fact) structure that allows us to pass an arbitrary number of params to the threads we create. Currently, that's just one single parameter: The socket which the thread should listen to. */ typedef struct { int sock; } pt_thread_info_t; /* forward_desc_t: Describes a piece of that needs to be forwarded. This structure is used for receiving data from the network, and for subsequent forwarding over TCP: 1. Client sends data to proxy over ICMP 2. Proxy receives the data, and puts it into a forward_desc_t 3. The proxy starts send()-ing the data over the TCP socket to the destination, decreasing forward_desc_t->remaining with the number of bytes transferred. 4. Once remaining reaches 0, the forward_desc_t is removed from the receive ring. The same procedure is followed in proxy-to-client communication. Just replace proxy with client and vice versa in the list above. */ typedef struct { int seq_no, // ping_tunnel_pkt_t seq_no length, // length of data remaining; // amount of data not yet transferred char data[0]; } forward_desc_t; /* icmp_desc_t: This structure is used to track the ICMP packets sent by either the client or proxy. The last_resend variable is used to prevent resending the packet too often. Once the packet is acknowledged by the remote end, it will be removed from the send-ring, freeing up space for more outgoing ICMP packets. */ typedef struct { int pkt_len; // total length of ICMP packet, including ICMP header and ptunnel data. double last_resend; int resend_count; uint16_t seq_no, icmp_id; icmp_echo_packet_t *pkt; } icmp_desc_t; /* challenge_t: This structure contains the pseudo-random challenge used for authentication. */ typedef struct challenge_t { uint32_t sec, // tv_sec as returned by gettimeofday usec_rnd, // tv_usec as returned by gettimeofday + random value random[6]; // random values } __attribute__ ((packed)) challenge_t; /* xfer_stats_t: Various transfer statistics, such as bytes sent and received, number of ping packets sent/received, etc. */ typedef struct xfer_stats_t { double bytes_in, bytes_out; uint32_t icmp_in, icmp_out, icmp_resent, icmp_ack_out; } xfer_stats_t; /* proxy_desc_t: This massive structure describes a tunnel instance. */ typedef struct proxy_desc_t { int sock, // ICMP or UDP socket bytes, // number of bytes in receive buffer should_remove; // set to true once this instance should be removed char *buf; // data buffer, used to receive ping and pong packets uint16_t id_no, my_seq, ping_seq, next_remote_seq, pkt_type, remote_ack_val, icmp_id; int recv_idx, // first available slot in recv ring recv_xfer_idx, // current slot in recv ring being transferred send_idx, // first available slot in send ring send_first_ack, // first packet in send ring not yet acked recv_wait_send, // number of items in recv ring awaiting send send_wait_ack, // number of items in send ring awaiting ack next_resend_start, authenticated; challenge_t *challenge; // Contains the challenge, if used. uint32_t state, // Protocol state type_flag, // Either kProxy_flag or kUser_flag dst_ip, // IP and port to which data should be forwarded. dst_port; struct sockaddr_in dest_addr; // Same as above double last_ack, // Time when last ack packet was sent. last_activity; // Time when a packet was last received. icmp_desc_t send_ring[kPing_window_size]; forward_desc_t *recv_ring[kPing_window_size]; xfer_stats_t xfer; struct proxy_desc_t *next; } proxy_desc_t; /* pqueue_elem_t: An queue element in the pqueue structure (below). */ typedef struct pqueue_elem_t { int bytes; // size of data buffer struct pqueue_elem_t *next; // next queue element (if any) char data[0]; // data (duh!) } pqueue_elem_t; /* pqueue_t: A simple queue strucutre. */ typedef struct { pqueue_elem_t *head, *tail; int elems; } pqueue_t; /* pcap_info_t: Structure to hold information related to packet capturing. */ typedef struct { pcap_t *pcap_desc; struct bpf_program fp; // Compiled filter program uint32_t netp, netmask; char *pcap_err_buf, // Buffers for error and packet info *pcap_data_buf; pqueue_t pkt_q; // Queue of packets to process } pcap_info_t; // Prototypes (sorry about the long lines..) void usage(char *exec_name); void* pt_proxy(void *args); void pcap_packet_handler(u_char *refcon, const struct pcap_pkthdr *hdr, const u_char* pkt); void handle_packet(char *buf, int bytes, int is_pcap, struct sockaddr_in *addr, int icmp_sock); 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); void remove_proxy_desc(proxy_desc_t *cur, proxy_desc_t *prev); void pt_forwarder(void); void print_statistics(xfer_stats_t *xfer, int is_continuous); int queue_packet(int icmp_sock, uint8_t type, char *buf, int num_bytes, uint16_t id_no, uint16_t icmp_id, uint16_t *seq, icmp_desc_t ring[], int *insert_idx, int *await_send, uint32_t ip, uint32_t port, uint32_t state, struct sockaddr_in *dest_addr, uint16_t next_expected_seq, int *first_ack, uint16_t *ping_seq); uint32_t send_packets(forward_desc_t *ring[], int *xfer_idx, int *await_send, int *sock); void handle_data(icmp_echo_packet_t *pkt, int total_len, forward_desc_t *ring[], int *await_send, int *insert_idx, uint16_t *next_expected_seq); void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, int one_ack_only, int insert_idx, int *first_ack, uint16_t *remote_ack, int is_pcap); forward_desc_t* create_fwd_desc(uint16_t seq_no, uint32_t data_len, char *data); void init_ip_packet(ip_packet_t *packet, uint16_t id, uint16_t frag_offset, uint16_t pkt_len, uint8_t ttl, uint32_t src_ip, uint32_t dst_ip, bool is_last_frag, bool dont_frag); uint16_t calc_ip_checksum(ip_packet_t *pkt); uint16_t calc_icmp_checksum(uint16_t *data, int bytes); challenge_t* generate_challenge(void); void generate_response(challenge_t *challenge); int validate_challenge(challenge_t *local, challenge_t *remote); void send_termination_msg(proxy_desc_t *cur, int icmp_sock); char* f_inet_ntoa(uint32_t ip); void pt_log(int level, char *fmt, ...); double time_as_double(void); #endif PingTunnel/README0000700000076500007650000001140211217641661013647 0ustar danielsdanielsPingTunnel Read Me ================== What is ptunnel? ---------------- Ptunnel is an application that allows you to reliably tunnel TCP connections to a remote host using ICMP echo request and reply packets, commonly known as ping requests and replies. Contact details --------------- You can the author, Daniel Stoedle, here: The official ptunnel website is located here: The Windows port was created by Mike Miller: Compiling --------- To compile ptunnel, simply run make. If everything goes well, you should end up with a binary called ptunnel. This serves as both the client and proxy. You can optionally install it using "make install". On Windows, run "make ptunnel.exe" to compile the Windows binary. You will need mingw installed, as well as the WinPcap library. WinPcap is available here: Running ------- Ptunnel works best when running as root, and usually requires running as root. Again, from the website: Client: ./ptunnel -p -lp -da -dp [-c ] [-v ] [-u] [-x password] Proxy: ./ptunnel [-c ] [-v ] [-u] [-x password] The -p switch sets the address of the host on which the proxy is running. A quick test to see if the proxy will work is simply to try pinging this host - if you get replies, you should be able to make the tunnel work. The -lp, -da and -dp switches set the local listening port, destination address and destination port. For instance, to tunnel ssh connections from the client machine via a proxy running on proxy.pingtunnel.com to the computer login.domain.com, the following command line would be used: sudo ./ptunnel -p proxy.pingtunnel.com -lp 8000 -da login.domain.com -dp 22 An ssh connection to login.domain.com can now be established as follows: ssh -p 8000 localhost If ssh complains about potential man-in-the-middle attacks, simply remove the offending key from the known_hosts file. The warning/error is expected if you have previously ssh'd to your local computer (i.e., ssh localhost), or you have used ptunnel to forward ssh connections to different hosts. Of course, for all of this to work, you need to start the proxy on your proxy-computer (we'll call it proxy.pingtunnel.com here). Doing this is very simple: sudo ./ptunnel If you find that the proxy isn't working, you will need to enable packet capturing on the main network device. Currently this device is assumed to be an ethernet-device (i.e., ethernet or wireless). Packet capturing is enabled by giving the -c switch, and supplying the device name to capture packets on (for instance eth0 or en1). The same goes for the client. On versions of Mac OS X prior to 10.4 (Tiger), packet capturing must always be enabled (both for proxy and client), as resent packets won't be received otherwise. To protect yourself from others using your proxy, you can protect access to it with a password using the -x switch. The password is never sent in the clear, but keep in mind that it may be visible from tools like top or ps, which can display the command line used to start an application. Finally, the -u switch will attempt to run the proxy in unprivileged mode (i.e., no need for root access), and the -v switch controls the amount of output from ptunnel. -1 indicates no output, 0 shows errors only, 1 shows info messages, 2 gives more output, 3 provides even more output, level 4 displays debug info and level 5 displays absolutely everything, including the nasty details of sends and receives. The -f switch allows output to be saved to a logfile. Security features: Please see the ptunnel man-page for instructions. Supported operating systems --------------------------- Ptunnel supports most operating systems with libpcap, the usual POSIX functions and a BSD sockets compatible API. In particular, it has been tested on Linux Fedora Core 2 and Mac OS X 10.3.6 and above. As of version 0.7, ptunnel can also be compiled on Windows, courtesy of Mike Miller, assuming mingw and WinPcap is installed. Credits ------- Thanks to L. Peter Deutsch for his open-source MD5 implementation, included with ptunnel, but also available here: http://sourceforge.net/projects/libmd5-rfc/ Many thanks also to Mike Miller for his work on creating a Windows port of ptunnel. Thanks to Sebastien Raveau for implementing various security features and SELinux support. License ------- Ping Tunnel is Copyright (c) 2004-2009, Daniel Stoedle , Yellow Lemon Software. All rights reserved. Ping Tunnel is licensed under the BSD License. Please see the LICENSE file for details. PingTunnel/redhat/0000755000076500007650000000000011217641661014247 5ustar danielsdanielsPingTunnel/redhat/ptunnel.spec0000644000076500007650000000373711217641661016622 0ustar danielsdaniels# $Id: $ # Authority: dries # Upstream: Daniel Stodle %define real_name PingTunnel Summary: Reliably tunnel TCP connections over ICMP packets Name: ptunnel Version: 0.70 Release: 1.rf License: BSD Group: Applications/Internet URL: http://www.cs.uit.no/~daniels/PingTunnel/index.html Packager: Dries Verachtert Vendor: Dag Apt Repository, http://dag.wieers.com/apt/ Source: http://www.cs.uit.no/~daniels/PingTunnel/PingTunnel-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: libpcap %description Ping Tunnel is a tool for reliably tunneling TCP connections over ICMP echo request and reply packets (commonly known as ping requests and replies). It is useful for evading firewalls that, for whatever reason, prevent outgoing TCP connections, but allow in- and outgoing ICMP packets. The tunnel works by having a proxy run on a machine ping-able from the inside of the firewall, with the client running on the local machine from which TCP access is required. %prep %setup -n %{real_name} %build %{__make} %{?_smp_mflags} %install %{__rm} -rf %{buildroot} %makeinstall mandir="%{buildroot}%{_mandir}/man8" %clean %{__rm} -rf %{buildroot} %files %defattr(-, root, root, 0755) %doc CHANGELOG LICENSE README web/ %doc %{_mandir}/man8/ptunnel.8* %{_bindir}/ptunnel %changelog * Wed Jan 12 2009 Michael Grigorev - 0.70 - Updated to release 0.70. * Fri May 27 2005 Dag Wieers - 0.61-1 - 3200+/dag - Updated to release 0.61. * Sat Apr 30 2005 Dag Wieers - 0.60-1 - Updated to release 0.60. * Mon Feb 21 2005 Dag Wieers - 0.55-1 - Updated to release 0.55. * Sun Feb 06 2005 Dag Wieers - 0.54-1 - Updated to release 0.54. * Mon Jan 03 2005 Dries Verachtert - 0.52-1 - Updated to release 0.52 (Makefile patch applied by author). * Sun Jan 02 2005 Dries Verachtert - 0.51-1 - Initial package. PingTunnel/selinux/0000755000076500007650000000000011217641661014467 5ustar danielsdanielsPingTunnel/selinux/CVS/0000755000076500007650000000000011217641661015122 5ustar danielsdanielsPingTunnel/selinux/CVS/Entries0000644000076500007650000000005511217641661016456 0ustar danielsdaniels/ptunnel.te/1.1/Mon Jun 22 08:09:46 2009// D PingTunnel/selinux/CVS/Repository0000644000076500007650000000002311217641661017217 0ustar danielsdanielspingtunnel/selinux PingTunnel/selinux/CVS/Root0000644000076500007650000000007311217641661015770 0ustar danielsdaniels:ext:daniels@lgserv3.stud.cs.uit.no:/users/daniels/cvsroot PingTunnel/selinux/ptunnel.te0000644000076500007650000000117611217641661016513 0ustar danielsdanielspolicy_module(ptunnel, 1.7) require { type initrc_t; type unconfined_t; type unlabeled_t; class tcp_socket { read write create connect }; class association recvfrom; class rawip_socket { write read }; } type ptunnel_t; domain_dyntrans_type(initrc_t) allow ptunnel_t self:tcp_socket { read write create connect }; allow ptunnel_t unconfined_t:rawip_socket { write read }; allow ptunnel_t unlabeled_t:association recvfrom; corenet_tcp_sendrecv_generic_if(ptunnel_t) corenet_tcp_sendrecv_ssh_port(ptunnel_t) corenet_raw_receive_generic_node(ptunnel_t) corenet_tcp_connect_ssh_port(ptunnel_t) corenet_tcp_sendrecv_lo_node(ptunnel_t) PingTunnel/web/0000755000076500007650000000000011217641661013555 5ustar danielsdanielsPingTunnel/web/index.html0000700000076500007650000005307211217641661015552 0ustar danielsdaniels Ping Tunnel - Send TCP traffic over ICMP

Ping Tunnel

For those times when everything else is blocked.

By Daniel Stødle, daniels@cs.uit.no
Last updated: 22. June 2009

Contents


What is it?

Ptunnel is an application that allows you to reliably tunnel TCP connections to a remote host using ICMP echo request and reply packets, commonly known as ping requests and replies. At first glance, this might seem like a rather useless thing to do, but it can actually come in handy in some cases. The following example illustrates the main motivation in creating ptunnel:

Setting: You're on the go, and stumble across an open wireless network. The network gives you an IP address, but won't let you send TCP or UDP packets out to the rest of the internet, for instance to check your mail. What to do? By chance, you discover that the network will allow you to ping any computer on the rest of the internet. With ptunnel, you can utilize this feature to check your mail, or do other things that require TCP.

Features and requirements

Ptunnel is not a feature-rich tool by any means, but it does what it advertises. So here is what it can do:

  • Tunnel TCP using ICMP echo request and reply packets
  • Connections are reliable (lost packets are resent as necessary)
  • Handles multiple connections
  • Acceptable bandwidth (150 kb/s downstream and about 50 kb/s upstream are the currently measured maximas for one tunnel, but with tweaking this can be improved further)
  • Authentication, to prevent just anyone from using your proxy
So what do you need for all this to work?

  • One computer accessible on the internet that is not firewalled (or at least allows incoming ICMP packets)
  • A computer to act as the client (this will usually be your laptop, on the go..)
  • Root access, preferably on both computers
  • A posix-compliant OS, with libpcap (for packet capturing)
  • Or: Windows with mingw and WinPcap installed
Compiling the sources simply consists of running make. No ./configure, make, make install, just make. On Windows, you'll need to run make ptunnel.exe, but that's it. The resulting binary is called ptunnel. See the usage section below for info on running it.

How it works

This is a technical description of how ptunnel works. If you're not interested in low-level networking details, you can skip this section. It might help to read it either way, as it provides some insights into the situations where ptunnel doesn't work. Ptunnel works by tunneling TCP connections over ICMP packets. In the following, we will talk about the proxy, the client and the destination. The proxy is the "endpoint" for our ping packets, i.e. the computer that we send the ping packets to. The client is the computer we're trying to surf the net from, and the destination is the computer we would normally be trying to access over TCP (such as a web site or an ssh server somewhere).

So, in order to accomplish this, we need the ability to send and receive ping packets. Many operating systems enable us to do this using so-called raw sockets. Raw sockets is the preferred mechanism for sending ICMP packets, and is used by both the proxy and the client. Unfortunately, raw sockets require root, so there is a provision for using standard datagram sockets if they are supported by the operating system (Mac OS X 10.2 or later supports this, but Linux systems will require root either way). Ptunnel supports this, however it is not recommended for actual use. We'll get back to the reason later.

The client will perform all its communications using ICMP echo request (ping) packets (type 8), whereas the proxy will use echo reply packets (type 0). In theory, it is possible to have the proxy also use echo request packets (and thus make it operate without root), but these packets are not necessarily forwarded to the client beyond the host network, so they are not used.

Machine setup
Figure 1. Networking setup.

The protocol

The proxy protocol uses four different message types combined with sequence numbers and an acknowledgement field. A magic number is used to differentiate our ping requests and replies from "usual" pings. The general packet format (not including the IP or ICMP headers) can be seen in the figure below.

Packet format
Figure 2. The packet format used to exchange messages between client and proxy.

The IP and port fields are only used in packets from the client to the proxy. They indicate where the client wants the received packets to be forwarded (they are really only used once - when the proxy receives the first message with state code kProxy_start).

State codes:
kProxy_start = 0;
kProto_data = 1;
kProto_ack = 2;
kProto_close = 3;
kProto_authenticate = 4;

Identifier flags:
kUser_flag = 1 << 30;
kProxy_flag = 1 << 31;

The state code serves a dual purpose. First, it indicates what kind of message is being received - either a message starting a new proxy session (kProxy_start), a message containing data to be forwarded (kProto_data), an explicit acknowledgement of received packets (kProto_ack, a close message (kProto_close) or an authentication challenge/response (kProto_authenticate). Second, it indicates who sent the message: A message sent by the client will have the kUser_flag bit set, whereas a message sent by the proxy has the kProxy_flag bit set. This is necessary since a ping request will cause the operating system on the proxy computer to return its own echo reply, which is identical to the packet we just sent to the proxy. The ack and seq fields are tightly related. Modelled after the use of acknowledgements in TCP, the ptunnel protocol places the sequence number of the last packet received into the ack field of any outgoing message. The seq field is a monotonically increasing 16-bit counter, that is allowed to wrap around (ok, so I guess it's monotonic until it wraps around). Whenever an outgoing packet has been waiting for an acknowledgement too long, the peer will attempt to resend the first packet not yet acknowledged.

The length field simply indicates the length of the data portion of the packet, and is 0 for all other state values than kProto_data. Finally the rsv field contains two bytes that are reserved (for now they serve as padding).

On receiving a kProxy_start request, a proxy will open a TCP connection to the server given by the ip and port fields. As data comes in over the TCP connection, the proxy will convert the data to ICMP echo reply packets, and send them to the remote peer. The client will do the same, except its packets will always be echo request packets.

Authentication

As of version 0.60, Ping Tunnel supports authentication. The authentication used is very simple, and works as follows. The user first specifies a password or passphrase, which is then hashed using the MD5 algorithm (Ping Tunnel uses the implementation by L. Peter Deutsch, available here. Note that the implementation is included with Ping Tunnel, so there is no need to download it separately).

Whenever a proxy receives a request for a new tunnel, it will respond with an authentication challenge. The challenge consists of a timestamp augmented with random data, totalling 32 bytes. The response is calculated as follows (the + denotes string concatenation):

md5(challenge + md5(password))

The proxy verifies the result by computing the same md5 sums, and then comparing the two. If authentication succeeds, the proxy allows TCP traffic to start flowing in either direction; if not, the connection is torn down.

Handling multiple connections

The proxy handles multiple different connections by using the ICMP identifier field. A client will randomly generate an identifier when it starts a session, and the remote peer will use this identifier to associate the packets with a connection. The mechanism is not foolproof, but works acceptably as long as no two instances attempt to use the same identifier (there is currently no mechanism for reporting such errors).

The ICMP sequence number field is not used by ptunnel, mostly due to fears that some routers might drop packets whose sequence number repeats. Instead, a separate sequence number is used as part of the ptunnel packet format (see above).

Send and receive windows

Ptunnel uses the simple concept of send and receive windows for controlling the number of packets that can be in-flight at the same time. The window is currently statically allocated at 64 packets, but the number can be tweaked by modifying the ptunnel header file (yes, a recompile is required). Increasing the window size will improve the maximum potential bandwidth. The send and receive windows are simply implemented as a set of circular arrays, with pointers indicating the next available send/receive slot, and the first non-acked packet.

Handling packet loss

Ptunnel handles packet loss by resending (presumably) lost packets. As it sends packets, it will increment a sequence number. Both the client and proxy maintain their own sequence number, and also a number indicating the last sequence number acknowledged by the remote peer. Whenever too much time (1.5 seconds) passes without a packet being acknowledged, the peer will resend that packet.

Note that the peer will only resend the first missing packet. Once that packet has been acknowledged, it may resend the next packet(s), depending on how many packets were acknowledged. If the next few packets are acknowlegded as well, they are removed from the send queue. It is not uncommon for one packet to get lost, with most of the others making it through. This mechanism avoids unecessary resends as much as possible.

Congestion control

Ptunnel currently does no explicit congestion control. It will send as many ping packets as the window size allows, as slowly or as quickly as it sees fit. This might be improved in the future, if it turns out to be a problem (which is not at all unlikely..).

When things don't work

There are a number of situations where ptunnel will fail. They can briefly be put into the following categories:
  1. Outgoing/incoming ping not allowed, or filtered by a gateway somewhere along the way
  2. Operating system causing trouble
  3. Probably some other failures as well ;)

We can't handle the first failure - if our packets are filtered before we can get at them, there's little we can do. It is possible to deal with the second scenario by using the packet capturing library to get the packets before the OS sees them. This is necessary on Mac OS X, and may be necessary on other platforms as well. The problem lies in that the OS may occasionally not deliver ICMP packets to the raw socket we have opened for sending and receiving. This happens when the ICMP packet is an echo request (which the OS handles by itself) or when the ICMP packet is a resend (for some weird reason). The workaround is to use packet capture, however this tends to diminish bandwidth by quite a bit. For this reason, you should always try to run the proxy without packet capturing, and see if that works first. (This is the default mode.)

Download

The current version of ptunnel is 0.71, and the source code is available for download here. Some other links:

Ptunnel has so far been tested on a variety of configurations, including Linux Fedora Core 2 and Mac OS X 10.3.6 and above.

Debian users can install Ping Tunnel by adding the following to /etc/apt/sources.list

deb http://www.cti.ecp.fr/~beauxir5/debian binary/
deb-src http://www.cti.ecp.fr/~beauxir5/debian source/

And then run apt-get update followed by apt-get install ptunnel. A big thanks to Romain Beauxis for taking the time to create a Debian package for ptunnel!

As of version 0.70, ptunnel has support for Windows built-in, thanks to Mike Miller. You will need mingw and WinPcap (download here) installed to compile on Windows. Please note that I don't use Windows, so I can not provide support for compilation-specific issues regarding the Windows port. Wouter van der Veer has been kind enough to provide a compiled Windows binary on his website, which you can download here.

Using ptunnel

Client: ./ptunnel -p <proxy address> -lp <listen port> -da <destination address> -dp <destination port> [-c <network device>] [-v <verbosity>] [-f <logfile>] [-u] [-x password]
Proxy: ./ptunnel [-c <network device>] [-v <verbosity>] [-f <logfile>] [-u] [-x password]

The -p switch sets the address of the host on which the proxy is running. A quick test to see if the proxy will work is simply to try pinging this host - if you get replies, you should be able to make the tunnel work.

The -lp, -da and -dp switches set the local listening port, destination address and destination port. For instance, to tunnel ssh connections from the client machine via a proxy running on proxy.pingtunnel.com to the computer login.domain.com, the following command line would be used:

sudo ./ptunnel -p proxy.pingtunnel.com -lp 8000 -da login.domain.com -dp 22

An ssh connection to login.domain.com can now be established as follows:

ssh -p 8000 localhost

If ssh complains about potential man-in-the-middle attacks, simply remove the offending key from the known_hosts file. The warning/error is expected if you have previously ssh'd to your local computer (i.e., ssh localhost), or you have used ptunnel to forward ssh connections to different hosts.

Of course, for all of this to work, you need to start the proxy on your proxy-computer (we'll call it proxy.pingtunnel.com here). Doing this is very simple:

sudo ./ptunnel

If you find that the proxy isn't working, you will need to enable packet capturing on the main network device. Currently this device is assumed to be an ethernet-device (i.e., ethernet or wireless). Packet capturing is enabled by giving the -c switch, and supplying the device name to capture packets on (for instance eth0 or en1). The same goes for the client. On versions of Mac OS X prior to 10.4 (Tiger), packet capturing must always be enabled (both for proxy and client), as resent packets won't be received otherwise.

To protect yourself from others using your proxy, you can protect access to it with a password using the -x switch. The password is never sent in the clear, but keep in mind that it may be visible from tools like top or ps, which can display the command line used to start an application.

Finally, the -u switch will attempt to run the proxy in unprivileged mode (i.e., no need for root access), and the -v switch controls the amount of output from ptunnel. -1 indicates no output, 0 shows errors only, 1 shows info messages, 2 gives more output, 3 provides even more output, level 4 displays debug info and level 5 displays absolutely everything, including the nasty details of sends and receives. The -f switch allows output to be saved to a logfile.

Changes

0.71 - 22. June 2009
  • Added security features and SELinux support, courtesy of Sebastien Raveau.

0.70 - 12. January 2009

  • Added rudimentary support for tunneling over UDP port 53. This may or may not be useful.
  • Added built-in support for compiling on Windows, thanks to Mike Miller.
  • Added syslog support, also courtesy of Mike Miller.
  • Experimental support for spoofing the source IP address. The source address must be explicitly configured in the source code, and it might not work (which is why it's experimental). To enable, define kPT_add_iphdr to 1.

0.62 - Never released

  • Added many comments to the ptunnel header file.
  • Moved most include directives to the header file.
  • Fixed crash if proxy or destination hostnames could not be looked up.

0.61 - 26. May 2005

  • Noted that ptunnel now works without packet capturing on Mac OS X 10.4 Tiger.
  • Log files are now opened in append-mode (i.e., not truncated).
  • Fixed a bug that could cause ptunnel to crash if passwords shorter than 16 characters were used (the overwhelming majority of passwords, most likely!).

0.60 - 15. Apr 2005

  • Added authentication support, changing parts of the ptunnel protocol.
  • Added a manpage.
  • Added MD5 implementation by L. Peter Deutsch for authentication.
  • Moved declaration of a variable to allow ptunnel to compile cleanly on gcc 2.95 - thanks Choong!
  • Added error handling for proxy connections.
  • Fixed a problem with printing send/recv info. All verbosity levels should work now.
  • Updated contact info.

0.55 - 18. Feb 2005

  • Fixed a locking bug that would manifest itself when the maximum connection count on the proxy was reached.
  • Fixed ptunnel to compile correctly on 64-bit architectures (thanks to Åsmund Grammeltvedt for pointing out the necessary changes!)
  • Added a new logging level (level 5). Level 4 no longer outputs send and receive info; for this info level 5 is necessary.
  • When libpcap is in use, the interface is no longer put in promiscous mode. This has never been necessary, and also makes it impossible to use ptunnel with more than one client on the same network.
  • Discovered a problem with pcap that would cause ptunnel to hang when pcap was in use. This problem has not been resolved; the workaround is to simply re-execute the client, or disable pcap if possible.
  • Added support for specifying a log file on the command line, using the -f switch.

0.54 - 1. Feb 2005

  • Fixed a byte-order bug (thanks Alfred!)

0.53 - 31. Jan 2005

  • Fixed a threading bug, and implemented support for limiting the maximum number of tunnels.

Thanks to Alfred Young for reporting and fixing the following bugs:

  • Fixed a bug with packets captured via libpcap, where the struct sockaddr wasn't properly zeroed.
  • Fixed a byte order bug in create_and_insert_proxy_desc.
  • Fixed a memory leak in remove_proxy_desc.

0.52 - 3. Jan 2005

  • Fixed a problem introduced by the ICMP ID field changes. The proxy didn't retain the ID of the packets received from clients, resulting in dropped ICMP packets at the router. This in turn lead to the tunnel no longer working, depending on the strictness of the router. This version fixes that problem.
  • Makefile is better now, thanks to Dries Verachtert.

0.51 - 2. Jan 2005

  • Fixed incorrect checksum calculation for resent packets.
  • Stopped relying on the ICMP packet's "built-in" ID field, and moved the information to the data portion of the packet.
  • Changed default verbosity level from debug to verbose.

0.50 - 7. Dec 2004
Initial public release.

License

Ptunnel is licensed under the BSD license.

Feedback

I'd be grateful for any feedback you may have - send it to me here: daniels@cs.uit.no. I'm interested in hearing about bugs, suggestions and naturally general criticism. If you find it useful, I'd love to know about that too :)

PingTunnel/web/packet-format.png0000700000076500007650000001036411217641661017015 0ustar danielsdanielsPNG  IHDR|gAMAܲIDATxyTWǿ@! RV .GRrףֵhTpV GmUT,**ҊJZa `$)?sg;w{gS:BLD EE#T " " DoE{yetȢEHhhCT(j߿okEcUr8R=___СCVO@${}bz{3hhh?lkb( [`3T*JKK-˗?b6ym8 !LMM =zdi3Vwʔ).i毿Bqqeh6c oɺuit-Kuu5bbb,YYv؁˗/5Zl۶M'-""¬6Xڦ)))o((>!d@.]|uމڼ߿?l- ooo[0+Ν7nڠc͡~HÇ=_BK.utv`i)^Cga5k4MqFmcL Pa߾} ///9rr˗/Ǐ!JsN###ooϘǏ{nTTT@*HmBsѣG믿1bT*^zYLߙ6m̙ݻwo߾8v$ 6oތ'OE.]'`ȑHKKý{PZZ@"h}xbxxoX,Fvv6T*&LUV!55%%%Ÿt&OCcϞ= BZ z]vpRTn%%%@Q?~w_}PSS 6o߾sPPP}vƬԩSpwwG\\|}}c޽pqqAvv6\]]uVNӧ#-- ؾ}; ޽{쌜x<=={a߾}`jǎE˲ظq#x eVä??? 2pc sN-**Ҿ+8gϞzUm lذУG<}ϟ>`ӦMXdV+Eff&`ȑ9rE%''#&&* [nň#0x`xyyaZbUL899W\\ DHFxyyYD{ii)7|S'=##[ln7ǟD"ڵkQRRWW/ϸ~:!@n޼E555GDDZNa  [󑐐@ <_|quJE"?? Xp6uuubBAaĉHNNƑ#G0gho)77<!>>>:=fP444XDSK ^z!99))):!ka }BP֖ M͛71~x 8˗/Grr21tP&<ڵkq-TTThV\D8p":ˢe* {9-***Z=}չ*J`m>A 88!CtRcŘ4iN!,,L}!̟?2e ^xanmdmM=f 0V2 7nKt˖-Cjj*fϞ"|G1gL>qٴi4B$a֭ Ell,,XEFFŋX`f̘Xl6-Ǹqp5 ذa.\cb̙:3ĦL(|r|}}Yf;ki޼yĉ1vXmݻ7͛iӦioxPRR`DFFɓ1c+iӦ(((@@@<<<{~k׮:tY' eY?E1"##1yd|xIЧOHRt???9sF'oE``.֌tzQ'MDPՑB!AAAÇ#HF8K-7ovim} zɐ!CL&ӻt}444Tj466JWTR/&WSScP/ZS}Q[[L3:HGR)儦im6_"{}\ٮOOuu60D&LFy6̢NFC:___R:ˢ%ĿL&3(WO={FFw]Z&ϟ?78_cD/$$dgwinP,BFEEi0 S_~ӭ[7K3P@`֭[3 ŒqwwכݻkD3Y면-tj3p8pwwo&W_՛驳pP(Z3aKM1&.ѣG4'''Fώ= B!11Ѧ mj1;ϡƦih m1LBիpqi ŋJSZMݹsKuu5JU}hsΙ4y$ (7Gj~cPwwBPphj(REs8ZMjϷ/3 \VS4Msx<#Ka=X8;;hI42ֺ(JF#EGATr3 saiNNN~ϦRB0 Ӆ.EQB;Ԇ.y5SsZ+EQFB(b~yߎ7!DG`a!Q899J{MZi{Ea(k0ǮySSw/P(4}DZ Hh~вgc5Jei899}T*XgQB4xZvq4:Bhll\\\J%rF̙3?jVXvBAxfcL5rK JS޷ɻsJ*՚%zϘ z_1ڥ88l&ǜ @ vLBAQy7gw$'JNwܙY'5 YCi-TUn v,B GF>cc ~=X8Oc}~70߂⛩ _]~7^s_Erkfƅ?G؜9s,mn PCD0* L&]]vYTz=9d v!'5sO=??z RGz~MIj0[3Y"162%??Edjbj0Jid|||V^^^k[3*z0:E;G1!$<..+Bt:}E]຀i2̚5K<! Ұ†!!Dз;' BRCaC$8AzlQw N^+ w UPn> y(La@I%pࡹ|{0q`]1/ Ћ^j`e푉>a6.0f{#:˫ n "`@R,+|dE)0yƩ" B[.>Lq@MqP"c0銻Y# +6%7x ǢhK' s=W=w^xt;l_=<: sf|_5؛ӷ^yg|C|ߒ?wwڳ7‚ ^=7+5b*.[~?FNiXµHH}W߇7 GMFO {~Y_cوM¶/n<) tfxg}?g>2"Q )6l=0[gg(l1^ rی[4FLq ᱸ?å c漿fFkӞ P| 73_k$=C'cm]q+k Ljڟ7Pо SoE3L6M-R2*#61 &Kx脁p֖^k|ޣIx 0"&iQsIB\DD'j3õ?'8 xx.0ZQ@%4A0ъv7V|8k() o}PNi{~> wҖ?:e|xwcˆV}5%[Gk=^WD.}WDL £G@q? .G|mTta߶Mx n-sx n@|;s>tpVRJyI;ڿ$FXĴiS~\>CM1\2ې> C΀*r \6k"cS Ԁay2jp蝦7t?^W?hy:{7l&-]wC-^ FLEeaDǥaYYX7.cѯDepfa&+rʲP~b_PO1^n*>.YJ b ">yⓇ5Q N^K$8Az-AD N^K$8Az-AD N^L NUѣP粓$;o PMS|`iJmi7Q-[vwyL=ޮÀjPu:JSh;z#=-nBmk W?}jF8!'t;z7FZ551vʽxGԿ:wzM?^\ɤa#fy(>YŃЭTsǷ{놥%dy^v{9;;c̣鼌ű3Mܘ{p|SRﺆ $K>AVl)S.g0J5M+'8eYRJş;)~[xhmȨ+)4MAo}?=;c*4Ms2&U9rp8")e!PJmnʆ煓tec:2D)NJ:FUY$Z/85Uҥ@vjɛt~I*(Gc%:JBˀLxY[[{qfne%gaO8ۮl$PƘR+4LI)UFHrBg)01UUU͚1Q,ϡBdJ))**wȑ 4iҷ.Gc!nX$I%tNӥigQBH Ln.$)R*wGjժUOZ,'O^麲K䢔֩ZrF,]:6 @X -g7xSNMv8(((riqKLL,C$ %TzZMjl6sʕSǏ_ПbbIpϪweɊRJ RRJ]1qt:HnB Zk^GpYch\ ]4M!>J]f٭<ٍ͝~H_hw^0[fZ($ioGƘ&˲j]rx<:L`xi.~󒞒2aĈǺ>ФcLs:jdd?;;۷x~ε12ydyz.x5.oʌ.KB7nƍ5Qj΁nO m-ZtgWݻX{q_6*bBhp-? '/kk\iX^PO4%G`'Or.d!ADB@4}V?ũ@ 6@ !`N_INE6icjQaSbBMh8ߐBWpv3 f ^%q x]Ї\ӗ48?h }Ƿ?/'ŸP;S7}!}w"Vczh`x J蓮aɬ-?'rF$g' Mj\pPD gxp | BPD]0|HV9xr ))M n=sjh8!؄sM] $Xo?7sAE$D6'7AFUL,g>lg<_A `1=O Rt)?{?@&tk\ WN8#c4~].8 B] `9C>HFoRAH'6/9 GkdfJG* D἗1an i`{'1A J_Qt(%hjLx1A:21wybS`Jfuym`^@i8#hMLBʣ"؁=5cIo7A8>b05h{sE,F4GoAj o-/pFɭf.VMǻ|= +GL ѵBA6u λCL%zh |k4M+>#3zv B 눞  i(p^7\Cn,Hu9hZn JH ^.B/ pKpHWZ"-? Ѵ_I I;\KpMߔ8{_|]{WK7^g:k1 t n4<9u~f xӓ" ^ekh ԃϜ toh?5Op%n0M n `%x߭ nDp}DݳXTop3: ^α)/qj !\ >F_,gp!,^1kHp 7aB;6YTk<׏t}ìZ0X>3O: ݭ!gq%96$8K$տ:]^wzgzIw7VcWt 𱏁Ph/5LR>v<Sx r*B5odȸ|^y .@\{ :|D߂YAR~p UԂ/l0bu('60 d0bٚ 0 G $xM5^. v!(EM2P&=7$|n@ ]WzK*W;>l:\`+#KrZi"B/7lA|2)L~hxxNH)QUNU$>vt >Slo<_;*>} 5  ph`0`Enjc5j!&& (z(**‰'uV_,暚Ǎ:}QO!O8*@AxNG\:xŋ[neUWW|Mv;\F9M /7` %d2=m2\7x\9r~~ WMJ0;6t}C !I-&npvybk~b'NXogpVR鷭i-Mwn n8<~3wkSl6ۆ,=6d &n/I%""b]DDDd%hV0h4Vu)xBճ>U9vhZ| $oTTԗ6D0$I1L5{m6D-7L .)o"##_ Zf]e˖۷bjs~VZZʶm^|EvUW9&*,,)dw&Bp ְҕk( IFDDX,nɽj*o>zotJKK͛k͛牍uLj6ग़fצM4a5**I#vZyGw>o7T5yw:W"gd/XnLi맟~b=6p@dw~<?H?\.=(l͚5,++c"& xbD&3[U w;k.v.;_Wٶmz J)[|9slb;/u%LA2؜9s[o|P1L^i.M3&^b)?~gj*Y,;|=DgX,?/7_QTTX;ƄwbmΜ9`}Mee%4hbZ__ 4Lx1AC&EDDkjj}I.(fnWb(PRRRZhѹ.2o8 Sz Jik޾}jUNz-c3 wO)G)#g!$Zww}fBDZe8tB!M7nWk'B`ώư z;3}!vІ>QQQ\'IRʾW?; kJ'YtEjĨ`2YQQKOwLiCh7-**1V?]no9!D"b^jȲUW_=AOp^ɓ'["55(vÆ 3L~^?|Fbm9#DIt)SR[WZRR"oB_222JTaÆ)uuuv ]w2Ime!`E O|wM~/A(ͥ թׯ_?v!đr 6n8R.IRo7=zP]]}#٣>3fua߾}7Pt:pB;srroذ!n$IusssXB B!0'N6 Nl?~-Y[l ȵ(?>|-:>Æ r$I^0tmTBHw/)zBd0 BCUUՒz8{fBRRZ|>+ذayZ Ox999/x3f>رck.L6 V˜1cW_}]vaHIIܹsp3gL4 ՝FQL4Z;lذo7mڔp8dY}ffEQd2u6Db3X,|%rk↑y `T'cz7Nl1q4:v>\s5xeX^{-ƌ[n]tV^{ O?4-[W^yDc׮]q#-- ;wc=%KƝw ÁK.W\q6nYq7}Yo{V OG;xY+?u:~ߙ[r/ka*6䀏􃏘hz@ǬYpEꫯƁՊxv꫸1m41wuys,Xwuv;OF; (((+{O<5kvc!wIDATAFF-55~D!.HKK*,K`.]\X~ lD۟UW9mov J߿ˊ!+>99VGĂ///6mBuu5 >c;vǷr I/˕s|>ХK0|:t8 ~@ibڋUWYOv Tխ$]([WǾJ;} 2~x!ܥ^E>C='N ==iii8q .ފ4Ͻx¹1zh\ih1tP#G_y啉(SWj*M~^,ލq a&|9v9UHv nq/:%PWdh ?P10**Gvm̙33N'L&dYc :'N!C`0, "裏0w\\p,\+&XSI] M:k MY9$➮-q쭽86L6_N7AV뮍(,yj") EKvCu)H?羚B采aI_Əׯg.Rpm‘ }2?'}QF, o,p8"tf ԅ>C0c؋!&/;O{e»ԼV0ƾ ܪŋXkp*ɓo.\6**1JjBsJnÇ4i`4 WA=xI4U'&6>Vw_WFڼ2}pBђ8rzG8 l`A6P[Rp}u`0%۰o;QcV^Q˧Uk9&oEE)p]TڵD7c}c-s{ Λ0aQQQ?m޼Mſ5NJ***r -[te݌Wo4H/E?6tO=7O4C WR1<؇ !ƢBx9@qI_FXK%#F2݀/LmH42*l5`Sas T",Ux38AtXMH[N { +[,~Xd6mG%4L{6mڤҕ[od#u-֢ ׳w <7CejҊ}J W @CLl H)3tFX߆:v>=W%v5fEAޔ,+wÖutT'8O{コ9Ms\tE(//7kO~?iӦ!^$f_|>9$xFJVBKAg7%\W| ͭ@2yT }.O^O/aˎFDnST ,֌oZn!k,% jrÐvQ=D4@Ui ,ɸ72d-//,IRLYYYt~~)B8O8u侓nlT; y@8 )d~[Uc܋wnj@/2=1@@{vU k[vJE)SeGRoUص!4,~dXJEG$IѲ,[oĐR"&H ܰEwf>r͋,:x.\$V$ b/MC+C2E2l?=GQH<>Jn0Dwy6^xV|9=-H6 OnE/2wi= ZÃk`1'q䰮MIMF㱢"s\\ܙٳdff[/+WvƷgdo4ۺ<=u&r4 jinEa52)5^#ξDtÜ)|n!ª٤[>4N?\kaIzj39S7.I4w-xbccΝ; 1#c\]]m\hx" U`ZX;_pVlw1NҐ-^fw~+Qf?2 >BeG&]crИ6y鬆6*!fwq%"zYVմw^z:IjfR Fsm)W>aَejk{PNp'N8j6}c:rS-lp|Ά L']1Vy#C QZ gXnEB>|f RJRrb{xkNs|۾fn {I:Win<j#YfBtq 2IBTI$hټTGFkx"kw/7P;qAv nd\\84˅*bZ1NS=X,ݝtH ucͶ% z 쬡g3VN4IÄvZ{t:Re1uU ueN?yãeR  n_'ZOޣV1Vi:X:5l)8}_:1!L7NTSc ~)Ǝ[Bq0ƜzG)U,*{7ehj@秤z½^olNNNӖ.]!t~?MTPJ1 У)e+heB1 LKKsIFR<Z^2;z1VXX"),1Bw{ܽG~1_v:q1!$b3Crᮻ233'%%1vRD)-zF$Q n|!#d6C}^n&.111VPU<:3 -,YU'q"4 T/Z'|UV!C&$$cZZI\b 1($٬S]tkv[9ٜ8McU(&;vpv̞=fgg2 %NZL4ͥ뽑JZJ>h^Qn>Z "iNBTUm WiVRR8CaԨQ,##())i?2Y+c߿Oԓ}2Zߙ?`clAE:ƘWOP~:4 ٘1cRRRvkvRB)-Pzk),_/G.ej%O(DN'>V[ͼ*fu:(Xzz^NjVzƍF۷LsjII B1\.ty9s&߿cRJȲ|\QJUUo[$>|xA||X1c$IbUU+%Ix0Ga:NcNEQz}m||3 V͑#G/t.3еk"55dffAP(J_}=p e8Ӊ)ShŞ1c|G)=? v={L봶4099@ińRX$IHT <a-vORJCN;)~jz;))ozt ׮]+Ƿ71Ey<x'bX~h4/3*=۶m{|Q=Xvk֬y󴈈$ITiZaQQ%%%V+HkjjȱcLj$I_~u '㏃TJ9 TU*匱355/0ȬYG'ICR111,ڑ#GM6'~j.w:}]̜9raaڵXl~zIHH8tDU*)U$41VUTT߱cF'FcKuΝ;h_}*LNN>iZ!!1Vݒ$󍯫3 w $IjBHi*cZMjeYv~f\Rt zQQQS$Iz1f;w.~Gx__YY-[믿f6m۷o'IIIM&S5c@c @!1V۬/`0x˽~h4Nz3f+ns^1,Ym޼)))eu:IX1x[eXINN >3b') 84ML&oyy_AAzL'~VVV(ɆNӉ,D!,, ǏgGEYYH||h4ֆ$&& ZMjITC)mHfՔRci'22?p@ŋՄa-6ͦx@+ij˅իW>s)66qr+Ot('TZz3""[o~am۶4јi1cQI~UUܔR!ħ('33K/EW7Q 111S(c|>ߠ4L1UeY1X,.OZnnBHCcI䤔(.I\~ ~JiӦ=[QQWVV5l06i$iԨQCtt4!MݕTUűcP\\B|oe{!qqqR%x}YxRZI 䪪: !իW{cp 'OeY6 cLSUUaiA>٬/: QKp/;΁F1SDX!$\$+TzT4UEH%x'j$+IK4i^EQ,{5Mn^?S-TEQMVU5p^r$E!B$l6l6&ɧkcccOZJiR%,˵a9)NMB1g5MSdYRJ^W1dRWXqփ >/C !$ 1faE1K$˲$IO01VG)uȲh(Uj$ɥ鼔RiQFyEKc\|񄁯`&!ֈ114MhXX_$,fYEFF_Ζjcdƌ^7j1"$I3l`:BRFQc$I c̥Ge?!ݐԚ*UU'`]  cE~,{~,٪( 5 .5 A4^t:%Y, TUPHcfS|>=R e"cL