/* * This program is a CGI script that proxies HTTP requests to a * remote host. * * In DECC, this program must be compiled /prefix=all to get the socket * routines. * * Add the following rule to your configuration file to enable this script: * ProxyScript /htbin/proxygw/ * * Author: David Jones * Date: 16-FEB-1998 */ #include #include #include #include #include #include #include #include #include #include #include "scriptlib.h" /* net_link_... routines */ struct string { int l; char *s; }; static int open_remote(), send_query(), read_response(); int http_parse_url ( char *url, /* locator to parse */ char *info, /* Scratch area for result pts*/ char **service, /* Protocol (e.g. http) indicator */ char **node, /* Node name. */ char **ident, /* File specification. */ char **arg ); /* Search argument */ /********************************************************************/ /* Main routine. */ int main ( int argc, char **argv ) { int sd, tcnt, length, i, j, used, status, content_length; char *inp, *cmp, *ident, *uarg, *node, *scheme; char orig_url[4096], url_parts[5000]; char hdrbuf[8192]; struct string hdr[128]; char bin_path[256], remhost[256]; /* * Validate command line arguments. The target machine * is appended to the script name that invokes this program. */ if ( argc < 3 ) { fprintf( stderr, "Missing arguments, usage: prog method url\n"); exit ( 1 ); } /* * Open network link and get original URL and headers, noting along * the way if a content-length present. */ status = net_link_open(); if ( (status&1) == 0 ) { perror("Network connect fail"); exit (status); } status = net_link_query ( "", orig_url, sizeof(orig_url)-1, &length ); if ( (status&1) == 0 ) { perror("Network I/O failure"); exit (status); } if ( length < 0 ) exit ( 1 ); orig_url[length] = '\0'; cmp = "CONTENT-LENGTH:"; content_length = 0; j = 0; used = 0; for ( status = net_link_query ("",hdrbuf,sizeof(hdrbuf),&length); ((status&1) == 1) && (length > 0); status = net_link_read(&hdrbuf[used], sizeof(hdrbuf)-used-1, &length)) { /* * Add to string list. Terminate strings with null chars for safety. */ hdr[j].l = length; hdr[j].s = &hdrbuf[used]; hdrbuf[used+length] = '\0'; used += (length + 1); /* * Check if line is 'content-length:' and save. */ for ( i = 0; i < length; i++ ) { if ( cmp[i] != _toupper(hdr[j].s[i]) ) break; if ( cmp[i] == ':' ) { content_length = atoi ( &hdr[j].s[i+1] ); break; } } j++; /* advance to next string */ } #ifdef ECHO_TEST /* * For now, echo back the info. */ status = net_link_write ( "", 10 ); if ( (status&1) == 0 ) return status; status = net_link_write ( "200 proxy request echo\r\n", 24 ); status = net_link_write ( orig_url, strlen(orig_url) ); if ( (status&1) == 0 ) return status; for ( i = 0; i < j; i++ ) { status = net_link_write ( hdr[i].s, hdr[i].l ); if ( (status&1) == 0 ) break; } status = net_link_write ( "", 11 ); return status; #else /* * Parse original URL. */ status = http_parse_url ( orig_url, url_parts, &scheme, &node, &ident, &uarg ); if ( !status || !scheme || !node ) { /* * Couldn't make sense of request. */ net_link_write ( "", 10 ); net_link_write ( "400 Invalid proxy request syntax", 32 ); net_link_write ( "", 11 ); return status; } cmp = "HTTP"; for ( i = 0; scheme[i]; i++ ) { if ( cmp[i] != _toupper(scheme[i]) ) break; } if ( i != 4 ) { /* * Only know how to do HTTP */ net_link_write ( "", 10 ); net_link_write ( "500 unsupported scheme requested", 32 ); net_link_write ( "", 11 ); return status; } /* * Open connection. */ sd = open_remote ( node ); if ( sd < 0 ) { /* * Network error. */ net_link_write ( "", 10 ); net_link_write ( "500 error connecting to remote ", 32 ); net_link_write ( node, strlen(node) ); net_link_write ( "", 11 ); return status; } /* * Send request. method and protocol version were on command line. */ status = send_query ( sd, hdr, j, argv[1], ident, argv[3] ); if ( (status&1) == 1 ) status = read_response ( sd ); return status; #endif } /*****************************************************************************/ /* Create socket and connect to remote host. */ static int open_remote ( char *remhost ) { struct sockaddr local, remote; struct hostent *hostinfo; int i, j, rem_port, sd, status; char *alt_port; /* */ rem_port = 80; alt_port = strchr ( remhost, ':' ); if ( alt_port ) { *alt_port++ = '\0'; rem_port = atoi ( alt_port ); } hostinfo = gethostbyname ( remhost ); if ( !hostinfo ) { fprintf(stderr, "Could not find host '%s'\n", remhost ); return -1; } /* * Attempt connect to remote host. */ sd = socket ( AF_INET, SOCK_STREAM, 0 ); if ( sd < 0 ) { fprintf(stderr,"Error creating socket: %s\n", strerror(errno) ); return sd; } local.sa_family = AF_INET; for ( j = 0; j < sizeof(local.sa_data); j++ ) local.sa_data[j] = '\0'; local.sa_data[0] = local.sa_data[1] = 0; status = bind ( sd, &local, sizeof ( local ) ); if ( status < 0 ) { fprintf(stderr,"Error binding socket: %s\n", strerror(errno) ); return -1; } remote.sa_family = hostinfo->h_addrtype; for ( j = 0; j < hostinfo->h_length; j++ ) { remote.sa_data[j+2] = hostinfo->h_addr_list[0][j]; } remote.sa_data[0] = rem_port >> 8; remote.sa_data[1] = (rem_port&255); status = connect ( sd, &remote, sizeof(remote) ); if ( status != 0 ) { return -1; } return sd; } /*****************************************************************************/ /* Get request information and send query. */ static int send_query ( int sd, struct string *hdr, int hdrcnt, char *method, char *path, char *version ) { int content_length, j, i, status, length, written; char *cmp, buffer[4096]; /* * Send initial part of request. */ strcpy ( buffer, method ); strcat ( buffer, " " ); strcat ( buffer, path ); strcat ( buffer, " " ); strcat ( buffer, version ); strcat ( buffer, "\r\n" ); length = strlen ( buffer ); written = write ( sd, buffer, length ); if ( written != length ) return 0; /* * Fetch header lines. */ cmp = "CONTENT-LENGTH:"; content_length = 0; for ( j = 0; j < hdrcnt; j++ ) { /* * Relay to remote. */ length = hdr[j].l; strncpy ( buffer, hdr[j].s, length ); buffer[length++] = '\r'; buffer[length++] = '\n'; written = write ( sd, buffer, length ); if ( written != length ) return 0; /* * Check if line is 'content-length:' and save. */ buffer[length-2] = '\0'; for ( i = 0; i < length; i++ ) { if ( cmp[i] != _toupper(buffer[i]) ) break; if ( cmp[i] == ':' ) { content_length = atoi ( &buffer[i+1] ); break; } } } written = write ( sd, "\r\n", 2 ); /* * Send additional. */ while ( content_length > 0 ) { status = net_link_query ( "", buffer, sizeof(buffer), &length ); if ( (status&1) == 0 ) break; if ( length > 0 ) { written = write ( sd, buffer, length ); if ( written != length ) return 0; content_length = content_length - length; } } return 1; } /*****************************************************************************/ static int read_response ( int sd ) { int status, length; char buffer[4096]; status = net_link_write ( "", 9 ); if ( (status&1) == 1 ) status = net_link_set_rundown ( "" ); while ( (length = read ( sd, buffer, sizeof(buffer))) > 0 ) { status = net_link_write ( buffer, length ); if ( (status&1) == 0 ) break; } return status; } /****************************************************************************/ int http_parse_url ( char *url, /* locator to parse */ char *info, /* Scratch area for result pts*/ char **service, /* Protocol (e.g. http) indicator */ char **node, /* Node name. */ char **ident, /* File specification. */ char **arg ) /* Search argument */ { int i, state; char *last_slash, *p, c, arg_c; /* * Copy contents of url into info area. */ *service = *node = *ident = *arg = last_slash = ""; for ( state = i = 0; (info[i] = url[i]) != 0; ) { c = info[i]; switch ( state ) { case 0: if ( c == ':' ) { info[i] = '\0'; /* terminate string */ *service = info; state = 1; } case 1: if ( c == '/' ) { *ident = last_slash = &info[i]; state = 2; } break; case 2: state = 4; if ( c == '/' ) { /* 2 slashes in a row */ *node = (*ident)+1; state = 3; } else if ( (c == '#') || (c == '?') ) { arg_c = c; info[i] = '\0'; *arg = &info[i+1]; state = 5; } break; case 3: /* find end of host spec */ if ( c == '/' ) { state = 4; *ident = last_slash = &info[i]; for ( p = *node; p < *ident; p++ ) p[0] = p[1]; info[i-1] = '\0'; /* Terminate host string */ } break; case 4: /* Find end of filename */ if ( c == '/' ) last_slash = &info[i]; else if ( (c == '#') || (c == '?') ) { arg_c = c; info[i] = '\0'; *arg = &info[i+1]; state = 5; } case 5: break; } i++; } /* * Insert arg delimiter back into string. */ if ( **arg ) { char tmp; for ( p = *arg; arg_c; p++ ) { tmp = *p; *p = arg_c; arg_c = tmp; } *p = '\0'; } return 1; }