/* bouncer v1.1 connect() client dynamic wrapper to bounce tcp connections by mirage (ptlink/ptnet) .\\idgard Security Services [March/April/May 1999] Some portability fixes added in September MANY thanks to LiquidK @ptnet/promiscnet/efnet this whole .c was based on an example of his... this can be used, for example, to fetch your mail anonymously, to transparently bounce irc, http or even remote exploit connections. actually, anything AF_INET that uses SOCK_STREAM is likely to work. it does this by grabbing calls to connect() and, behind the scenes, use one or more proxies to "bounce" and thus hide the source of the connection (you). tested only in linux, but others should work too, at least with some changes. it doesn't require root priviledges. compilation: linux: gcc bouncer.c -o bouncer.so -ldl -shared -O2 -s bsd: gcc bouncer.c -c -fPIC -O2 -s && ld bouncer.o -o bouncer.so -shared you may need to use -DSOCKLEN, depending on the OS and libs. to use, in bash: export BOUNCER="type:host:port@type:host:port(@...)" export LD_PRELOAD=/path/to/bouncer.so ./your_connect_program where is: 'w' for Wingate 's' for SOCKS4 'c' for CSM Proxy to unload: unset BOUNCER LD_PRELOAD some debugging output can be turned on by setting the environment variable BOUNCER_DEBUG to "1". note that, when using wingate, byte 255 is used as a telnet control caracter, so wingate should not be used in binary connections, not even before a socks bounce for example (socks may use byte 255 in its protocol). AFAIK there is no simple way to fix this, suggestions are welcome. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SOCKLEN typedef int socklen_t; #endif #define SOCKS4_USER "billgates" #define BOUNCE_NORMAL 0 #define BOUNCE_WINGATE 1 #define BOUNCE_SOCKS4 2 #define BOUNCE_CSM 3 #define YES 1 #define NO 0 #define ERROR -1 char *bounce_env; static void *lib_handle = NULL; /* place to put the old connect() call */ static int (*old_connect) (int sockfd, const struct sockaddr *serv_addr, int addrlen); /* this is not reentrant :) */ int in_use=0; /* whether to print or not some debugging output */ int debug=0; /* get original connect() address and BOUNCER from the environment */ static void init (void) { char *debug_env = getenv ("BOUNCER_DEBUG"); if (debug_env != NULL) if (debug_env[0] == '1') { debug = YES; printf ("bouncer debugging started\r\n"); } bounce_env = getenv ("BOUNCER"); if (!(lib_handle = dlopen ("libc.so", RTLD_LAZY))) if (!(lib_handle = dlopen ("libc.so.6", RTLD_LAZY))) if (!(lib_handle = dlopen ("libc.so.5", RTLD_LAZY))) { printf ("error loading libc!\n"); exit (1); } if (!(old_connect = dlsym (lib_handle, "connect"))) { printf ("connect() not found in libc!\n"); exit (1); } } /* return the number of chars needed represent */ int int_chars (int num) { int i = 1, size = 0; while ((num / i) != 0) { i *= 10; size++; } return size; } /* connect to , using the given */ int connect_to (int sock, char *host, int port) { struct hostent *he = NULL; struct sockaddr_in sa; if ((he=gethostbyname(host)) == NULL) return ERROR; memcpy (&sa.sin_addr, he->h_addr, he->h_length); sa.sin_port = htons (port); sa.sin_family = AF_INET; return old_connect (sock, (struct sockaddr *)&sa, sizeof sa); } /* read chars from to */ int read_sock (int sock, char *dest, int num) { int i; for (i = 0; i < num; i++) if (read (sock, dest+i, 1) == ERROR) return ERROR; dest[i] = 0; return !ERROR; } /* read from until a is received */ int read_until (int sock, char c) { char buf = 255; while (buf != c) if (read (sock, &buf, 1) == ERROR) return ERROR; return !ERROR; } /* bounce through a Wingate */ int bounce_wingate (int sock, char *host, int port) { char buf[80]; if (debug) printf ("wingate in %s:%d\r\n", host, port); snprintf (buf, 80, "%s %d\n", host, port); write (sock, buf, strlen (buf)); if (debug) printf ("request sent\r\n"); if ( (read_until (sock, '\n') == ERROR) || (read_until (sock, '\n') == ERROR) ) return ERROR; if (debug) printf ("done\r\n"); return !ERROR; } /* bounce through a SOCKS4 firewall */ int bounce_socks4 (int sock, char *host, int port) { struct hostent *he = NULL; unsigned char buf[4]; char buf2[100]; if (debug) printf ("socks4 in %s:%d\r\n", host, port); if ((he = gethostbyname (host)) == NULL) return ERROR; memcpy (&buf, he->h_addr, he->h_length); /* taken from eggdrop's net.c:proxy_connect() */ sprintf (buf2, "\004\001%c%c%c%c%c%c%s", (port >> 8) % 256, (port % 256), buf[0], buf[1], buf[2], buf[3], SOCKS4_USER); write (sock, buf2, 8 + strlen (SOCKS4_USER) + 1); if (debug) printf ("request sent\r\n"); if (read_sock (sock, buf2, 8) == ERROR) return ERROR; if (debug) printf ("done\r\n"); return !ERROR; } /* bounce through a CSM Proxy */ int bounce_csm (int sock, char *host, int port) { char buf[100]; if (debug) printf ("csm in %s:%d\r\n", host, port); if (read_until (sock, '>') == ERROR) return ERROR; sprintf (buf, "%s:%d\n", host, port); write (sock, buf, strlen (buf)); if (debug) printf ("request sent\r\n"); if (read_until (sock, '\n') == ERROR) return ERROR; if (debug) printf ("done\r\n"); return !ERROR; } /* decide how to connect */ int bounce_to (int type, int sock, char *host, int port) { switch (type) { case BOUNCE_NORMAL: return connect_to (sock, host, port); case BOUNCE_WINGATE: return bounce_wingate (sock, host, port); case BOUNCE_SOCKS4: return bounce_socks4 (sock, host, port); case BOUNCE_CSM: return bounce_csm (sock, host, port); default: return ERROR; /* should never be reached */ } } /* main bouncer */ int bounce (int sock, struct sockaddr_in *sa) { int last_bounce = BOUNCE_NORMAL; /* last bounce type made */ char *type, *host, *port, *env = bounce_env; /* BOUNCER variable parser */ if ((type = strtok (env, ":")) != NULL) do { if ((host=strtok(NULL, ":")) == NULL) return ERROR; if ((port=strtok(NULL, "@")) == NULL) return ERROR; if (bounce_to(last_bounce, sock, host, atoi(port)) == ERROR) return ERROR; switch (type[0]) { case 'w': last_bounce=BOUNCE_WINGATE; break; case 's': last_bounce=BOUNCE_SOCKS4; break; case 'c': last_bounce=BOUNCE_CSM; break; default: return ERROR; } } while ((type=strtok(NULL, ":")) != NULL); else return ERROR; host = (char *) inet_ntoa (sa->sin_addr); /* connect to the real destination */ last_bounce = bounce_to (last_bounce, sock, host, ntohs (sa->sin_port)); in_use = NO; return (last_bounce); } /* the all mighty wrapper */ int connect (int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) { int optval = 0, optlen = sizeof (optval), tmp, flags; if (!lib_handle) /* If not loaded, load it now */ init (); /* if it's not connecting to localhost, and socket is AF_INET */ if ( (((struct sockaddr_in *)serv_addr)->sin_addr.s_addr != 16777343) && (((struct sockaddr_in *)serv_addr)->sin_family == AF_INET) ) /* if we're not already being used */ if (!in_use) /* if BOUNCER is defined */ if (bounce_env != NULL) /* if getsockopt() succeds */ if (getsockopt (sockfd, SOL_SOCKET, SO_TYPE, &optval, &optlen) != -1) /* and socket is SOCK_STREAM */ if (optval == SOCK_STREAM) { /* bounce'em baby */ in_use = YES; flags = fcntl (sockfd, F_GETFL); if (flags & O_NONBLOCK) /* if O_NONBLOCK is set */ fcntl (sockfd, F_SETFL, flags-O_NONBLOCK); /* unset it */ tmp = bounce (sockfd, (struct sockaddr_in *) serv_addr); if (flags & O_NONBLOCK) fcntl (sockfd, F_SETFL, flags); /* put O_NONBLOCK back in */ return tmp; } /* otherwise, use normal connect */ return old_connect (sockfd, serv_addr, addrlen); }