/* Proxy Tunnel redirectior. Complemental program for ptun. * (c) 2001, Luciano Rocha * Released under the GNU GPL * compile: cc -O2 -o predir predir.c * * This program's purpose is to be used by ptun to use a proxy server to * redirect connections to any defined port. * * Some administrators limit outgoing and incomming connectings normally * only allowing outgoing connections to web servers. * Others limit even more and only allow you to connect through a proxy server. * A small program that allows you to use a proxy to connect to other hosts * Of course, decent administrators configure the proxy correctly and will * only allow connections the port 80 and 443 (SSL) of webservers. * So, in that case, you can use my ptun program to do something like redirect * port 2200 on your machine to port 443 in a remote machine of yours. And * if you have a sshd deamon running on that port (or redirecting connections * from that port to port 22), you'll be able to remotely connect with ssh to * your machine using the proxy. * * Now, the problem is that you can only have one redirector or one program * listening on a given port, and normally only allow CONNECT to SSL ports * (443 and 563). The CONNECT is required by my ptun program, so you'll only * be able to use two daemons, at most. * * There's where this program comes in. It allows you to specify services * and their host and port. So you'll be able to use that only port, 443, * to connect to any service! And my program, ptun, suports just that, you'll * just have to specify the service as the last argument. * * Of course, if your administrator is lazy, you'll may be able to use ptun * to any host and port without needing this. * * Please note that all SSL ports that I know off are below 1024, so besides * needing a remote server, you'll also need to be able to bind to those * ports, which normally means you'll have to be root... * * There will be a default service that will be used in the case an invalid * service is given or no service at all. So, you'll be able to keep using * your SSL webserver by telling it to use another port for SSL and telling * predir to connect to that port by default. You'll have to live with only * localhost/127.0.0.1 showing up in the logs, though... * * How to use it: * compile (see above) * $ ./predir port service:host:port .... * * The first port is where the program will receive incoming connections. * * You may leave service, host or port empty. The service will default to * the port defined (the name of the service belonging to that port, if any), * the host will default to localhost and the port will default to the port * belonging to the service specified. If only the host is specified, * the port and service will default to the one the program will receive * connections. * * The default service will be the first one that you specify. * * example: * $ predir https https:444 ssh imap * * The program will bind to port https/443 and will create three services: * https (HTTP + SSL) to port 444, ssh (22) and imap (143). The default * if none service is specified by ptun or if an invalid one is specified * will be https. * * 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. * */ #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" #define NL 24 #define PL 6 struct service { char name[NL]; struct sockaddr_in sa; struct service *next; } *services; static void do_copy(int, int); static struct service *getnewservice(struct service *, char *); static void runservice(int); static unsigned short getport(const char *); static char *lport, *program; int main(int ac, char *av[]) { struct sockaddr_in lis, con; int len, ls, cn; program = av[0]; if (ac == 1) { fprintf(stderr, "usage: %s [service:][host:][port]\n", program); return 0; } if (ac == 2) { fprintf(stderr, "invalid use.\n%s for details\n", program); return 1; } lport = av[1]; memset(&lis, '\0', sizeof lis); lis.sin_family = AF_INET; lis.sin_addr.s_addr = INADDR_ANY; if (!(lis.sin_port = getport(lport))) return 1; ++av; while (*++av) { if (!(services = getnewservice(services, *av))) return 1; } if (!services) { fprintf(stderr, "usage: %s [service:][host:][port]\n", program); return 1; } 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); runservice(cn); exit(0); default: close(cn); if (fork() > 0) exit(0); } } return 0; } #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif static 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); } static 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); } static int setport(struct service *v, const char *s) { unsigned short i; for (i = 0; isdigit(s[i]); ++i); if (s[i]) { struct servent *serv; if (!(serv = getservbyname(s, "tcp"))) return 0; v->sa.sin_port = serv->s_port; } else v->sa.sin_port = htons(atoi(s)); return 1; } static int setserv(struct service *v, const char *s) { unsigned short i; for (i = 0; isdigit(s[i]); ++i); if (!s[i]) { struct servent *serv; if ((serv = getservbyport(atoi(s), "tcp"))) s = serv->s_name; } else { for (i = 0; isalnum(s[i]); ++i); /* probably a hostname */ if (s[i] == '.') return 0; } if ((i = strlen(s)) >= NL) i = NL - 1; memcpy(v->name, s, i); v->name[i] = '\0'; return 1; } static int sethost(struct service *v, const char *s) { struct hostent *host; int i; for (i = 0; isdigit(s[i]); ++i); if (!s[i]) return 0; if (!(host = gethostbyname(s))) { return 0; } if (host->h_addrtype != AF_INET || host->h_length > sizeof v->sa.sin_addr.s_addr) { fprintf(stderr, "%s is not a valid host address\n", s); return 0; } memcpy(&(v->sa.sin_addr.s_addr), host->h_addr_list[0], host->h_length); return 1; } static struct service *getnewservice(struct service *l, char *s) { struct service *n, *t; char *a, *b, *c; if (!s) return l; if (!(n = (struct service *) malloc(sizeof(struct service)))) { fprintf(stderr, "Not enough memory for %s\n", s); return NULL; } memset(n, '\0', sizeof(struct service)); b = c = NULL; a = s; while (*s && *s != ':') ++s; if (*s) { *s++ = '\0'; b = s; while (*s && *s != ':') ++s; if (*s) { *s++ = '\0'; c = s; while (*s && *s != ':') ++s; if (*s) { } } } #define S(a) setserv(n, a) #define H(a) sethost(n, a) #define P(a) setport(n, a) #define LH "127.0.0.1" if (!( (c && S(a) && H(b) && P(c)) || (!c && b && ( (H(a) && P(b) && S(b)) || (H(b) && P(a) && S(a)) || (S(a) && P(b) && H(LH)) ) ) || (!c && !b && ( (P(a) && S(a) && H(LH)) || (H(a) && S(lport) && P(lport)) ) ))) { fprintf(stderr, "invalid use.\n" "%s for details\n", program); return NULL; } n->sa.sin_family = AF_INET; if (l) { t = l; while (t->next) t = t->next; t->next = n; return l; } return n; } static void runservice(int s) { int nc, i, j; char b[32], c; struct service *l = services; if ((i = read(s, b, sizeof b)) < 1) { close(s); return; } for (j = 0; j < i && isalnum(b[j]); ++j); if (j > 0) { c = b[j]; b[j] = '\0'; for (l = services; l && strcmp(b, l->name); l = l->next); if (l) { b[j] = c; if (b[j] == '\r') ++j; if (b[j] == '\n') ++j; } else { l = services; b[j] = c; j = 0; } } if ((nc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { write(s, SERROR, strlen(SERROR)); close(s); return; } if (connect(nc, (struct sockaddr *) &(l->sa), sizeof l->sa)) { write(s, CERROR, strlen(CERROR)); close(s); close(nc); return; } if (j < i) { write(nc, b+j, i-j); } do_copy(s, nc); }