/* $Header: /CVSROOT/multiproxy/multiproxy.c,v 1.8 2008-10-20 23:44:33 tino Exp $ * * A multi proxy implementation. * * Yes, this is crude. And still not cleaned up for the slightest. * * Copyright (C)2006-2007 Valentin Hilbig * * 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 * * $Log: multiproxy.c,v $ * Revision 1.8 2008-10-20 23:44:33 tino * tino/auxbuf bugfix release * * Revision 1.7 2007-05-08 16:20:41 tino * Internal overhaul to better function, lingering added and max-forks * * Revision 1.6 2007/04/17 23:40:37 tino * Now child termination is reported immediately * * Revision 1.5 2007/04/17 23:23:58 tino * Debugging switched off * * Revision 1.4 2007/04/17 22:04:15 tino * Output improved * * Revision 1.3 2007/04/16 20:38:57 tino * Version with accept-fork-loop * * Revision 1.2 2006/10/21 01:56:37 tino * Checkin for save * * Revision 1.1 2006/01/24 07:32:24 tino * First version. It is working as expected. */ #undef MULTIPROXY_GSSAPI #undef MULTIPROXY_IPv6 #define MULTIPROXY_HEX #define TINO_DP_main TINO_DP_OFF #include "tino/file.h" #include "tino/debug.h" #include "tino/sock.h" #include "tino/sleep.h" #include "tino/signals.h" #include "tino/auxbuf.h" #include "tino/misc_selectcopyloop.h" #include #include #if 0 #include "tino/xd.h" #define XD(STR,POS,PTR,LEN) tino_xd(stderr,(STR),8,(POS),(PTR),(LEN)) #endif /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ static const char *authenticator; static char authprog[64]; static char authuser[64]; static char authpass[64]; static unsigned char *inbuf, outbuf[BUFSIZ]; static int inp, imax; static long me; static int childnr; /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ static void strxcpy(char *dest, const char *src, size_t max) { strncpy(dest, src, max); dest[max-1] = 0; } static void prefix(const char *tag, int e, const char *s, va_list list) { fprintf(stderr, "[%05ld]", me); if (childnr) fprintf(stderr, "[%d]", childnr); fprintf(stderr, " %s: ", tag); vfprintf(stderr, s, list); if (e) { errno = e; fprintf(stderr, ": "); perror(NULL); } else fprintf(stderr, "\n"); } static void verr_no_exit(const char *s, va_list list) { prefix("error", errno, s, list); fflush(stderr); } static void err_no_exit(const char *s, ...) { va_list list; int e; e = errno; va_start(list, s); verr_no_exit(s, list); va_end(list); errno = e; } static void vinfo(const char *s, va_list list) { prefix("info", 0, s, list); } static void info(const char *s, ...) { va_list list; va_start(list, s); vinfo(s, list); va_end(list); } /* Output error and terminate with error */ static void verr(const char *s, va_list list) { verr_no_exit(s, list); fflush(stderr); sleep(1); exit(1); } static void err(const char *s, ...) { va_list list; va_start(list, s); verr(s, list); /* never reached */ } /* Output internal error and terminate with error */ static void internal(const char *s, ...) { va_list list; va_start(list, s); prefix("internal error", errno, s, list); fflush(stderr); sleep(1); exit(2); } /* Send a warning, but return true */ static void warn(const char *s, ...) { va_list list; va_start(list, s); prefix("warn", 0, s, list); fflush(stderr); sleep(1); exit(0); } static void vend(const char *s, va_list list) { vinfo(s, list); fflush(stderr); sleep(1); exit(0); } static void end(const char *s, ...) { va_list list; va_start(list, s); vend(s, list); /* never reached */ } /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /* Shutdown socket and do lingering */ static void linger(int sec) { char buf[BUFSIZ*10]; close(1); tino_sock_shutdownE(0, SHUT_WR); /* just in case */ tino_sock_linger(0, sec*1000l); tino_sigfix(SIGALRM); alarm(sec); while (read(0, buf, sizeof buf)>0); sleep(sec); close(0); } /* fetch data from input */ static void fetch(int n) { if (n>imax) { imax = (n+BUFSIZ)& ~(BUFSIZ-1); inbuf = realloc(inbuf, imax); if (!inbuf) err("fetch: out of memory"); } while (inp0) { XD("<", inp, inbuf+inp, got); inp += got; continue; } if (!got) err("read: unexpected EOF"); if (errno==EINTR || errno==EAGAIN) continue; err("read"); } } /* commit read data and send information to output */ static void commit(int in, int out) { int n; if ((inp-=in)<0) internal("commit"); if (in && inp) memmove(inbuf, inbuf+in, inp); for (n=0; n", n, outbuf+n, put); if (put>0) { n += put; continue; } if (!put) err("write: unexpected EOF"); if (errno==EINTR || errno==EAGAIN) continue; err("write"); } } /* Call the authenticator with parameters */ static int auth(char *prog, ...) { int status; pid_t pid; char *args[100]; va_list list; int i; if (!authenticator) return 0; args[0] = prog ? prog : authprog; args[1] = authuser; args[2] = authpass; va_start(list, prog); for (i=2; ++i=sizeof args/sizeof *args) err("auth: too many args: %d", i); args[i] = 0; if ((pid=fork())==0) { tino_file_dup2E(2,0); tino_file_dup2E(2,1); execvp(authenticator, args); perror(authenticator); exit(2); } for (;;) { pid_t got; got = waitpid(pid, &status, 0); if (got==pid) break; if (got!=(pid_t)-1) { warn("unknown child %ld returned: $%x", (long)got, status); continue; } if (errno==EINTR || errno==EAGAIN) continue; err("waitpid returned %d", got); } return status; } static int auth_user(char *prog, const unsigned char *user, const unsigned char *pass) { strxcpy(authprog, prog, sizeof authprog); strxcpy(authuser, (char *)user, sizeof authuser); strxcpy(authpass, (char *)pass, sizeof authpass); return auth(prog, "user", NULL); } /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ static unsigned network_order_2(int i) { return (((unsigned)inbuf[i])<<8)|((unsigned)inbuf[i+1]); } /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ static const char * dumpaddr(struct addrinfo *adr) { char buf[BUFSIZ]; int i; snprintf(buf, sizeof buf, "[%d %d %d %d %ld %p[" , adr->ai_flags , adr->ai_family , adr->ai_socktype , adr->ai_protocol , (long)adr->ai_addrlen , adr->ai_addr ); /*getaddrinfo*/ for (i=0; iai_addrlen; i++) { snprintf(buf+strlen(buf), sizeof buf-strlen(buf), "%02x", ((unsigned char *)adr->ai_addr)[i]); } snprintf(buf+strlen(buf), sizeof buf-strlen(buf), "] %s", adr->ai_canonname); return tino_auxbuf_sOn(0, buf); } static int connecter(struct addrinfo *adr) { int sock; #if 0 fprintf(stderr, "%d %d %d %d %ld %p %s\n" , adr->ai_flags , adr->ai_family , adr->ai_socktype , adr->ai_protocol , (long)adr->ai_addrlen , adr->ai_addr , adr->ai_canonname ); #endif switch (adr->ai_family) { default: err_no_exit("unknown address family: %d %d %d", adr->ai_family, adr->ai_socktype, adr->ai_protocol); return -1; #ifdef MULTIPROXY_IPv6 case PF_INET6: #endif case PF_INET: break; } if ((sock=socket(adr->ai_family, adr->ai_socktype, adr->ai_protocol))==-1) { err_no_exit("cannot create socket: %d %d %d", adr->ai_family, adr->ai_socktype, adr->ai_protocol); return -1; } if (connect(sock, adr->ai_addr, adr->ai_addrlen)) { err_no_exit("cannot connect to %s", dumpaddr(adr)); tino_file_closeE(sock); return -1; } info("%d: connected to %s", sock, dumpaddr(adr)); return sock; } static void copy_exit(int sock) { const char *fail; long long total; total = 0; fail = selectcopyloop(0, sock, 1, 0, 0, &total); if (fail) err("copyloop: %s", fail); info("%lld bytes transferred", total); exit(0); } /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ #define MULTIPROXY_SOCKS5 #include "socks5.h" /**********************************************************************/ /**********************************************************************/ static void multiproxy(void) { if (!(0 MULTIPROXY_SOCKS5 )) err("no matching proxy method found"); } static void loop(const char *name, int max) { int sock; int childs; sock = tino_sock_tcp_listen(name); if (sock<0) { perror(name); exit(2); } childs = 0; tino_sigdummy(SIGCHLD); /* Interrupt accept() on sigchild */ for (;;) { int fd; pid_t pid; int status; char *peer; while (((int)(pid=waitpid(-1, &status, (max && childs>=max) ? 0 : WNOHANG)))>0) printf("[%05ld][0] childs left %d, child %ld status $%x\n", me, --childs, (long)pid, status); if ((fd=tino_sock_acceptI(sock))<0) { if (errno!=EINTR) perror(name); continue; } peer = tino_sock_get_peernameN(fd); printf("[%05ld][0] accept %d from %s\n", me, childs, peer); tino_freeO(peer); if ((pid=fork())==0) { me = getpid(); childnr = childs+1; info("started"); tino_file_closeE(sock); tino_file_dup2E(fd, 0); tino_file_dup2E(fd, 1); tino_file_closeE(fd); multiproxy(); info("ended"); exit(0); } tino_file_closeE(fd); if (pid==(pid_t)-1) perror("fork"); else printf("[%05ld][0] forked child %d: %ld\n", me, ++childs, (long)pid); tino_relax(); } } int main(int argc, char **argv) { if (argc<2 || argc>4) { fprintf(stderr, "Usage: %s authenticator [[host]:port|unixsocket [maxcount]]\n" "\tuse authenticator '' if there is no authenticator\n" "\tIf [host]:port or unixsocket are given, listens on it\n" "\tOnly maxcount forks are done in parallel if maxcount!=0.\n" , argv[0]); return 1; } authenticator = argv[1][0] ? argv[1] : 0; me = getpid(); if (argc>2) loop(argv[2], argc>3 ? atoi(argv[3]) : 0); else multiproxy(); return 0; }