micro_proxy/0000755000175000017500000000000010154505522012701 5ustar danieldanielmicro_proxy/micro_proxy.80000644000175000017500000000532310260555147015355 0ustar danieldaniel.TH micro_proxy 8 "16 March 1999" .SH NAME micro_proxy - really small HTTP/HTTPS proxy .SH SYNOPSIS .B micro_proxy .SH DESCRIPTION .PP .I micro_proxy is a very small HTTP/HTTPS proxy. It runs from inetd, which means its performance is poor. But for low-traffic sites, it's quite adequate. It implements all the basic features of an HTTP/HTTPS proxy, in only 260 lines of code. .PP To install it, add a line like this to /etc/inetd.conf: .nf webproxy stream tcp nowait nobody /usr/local/sbin/micro_proxy micro_proxy .fi Make sure the path to the executable is correct. Then add a line like this to /etc/services: .nf webproxy port/tcp .fi Change "port" to the port number you want to use - 3128, or whatever. Then restart inetd by sending it a "HUP" signal, or rebooting. .PP On some systems, inetd has a maximum spawn rate - if you try to run inetd services faster than a certain number of times per minute, it assumed there's either a bug of an attack going on and it shuts down for a few minutes. If you run into this problem - look for syslog messages about too-rapid looping - you'll need to find out how to increase the limit. Unfortunately this varies from OS to OS. On FreeBSD, you add a "-R 10000" flag to inetd's initial command line. On some Linux systems, you can set the limit on a per-service basis in inetd.conf, by changing "nowait" to "nowait.10000". .SH AUTHOR Copyright © 1999 by Jef Poskanzer . All rights reserved. .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. micro_proxy/Makefile0000644000175000017500000000124107415243327014347 0ustar danieldaniel# Makefile for micro_proxy # CONFIGURE: If you are using a SystemV-based operating system, such as # Solaris, you will need to uncomment this definition. #SYSV_LIBS = -lnsl -lsocket BINDIR = /usr/local/sbin MANDIR = /usr/local/man/man8 CC = gcc #CFLAGS = -O CFLAGS = -g #LDFLAGS = -s $(SYSV_LIBS) LDFLAGS = -g $(SYSV_LIBS) all: micro_proxy micro_proxy: micro_proxy.o $(CC) $(CFLAGS) micro_proxy.o $(LDFLAGS) -o micro_proxy micro_proxy.o: micro_proxy.c $(CC) $(CFLAGS) -c micro_proxy.c install: all rm -f $(BINDIR)/micro_proxy cp micro_proxy $(BINDIR) rm -f $(MANDIR)/micro_proxy.8 cp micro_proxy.8 $(MANDIR) clean: rm -f micro_proxy *.o core core.* *.core micro_proxy/README0000644000175000017500000000146110260555142013564 0ustar danieldaniel micro_proxy - really small HTTP/HTTPS proxy micro_proxy is a very small HTTP/HTTPS proxy. It runs from inetd, which means its performance is poor. But for low-traffic sites, it's quite adequate. It implements all the basic features of an HTTP/HTTPS proxy, in only 260 lines of code. See the manual entry for more details. Files in this distribution: README this Makefile guess micro_proxy.c source file micro_proxy.8 manual entry To build: If you're on a SysV-like machine (which includes old Linux systems but not new Linux systems), edit the Makefile and uncomment the SYSV_LIBS line. Otherwise, just do a make. Feedback is welcome - send bug reports, enhancements, checks, money orders, etc. to the addresses below. Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/ micro_proxy/micro_proxy.c0000644000175000017500000003216310260555153015427 0ustar danieldaniel/* micro_proxy - really small HTTP proxy ** ** Copyright © 1999 by Jef Poskanzer . ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. 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. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #define SERVER_NAME "micro_proxy" #define SERVER_URL "http://www.acme.com/software/micro_proxy/" #define PROTOCOL "HTTP/1.0" #define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT" #define TIMEOUT 300 /* Forwards. */ static int open_client_socket( char* hostname, unsigned short port ); static void proxy_http( char* method, char* path, char* protocol, FILE* sockrfp, FILE* sockwfp ); static void proxy_ssl( char* method, char* host, char* protocol, FILE* sockrfp, FILE* sockwfp ); static void sigcatch( int sig ); static void trim( char* line ); static void send_error( int status, char* title, char* extra_header, char* text ); static void send_headers( int status, char* title, char* extra_header, char* mime_type, int length, time_t mod ); int main( int argc, char** argv ) { char line[10000], method[10000], url[10000], protocol[10000], host[10000], path[10000]; char* cp; unsigned short port; int iport; int sockfd, state; int ssl; FILE* sockrfp; FILE* sockwfp; /* Read the first line of the request. */ if ( fgets( line, sizeof(line), stdin ) == (char*) 0 ) send_error( 400, "Bad Request", (char*) 0, "No request found." ); /* Parse it. */ trim( line ); if ( sscanf( line, "%[^ ] %[^ ] %[^ ]", method, url, protocol ) != 3 ) send_error( 400, "Bad Request", (char*) 0, "Can't parse request." ); if ( url == (char*) 0 ) send_error( 400, "Bad Request", (char*) 0, "Null URL." ); openlog( "micro_proxy", 0, LOG_DAEMON ); syslog( LOG_INFO, "proxying %s", url ); if ( strncasecmp( url, "http://", 7 ) == 0 ) { (void) strncpy( url, "http", 4 ); /* make sure it's lower case */ if ( sscanf( url, "http://%[^:/]:%d%s", host, &iport, path ) == 3 ) port = (unsigned short) iport; else if ( sscanf( url, "http://%[^/]%s", host, path ) == 2 ) port = 80; else if ( sscanf( url, "http://%[^:/]:%d", host, &iport ) == 2 ) { port = (unsigned short) iport; *path = '\0'; } else if ( sscanf( url, "http://%[^/]", host ) == 1 ) { port = 80; *path = '\0'; } else send_error( 400, "Bad Request", (char*) 0, "Can't parse URL." ); ssl = 0; } else if ( strcmp( method, "CONNECT" ) == 0 ) { if ( sscanf( url, "%[^:]:%d", host, &iport ) == 2 ) port = (unsigned short) iport; else if ( sscanf( url, "%s", host ) == 1 ) port = 443; else send_error( 400, "Bad Request", (char*) 0, "Can't parse URL." ); ssl = 1; } else send_error( 400, "Bad Request", (char*) 0, "Unknown URL type." ); /* Get ready to catch timeouts.. */ (void) signal( SIGALRM, sigcatch ); /* Open the client socket to the real web server. */ (void) alarm( TIMEOUT ); sockfd = open_client_socket( host, port ); /* Open separate streams for read and write, r+ doesn't always work. */ sockrfp = fdopen( sockfd, "r" ); sockwfp = fdopen( sockfd, "w" ); if ( ssl ) proxy_ssl( method, host, protocol, sockrfp, sockwfp ); else proxy_http( method, path, protocol, sockrfp, sockwfp ); /* Done. */ (void) close( sockfd ); exit( 0 ); } #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) #define USE_IPV6 #endif static int open_client_socket( char* hostname, unsigned short port ) { #ifdef USE_IPV6 struct addrinfo hints; char portstr[10]; int gaierr; struct addrinfo* ai; struct addrinfo* ai2; struct addrinfo* aiv4; struct addrinfo* aiv6; struct sockaddr_in6 sa; #else /* USE_IPV6 */ struct hostent *he; struct sockaddr_in sa; #endif /* USE_IPV6 */ int sa_len, sock_family, sock_type, sock_protocol; int sockfd; (void) memset( (void*) &sa, 0, sizeof(sa) ); #ifdef USE_IPV6 (void) memset( &hints, 0, sizeof(hints) ); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; (void) snprintf( portstr, sizeof(portstr), "%d", (int) port ); if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 ) send_error( 404, "Not Found", (char*) 0, "Unknown host." ); /* Find the first IPv4 and IPv6 entries. */ aiv4 = (struct addrinfo*) 0; aiv6 = (struct addrinfo*) 0; for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next ) { switch ( ai2->ai_family ) { case AF_INET: if ( aiv4 == (struct addrinfo*) 0 ) aiv4 = ai2; break; case AF_INET6: if ( aiv6 == (struct addrinfo*) 0 ) aiv6 = ai2; break; } } /* If there's an IPv4 address, use that, otherwise try IPv6. */ if ( aiv4 != (struct addrinfo*) 0 ) { if ( sizeof(sa) < aiv4->ai_addrlen ) { (void) fprintf( stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv4->ai_addrlen ); exit( 1 ); } sock_family = aiv4->ai_family; sock_type = aiv4->ai_socktype; sock_protocol = aiv4->ai_protocol; sa_len = aiv4->ai_addrlen; (void) memmove( &sa, aiv4->ai_addr, sa_len ); goto ok; } if ( aiv6 != (struct addrinfo*) 0 ) { if ( sizeof(sa) < aiv6->ai_addrlen ) { (void) fprintf( stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv6->ai_addrlen ); exit( 1 ); } sock_family = aiv6->ai_family; sock_type = aiv6->ai_socktype; sock_protocol = aiv6->ai_protocol; sa_len = aiv6->ai_addrlen; (void) memmove( &sa, aiv6->ai_addr, sa_len ); goto ok; } send_error( 404, "Not Found", (char*) 0, "Unknown host." ); ok: freeaddrinfo( ai ); #else /* USE_IPV6 */ he = gethostbyname( hostname ); if ( he == (struct hostent*) 0 ) send_error( 404, "Not Found", (char*) 0, "Unknown host." ); sock_family = sa.sin_family = he->h_addrtype; sock_type = SOCK_STREAM; sock_protocol = 0; sa_len = sizeof(sa); (void) memmove( &sa.sin_addr, he->h_addr, he->h_length ); sa.sin_port = htons( port ); #endif /* USE_IPV6 */ sockfd = socket( sock_family, sock_type, sock_protocol ); if ( sockfd < 0 ) send_error( 500, "Internal Error", (char*) 0, "Couldn't create socket." ); if ( connect( sockfd, (struct sockaddr*) &sa, sa_len ) < 0 ) send_error( 503, "Service Unavailable", (char*) 0, "Connection refused." ); return sockfd; } static void proxy_http( char* method, char* path, char* protocol, FILE* sockrfp, FILE* sockwfp ) { char line[10000], protocol2[10000], comment[10000]; int first_line, status, ich; long content_length, i; /* Send request. */ (void) alarm( TIMEOUT ); (void) fprintf( sockwfp, "%s %s %s\r\n", method, path, protocol ); /* Forward the remainder of the request from the client. */ content_length = -1; while ( fgets( line, sizeof(line), stdin ) != (char*) 0 ) { if ( strcmp( line, "\n" ) == 0 || strcmp( line, "\r\n" ) == 0 ) break; (void) fputs( line, sockwfp ); (void) alarm( TIMEOUT ); trim( line ); if ( strncasecmp( line, "Content-Length:", 15 ) == 0 ) content_length = atol( &(line[15]) ); } (void) fputs( line, sockwfp ); (void) fflush( sockwfp ); /* If there's content, forward that too. */ if ( content_length != -1 ) for ( i = 0; i < content_length && ( ich = getchar() ) != EOF; ++i ) putc( ich, sockwfp ); (void) fflush( sockwfp ); /* Forward the response back to the client. */ (void) alarm( TIMEOUT ); content_length = -1; first_line = 1; status = -1; while ( fgets( line, sizeof(line), sockrfp ) != (char*) 0 ) { if ( strcmp( line, "\n" ) == 0 || strcmp( line, "\r\n" ) == 0 ) break; (void) fputs( line, stdout ); (void) alarm( TIMEOUT ); trim( line ); if ( first_line ) { (void) sscanf( line, "%[^ ] %d %s", protocol2, &status, comment ); first_line = 0; } if ( strncasecmp( line, "Content-Length:", 15 ) == 0 ) content_length = atol( &(line[15]) ); } /* Add a response header. */ (void) fputs( "Connection: close\r\n", stdout ); (void) fputs( line, stdout ); (void) fflush( stdout ); /* Under certain circumstances we don't look for the contents, even ** if there was a Content-Length. */ if ( strcasecmp( method, "HEAD" ) != 0 && status != 304 ) { /* Forward the content too, either counted or until EOF. */ for ( i = 0; ( content_length == -1 || i < content_length ) && ( ich = getc( sockrfp ) ) != EOF; ++i ) { putchar( ich ); if ( i % 10000 == 0 ) (void) alarm( TIMEOUT ); } } (void) fflush( stdout ); } static void proxy_ssl( char* method, char* host, char* protocol, FILE* sockrfp, FILE* sockwfp ) { int client_read_fd, server_read_fd, client_write_fd, server_write_fd; struct timeval timeout; fd_set fdset; int maxp1, r; char buf[10000]; /* Return SSL-proxy greeting header. */ (void) fputs( "HTTP/1.0 200 Connection established\r\n\r\n", stdout ); (void) fflush( stdout ); /* Now forward SSL packets in both directions until done. */ client_read_fd = fileno( stdin ); server_read_fd = fileno( sockrfp ); client_write_fd = fileno( stdout ); server_write_fd = fileno( sockwfp ); timeout.tv_sec = TIMEOUT; timeout.tv_usec = 0; if ( client_read_fd >= server_read_fd ) maxp1 = client_read_fd + 1; else maxp1 = server_read_fd + 1; (void) alarm( 0 ); for (;;) { FD_ZERO( &fdset ); FD_SET( client_read_fd, &fdset ); FD_SET( server_read_fd, &fdset ); r = select( maxp1, &fdset, (fd_set*) 0, (fd_set*) 0, &timeout ); if ( r == 0 ) send_error( 408, "Request Timeout", (char*) 0, "Request timed out." ); else if ( FD_ISSET( client_read_fd, &fdset ) ) { r = read( client_read_fd, buf, sizeof( buf ) ); if ( r <= 0 ) break; r = write( server_write_fd, buf, r ); if ( r <= 0 ) break; } else if ( FD_ISSET( server_read_fd, &fdset ) ) { r = read( server_read_fd, buf, sizeof( buf ) ); if ( r <= 0 ) break; r = write( client_write_fd, buf, r ); if ( r <= 0 ) break; } } } static void sigcatch( int sig ) { send_error( 408, "Request Timeout", (char*) 0, "Request timed out." ); } static void trim( char* line ) { int l; l = strlen( line ); while ( line[l-1] == '\n' || line[l-1] == '\r' ) line[--l] = '\0'; } static void send_error( int status, char* title, char* extra_header, char* text ) { send_headers( status, title, extra_header, "text/html", -1, -1 ); (void) printf( "\ \n\ %d %s\n\ \n\

%d %s

\n", status, title, status, title ); (void) printf( "%s\n", text ); (void) printf( "\
\n\
%s
\n\ \n\ \n", SERVER_URL, SERVER_NAME ); (void) fflush( stdout ); exit( 1 ); } static void send_headers( int status, char* title, char* extra_header, char* mime_type, int length, time_t mod ) { time_t now; char timebuf[100]; (void) printf( "%s %d %s\r\n", PROTOCOL, status, title ); (void) printf( "Server: %s\r\n", SERVER_NAME ); now = time( (time_t*) 0 ); (void) strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &now ) ); (void) printf( "Date: %s\r\n", timebuf ); if ( extra_header != (char*) 0 ) (void) printf( "%s\r\n", extra_header ); if ( mime_type != (char*) 0 ) (void) printf( "Content-Type: %s\r\n", mime_type ); if ( length >= 0 ) (void) printf( "Content-Length: %d\r\n", length ); if ( mod != (time_t) -1 ) { (void) strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &mod ) ); (void) printf( "Last-Modified: %s\r\n", timebuf ); } (void) printf( "Connection: close\r\n" ); (void) printf( "\r\n" ); }