/* Virtual tunnel for proxys supporting CONNECT method * (c) 2001, Luciano Rocha * Released under the GNU GPL * compile: cc -O2 -o ptun ptun.c * * A small program that allows you to use a proxy to connect to other hosts * The proxy *must* support the CONNECT method and allow you to * CONNECT to the host and port you want to connect to. * * How to use it: * compile (see above) * $ ./ptun [ host:port [ local_port [ proxy_ip [ proxy_port [ msg ] ] ] ] ] * * The defaults are defined and changeable blow and are: * ./ptun 192.168.1.1:22 2200 10.0.0.1 3128 * * After executing, all you have to do is 'ssh localhost -p 2200' and you'll * be connected to 192.168.1.1:22! * * You may also specify a string (msg) to send after the connection is * established, so you can, for example, have a special daemon running on * one host on port 443 and tell it to connect to some service for you. * (You can find such a program in http://strange.nsk.yi.org/) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #define PROXY "10.0.0.1" #define PPORT "3128" #define LPORT "2200" #define CHOST "192.168.1.1:22" #ifndef BIND #define BIND "127.0.0.1" #endif //#undef BIND /* Bind to INADDR_ANY: possibly dangerous! */ #include #include #include #include #include #include #include #include #include #include #include #include #define FERROR "Fork error\r\n" #define CERROR "Connection error\r\n" #define SERROR "Socket error\r\n" void do_copy(int, int); void get_ok(int s) { char c; while (read(s, &c, 1) > 0 && c != '\n'); while (read(s, &c, 1) > 0 && c != '\n'); } unsigned long gethost(const char *); unsigned short getport(const char *); int main(int ac, char *av[]) { struct sockaddr_in lis, con, ncon; int len, ls, nc, cn, cslen, sndlen = 0; char *cs, *snd = NULL; memset(&lis, '\0', sizeof lis); memset(&nc, '\0', sizeof nc); lis.sin_family = ncon.sin_family = AF_INET; #ifdef BIND lis.sin_addr.s_addr = inet_addr(BIND); #else lis.sin_addr.s_addr = INADDR_ANY; #endif if ((lis.sin_port = getport(ac > 2 ? av[2] : LPORT)) == 0) return 1; if ((ncon.sin_addr.s_addr = gethost(ac > 3 ? av[3] : PROXY)) == 0 || (ncon.sin_port = getport(ac > 4 ? av[4] : PPORT)) == 0) return 1; if (ac < 2) av[1] = CHOST; cslen = strlen(av[1]) + 22; if (!(cs = (char *) malloc(cslen--))) { fprintf(stderr, "Not enough memory for CONNECT string\n"); return 1; } sprintf(cs, "CONNECT %s HTTP/1.0\r\n\r\n", av[1]); if (ac > 5) { sndlen = strlen(av[5]) + 2; if (!(snd = (char *) malloc(sndlen--))) { fprintf(stderr, "Not enough memory for msg string\n"); return 1; } sprintf(snd, "%s\n", av[5]); } if ((ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket(2)"); return 1; } len = 1; if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &len, sizeof len)) { perror("setsockopt(2)"); } if (bind(ls, (struct sockaddr *) &lis, sizeof lis)) { perror("bind(2)"); close(ls); return 2; } if (listen(ls, 4)) { perror("listen(2)"); close(ls); return 3; } daemon(0, 0); for (;;) { memset(&con, '\0', len = sizeof con); if ((cn = accept(ls, (struct sockaddr *) &con, &len)) < 0) continue; switch (fork()) { case -1: /* Error */ write(cn, FERROR, strlen(FERROR)); close(cn); break; case 0: close(ls); if ((nc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { write(cn, SERROR, strlen(SERROR)); close(cn); exit(1); } if (connect(nc, (struct sockaddr *) &ncon, sizeof ncon)) { write(cn, CERROR, strlen(CERROR)); close(cn); close(nc); exit(1); } write(nc, cs, cslen); get_ok(nc); if (snd) { write(nc, snd, sndlen); } do_copy(nc, cn); exit(0); default: close(cn); if (fork() > 0) exit(0); } } return 0; } #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif void do_copy(int a, int b) { char buf[64]; fd_set s; int i, m; m = MAX(a, b) + 1; while (1) { FD_ZERO(&s); FD_SET(a, &s); FD_SET(b, &s); if (select(m, &s, NULL, NULL, NULL) <= 0) continue; if (FD_ISSET(a, &s)) { if ((i = read(a, buf, sizeof buf)) <= 0 || write(b, buf, i) != i) break; } if (FD_ISSET(b, &s)) { if ((i = read(b, buf, sizeof buf)) <= 0 || write(a, buf, i) != i) break; } } close(a); close(b); } unsigned long gethost(const char *h) { struct hostent *host; unsigned long r; if (!(host = gethostbyname(h))) { perror(h); return 0; } if (host->h_addrtype != AF_INET || host->h_length != sizeof r) { fprintf(stderr, "%s is not an IPv4 address\n", h); return 0; } memcpy(&r, host->h_addr_list[0], sizeof r); return r; } unsigned short getport(const char *p) { struct servent *serv; int i; for (i = 0; isdigit(p[i]); ++i); if (p[i]) { if (!(serv = getservbyname(p, "tcp"))) { fprintf(stderr, "unkown service: %s\n", p); return 0; } return serv->s_port; } i = strtol(p, NULL, 0); if (i < 1 || i > 65535) { fprintf(stderr, "invalid port: %s\n", p); return 0; } return htons(i); }