char *jcc_rcs = "$Id: jcc.c,v 3.42 1998/10/29 03:11:21 ACJC Exp $"; /* Written and copyright 1997 Anonymous Coders and Junkbusters Corporation. * Distributed under the GNU General Public License; see the README file. * This code comes with NO WARRANTY. http://www.junkbusters.com/ht/en/gpl.html */ #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include "win32.h" #include #include #else #include #include #include #include #ifdef __BEOS__ #include /* BeOS has select() for sockets only. */ #include /* declarations for threads and stuff. */ #endif #ifdef HAVE_POLL #include #else #ifndef FD_ZERO #include #endif #endif #endif #ifdef REGEX #include #endif #include "jcc.h" #include "zlib.h" #define DEF_MEM_LEVEL 8 char *prog; #define SOCKET_BUF_SIZE 5120 #define BODY "\n" char CFAIL[] = "HTTP/1.0 503 Connect failed\n" "Content-Type: text/html\n\n" "\n" "\n" "Internet Junkbuster: Connect failed\n" "\n" BODY "

" BANNER "

" "TCP connection to '%s' failed: %s.\n
" "\n" "\n" ; char CNXDOM[] = "HTTP/1.0 404 Non-existent domain\n" "Content-Type: text/html\n\n" "\n" "\n" "Internet Junkbuster: Non-existent domain\n" "\n" BODY "

" BANNER "

" "No such domain: %s\n" "\n" "\n" ; char CSUCCEED[] = "HTTP/1.0 200 Connection established\n" "Proxy-Agent: IJ/" VERSION "\n\n" ; char CHEADER[] = "HTTP/1.0 400 Invalid header received from browser\n\n"; char SHEADER[] = "HTTP/1.0 502 Invalid header received from server\n\n"; char VANILLA_WAFER[] = "NOTICE=TO_WHOM_IT_MAY_CONCERN_" "Do_not_send_me_any_copyrighted_information_other_than_the_" "document_that_I_am_requesting_or_any_of_its_necessary_components._" "In_particular_do_not_send_me_any_cookies_that_" "are_subject_to_a_claim_of_copyright_by_anybody._" "Take_notice_that_I_refuse_to_be_bound_by_any_license_condition_" "(copyright_or_otherwise)_applying_to_any_cookie._"; char DEFAULT_USER_AGENT[] ="User-Agent: Mozilla/3.01Gold (Macintosh; I; 68K)"; char BLANKGIF[] = "HTTP/1.0 200 OK\r\n" "Content-type: image/gif\r\n\r\n" "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000" "\000!\371\004\001\000\000\000\000,\000\000\000\000\001" "\000\001\000\000\002\002D\001\000;"; char JBGIF[] = "HTTP/1.0 200 OK\r\n" "Content-type: image/gif\r\n\r\n" "GIF89aD\000\013\000\360\000\000\000\000\000\377\377\377!" "\371\004\001\000\000\001\000,\000\000\000\000D\000\013\000" "\000\002a\214\217\251\313\355\277\000\200G&K\025\316hC\037" "\200\234\230Y\2309\235S\230\266\206\372J\253<\3131\253\271" "\270\215\342\254\013\203\371\202\264\334P\207\332\020o\266" "N\215I\332=\211\312\3513\266:\026AK)\364\370\365aobr\305" "\372\003S\275\274k2\354\254z\347?\335\274x\306^9\374\276" "\037Q\000\000;"; int debug = 0; int multi_threaded = 1; int hideConsole = 0; int tinygif = 0; char *logfile = NULL; FILE *logfp; char *blockfile = NULL; char *cookiefile = NULL; char *trustfile = NULL; char *forwardfile = NULL; char *aclfile = NULL; char *jarfile = NULL; FILE *jar; char *referrer = NULL; char *uagent = NULL; char *from = NULL; int suppress_vanilla_wafer = 0; int add_forwarded = 0; int auto_compress = 0; struct client_state clients[1]; struct file_list files[1]; struct list wafer_list[1]; struct list xtra_list[1]; struct list trust_info[1]; struct url_spec * trust_list[64]; int (*loaders[NLOADERS])(struct client_state *csp); struct gateway gateways[] = { /* type function gw type/host/port, fw host/port*/ { "direct", direct_connect, 0, NULL, 0, NULL, 0 }, { ".", direct_connect, 0, NULL, 0, NULL, 0 }, { "socks", socks4_connect, SOCKS_4, NULL, 1080, NULL, 0 }, { "socks4", socks4_connect, SOCKS_4, NULL, 1080, NULL, 0 }, { "socks4a", socks4_connect, SOCKS_4A, NULL, 1080, NULL, 0 }, { NULL, NULL, 0, NULL, 0, NULL, 0 } }; struct gateway *gw_default = gateways; char *haddr = "127.0.0.1"; /* default binding to localhost */ int hport = 8000; struct proxy_args proxy_args[1]; int write_socket(int fd, char *buf, int n) { if(n <= 0) return(0); if(DEBUG(LOG)) fwrite(buf, n, 1, logfp); #if defined(_WIN32) || defined(__BEOS__) return send(fd, buf, n, 0); #else return write(fd, buf, n); #endif } int write_compression_header(struct client_state *csp, char *buf, int n) { /* csp->gz_debug_fd = open("gz_debug", O_CREAT | O_BINARY | O_TRUNC | O_WRONLY, 0666); */ if (csp->gz_debug_fd >= 0) { write(csp->gz_debug_fd, buf, n); } if (write_socket(csp->cfd, buf, n) != n) { return -1; } if (csp->compressing_content) { int level = Z_BEST_COMPRESSION; int strategy = Z_DEFAULT_STRATEGY; static char gzip_hdr[10] = { 0x1f, (char) 0x8b, (char) Z_DEFLATED, 0, 0, 0, 0, 0, 0, 0x03 }; csp->gz_crc = crc32(0L, Z_NULL, 0); csp->gz_stream = &csp->gz_stream_struct; csp->gz_stream->zalloc = (alloc_func)0; csp->gz_stream->zfree = (free_func)0; csp->gz_stream->opaque = (voidpf)0; csp->gz_stream->next_in = 0; csp->gz_stream->next_out = 0; csp->gz_stream->avail_in = 0; csp->gz_stream->avail_out = 0; /* windowBits is passed < 0 to suppress zlib header */ if (deflateInit2(csp->gz_stream, level, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy) != Z_OK) { csp->gz_stream = 0; return -1; } memcpy(csp->gz_buffer, gzip_hdr, sizeof(gzip_hdr)); csp->gz_stream->next_out = csp->gz_buffer + sizeof(gzip_hdr); csp->gz_stream->avail_out = csp->gz_buffer_size - sizeof(gzip_hdr); } return 0; } int write_compression_tailer(struct client_state *csp) { if (csp->compressing_content) { char buf[8]; char *dest = buf; int n; uLong x; int done = 0; int err; if (csp->gz_stream == 0) { return -1; } for (;;) { uInt len = csp->gz_buffer_size - csp->gz_stream->avail_out; if (len > 0) { if (csp->gz_debug_fd >= 0) { write(csp->gz_debug_fd, csp->gz_buffer, len); } if (write_socket(csp->cfd, csp->gz_buffer, len) != len) { return -1; } /* fprintf(logfp, "wrote -- compressed %d bytes\n", len); */ csp->gz_stream->next_out = csp->gz_buffer; csp->gz_stream->avail_out = csp->gz_buffer_size; } if (done) break; err = deflate(csp->gz_stream, Z_FINISH); /* Ignore the second of two consecutive flushes: */ if (len == 0 && err == Z_BUF_ERROR) err = Z_OK; /* deflate has finished flushing only when it hasn't used up * all the available space in the output buffer: */ done = (csp->gz_stream->avail_out != 0 || err == Z_STREAM_END); if (err != Z_OK && err != Z_STREAM_END) break; } if (err != Z_STREAM_END) { return -1; } x = csp->gz_crc; for (n = 0; n < 4; n++) { *dest++ = (x & 0xff); x >>= 8; } x = csp->gz_stream->total_in; for (n = 0; n < 4; n++) { *dest++ = (x & 0xff); x >>= 8; } /* fprintf(logfp, "Compression bufer - uncompressed size = %d\n", csp->gz_stream->total_in); */ if (csp->gz_debug_fd >= 0) { write(csp->gz_debug_fd, buf, sizeof(buf)); } if (write_socket(csp->cfd, buf, sizeof(buf)) != sizeof(buf)) { return -1; } } return 0; } /* Returns: 0 upon success, non-zero upon failure */ int write_compression_buf(struct client_state *csp, char *buf, int n) { if (n <= 0) { return 0; } /* fprintf(logfp, "write_compression_buf -- uncompressed %d bytes\n", n); */ if (csp->compressing_content) { if (csp->gz_stream == 0) { return -1; } csp->gz_crc = crc32(csp->gz_crc, (const Bytef *)buf, n); csp->gz_stream->next_in = buf; csp->gz_stream->avail_in = n; while (csp->gz_stream->avail_in != 0) { if (csp->gz_stream->avail_out == 0) { csp->gz_stream->next_out = csp->gz_buffer; if (csp->gz_debug_fd >= 0) { write(csp->gz_debug_fd, csp->gz_buffer, csp->gz_buffer_size); } if (write_socket(csp->cfd, csp->gz_buffer, csp->gz_buffer_size) != csp->gz_buffer_size) { return -1; } /* fprintf(logfp, "wrote -- compressed %d bytes\n", csp->gz_buffer_size); */ csp->gz_stream->avail_out = csp->gz_buffer_size; } if (deflate(csp->gz_stream, Z_NO_FLUSH) != Z_OK) { return -1; } } } else { if (csp->gz_debug_fd >= 0) { write(csp->gz_debug_fd, buf, n); } if (write_socket(csp->cfd, buf, n) != n) { return -1; } } return 0; } int read_socket(int fd, char *buf, int n) { if(n <= 0) return(0); #if defined(_WIN32) || defined(__BEOS__) return recv(fd, buf, n, 0); #else return read(fd, buf, n); #endif } void close_socket(int fd) { #if defined(_WIN32) || defined(__BEOS__) closesocket(fd); #else close(fd); #endif } void chat(struct client_state *csp) { char buf[SOCKET_BUF_SIZE], *hdr, *p, *req; char *err = NULL; char *eno; fd_set rfds; int n, maxfd, server_body; int accept_gzip = 0; /* Accept-Encoding: gzip */ char *content_type = 0; char *content_encoding = 0; char *content_length = 0; struct cookie_spec *cs; struct gateway *gw; struct http_request *http; char *iob_buf; int iob_len; char *my_image_regexp = ".*(\\.gif|\\.jpe?g|\\gif$|\\jpe?g$)"; regex_t my_regexp; int errcode; http = csp->http; csp->compressing_content = 0; /* read the client's request. * note that since we're not using select() * we could get blocked here if a client * connected, then didn't say anything! */ for(;;) { n = read_socket(csp->cfd, buf, sizeof(buf)); if(n <= 0) break; /* error! */ add_to_iob(csp, buf, n); req = get_header(csp); if(req == NULL) break; /* no HTTP request! */ if(*req == '\0') continue; /* more to come! */ parse_http_request(req, http, csp); freez(req); break; } if(http->cmd == NULL) { strcpy(buf, CHEADER); write_socket(csp->cfd, buf, strlen(buf)); return; } /* decide how to route the HTTP request */ if((gw = forward_url(http, csp)) == NULL) { fprintf(logfp, "%s: gateway spec is NULL!?!? This can't happen!\n", prog); abort(); } /* build the http request to send to the server * we have to do one of the following: * * create = use the original HTTP request to create a new * HTTP request that has only the path component * without the http://domainspec * pass = pass the original HTTP request unchanged * * drop = drop the HTTP request * * here's the matrix: * SSL * 0 1 * +--------+--------+ * | | | * 0 | create | drop | * | | | * Forwarding +--------+--------+ * | | | * 1 | pass | pass | * | | | * +--------+--------+ * */ if(gw->forward_host) { /* if forwarding, just pass the request as is */ enlist(csp->headers, http->cmd); } else { if(http->ssl == 0) { /* otherwise elide the host information from the url */ p = NULL; p = strsav(p, http->gpc); p = strsav(p, " "); p = strsav(p, http->path); p = strsav(p, " "); p = strsav(p, http->ver); enlist(csp->headers, p); freez(p); } } /* decide what we're to do with cookies */ if((cs = cookie_url(http, csp))) { csp->accept_server_cookie = cs->accept_server_cookie; csp->send_user_cookie = cs->send_user_cookie; } else { csp->accept_server_cookie = 0; csp->send_user_cookie = 0; } /* grab the rest of the client's headers */ for(;;) { if(( p = get_header(csp)) && (*p == '\0')) { n = read_socket(csp->cfd, buf, sizeof(buf)); if(n <= 0) { fprintf(logfp, "%s: read from client failed: ", prog); fperror(logfp, ""); return; } add_to_iob(csp, buf, n); continue; } if(p == NULL) break; enlist(csp->headers, p); if (strncasecmp(p, "Accept-Encoding: ", 17) == 0 && (strcasecmp(p + 17, "gzip") == 0 || strncasecmp(p + 17, "gzip,", 5) == 0)) { accept_gzip = 1; } freez(p); } if(DEBUG(LOG)) { fprintf(logfp, "Client %s gzip\n", accept_gzip ? "accepts" : "does not accept"); } /* filter it as required */ hdr = sed(client_patterns, add_client_headers, csp); destroy_list(csp->headers); if((p = intercept_url(http, csp)) || (p = block_url(http, csp)) || (p = trust_url(http, csp))) { if(DEBUG(GPC)) { fprintf(logfp, "%s: GPC\t%s%s crunch!\n", prog, http->hostport, http->path); } if (tinygif > 0) { errcode = regcomp(&my_regexp, my_image_regexp, (REG_EXTENDED|REG_NOSUB|REG_ICASE)); if (regexec( &my_regexp, http->path, 0, NULL, 0) == 0) { if (tinygif == 1) write_socket(csp->cfd, BLANKGIF, sizeof(BLANKGIF)-1); else write_socket(csp->cfd, JBGIF, sizeof(JBGIF)-1); } else { write_socket(csp->cfd, p, strlen(p)); } regfree( &my_regexp ); } else write_socket(csp->cfd, p, strlen(p)); if(DEBUG(LOG)) fwrite(p, strlen(p), 1, logfp); freez(p); freez(hdr); return; } if(DEBUG(GPC)) { fprintf(logfp, "%s: GPC\t%s%s\n", prog, http->hostport, http->path); } if(DEBUG(CON)) { if(gw->forward_host) { fprintf(logfp, "%s: connect via %s:%d to: %s ... ", prog, gw->forward_host, gw->forward_port, http->hostport); } else { fprintf(logfp, "%s: connect to: %s ... ", prog, http->hostport); } } /* here we connect to the server, gateway, or the forwarder */ csp->sfd = (gw->conn)(gw, http, csp); if(csp->sfd < 0) { if(DEBUG(CON)) { fprintf(logfp, "%s: connect to: %s failed: ", prog, http->hostport); fperror(logfp, ""); } if(errno == EINVAL) { err = zalloc(strlen(CNXDOM) + strlen(http->host)); sprintf(err, CNXDOM, http->host); } else { eno = safe_strerror(errno); err = zalloc(strlen(CFAIL) + strlen(http->hostport) + strlen(eno)); sprintf(err, CFAIL, http->hostport, eno); } write_socket(csp->cfd, err, strlen(err)); if(DEBUG(LOG)) fwrite(err, strlen(err), 1, logfp); freez(err); freez(hdr); return; } if(DEBUG(CON)) { fprintf(logfp, "OK\n"); } if(gw->forward_host || (http->ssl == 0)) { /* write the client's (modified) header to the server * (along with anything else that may be in the buffer) */ n = strlen(hdr); iob_len = flush_buffer(csp, &iob_buf); if((write_socket(csp->sfd, hdr, n) != n) || (iob_len > 0 && write_socket(csp->sfd, iob_buf, iob_len) != iob_len)) { if(DEBUG(CON)) { fprintf(logfp, "%s: write header to: %s failed: ", prog, http->hostport); fperror(logfp, ""); } eno = safe_strerror(errno); err = zalloc(strlen(CFAIL) + strlen(http->hostport) + strlen(eno)); sprintf(err, CFAIL, http->hostport, eno); write_socket(csp->cfd, err, strlen(err)); freez(err); freez(hdr); return; } } else { /* we're running an SSL tunnel and we're not * forwarding, so just send the "connect succeeded" * message to the client, flush the rest, and * get out of the way. */ if(write_socket(csp->cfd, CSUCCEED, sizeof(CSUCCEED)-1) < 0) { freez(hdr); return; } IOB_RESET(csp); } /* we're finished with the client's header */ freez(hdr); maxfd = ( csp->cfd > csp->sfd ) ? csp->cfd : csp->sfd; /* pass data between the client and server * until one or the other shuts down the connection. */ server_body = 0; #ifdef HAVE_POLL fds[0].fd = csp->cfd; fds[0].events = POLLIN|POLLHUP; fds[1].fd = csp->sfd; fds[1].events = POLLIN|POLLHUP; #endif for(;;) { #ifdef HAVE_POLL n = poll(fds, 2, -1); if (n < 0) { fprintf(logfp, "%s: poll() failed!: ", prog); fperror(logfp, ""); return; } #define IS_CLIENT() (fds[0].revents & POLLIN) #define IS_SERVER() (fds[1].revents & POLLIN) #else FD_ZERO(&rfds); FD_SET(csp->cfd, &rfds); FD_SET(csp->sfd, &rfds); n = select(maxfd+1, &rfds, NULL, NULL, NULL); if(n < 0) { fprintf(logfp, "%s: select() failed!: ", prog); fperror(logfp, ""); return; } #define IS_CLIENT() FD_ISSET(csp->cfd, &rfds) #define IS_SERVER() FD_ISSET(csp->sfd, &rfds) #endif /* this is the body of the browser's request * just read it and write it. */ if(IS_CLIENT()) { n = read_socket(csp->cfd, buf, sizeof(buf)); if(n <= 0) break; /* "game over, man" */ if(write_socket(csp->sfd, buf, n) != n) { fprintf(logfp, "%s: write to: %s failed: ", prog, http->host); fperror(logfp, ""); return; } continue; } /* the server wants to talk. * it could be the header or the body. * if `hdr' is null, then it's the header * otherwise it's the body */ if(IS_SERVER()) { n = read_socket(csp->sfd, buf, sizeof(buf)); if(n < 0) { fprintf(logfp, "%s: read from: %s failed: ", prog, http->host); fperror(logfp, ""); eno = safe_strerror(errno); sprintf(buf, CFAIL, http->hostport, eno); freez(eno); write_socket(csp->cfd, buf, strlen(buf)); return; } if(n == 0) break; /* "game over, man" */ /* if this is an SSL connection or we're in the body * of the server document, just write it to the client. */ if(server_body || http->ssl) { /* just write */ if(write_compression_buf(csp, buf, n) != 0) { fprintf(logfp, "%s: write to client failed: ", prog); fperror(logfp, ""); return; } continue; } else { /* we're still looking for the end of the * server's header ... (does that make header * parsing an "out of body experience" ? */ /* buffer up the data we just read */ add_to_iob(csp, buf, n); /* get header lines from the iob */ while((p = get_header(csp))) { if(*p == '\0') { /* see following note */ break; } if (strncasecmp(p, "Content-Type: ", 14) == 0) { freez(content_type); content_type = p; } else if (strncasecmp(p, "Content-Length: ", 16) == 0) { freez(content_length); content_length = p; } else if (strncasecmp(p, "Content-Encoding: ", 18) == 0) { freez(content_encoding); content_encoding = p; } else { enlist(csp->headers, p); freez(p); } } /* NOTE: there are no "empty" headers so * if the pointer `p' is not NULL we must * assume that we reached the end of the * buffer before we hit the end of the header. * * Since we have to wait for more from the * server before we can parse the headers * we just continue here. */ if(p) continue; /* we have now received the entire header. * filter it and send the result to the client */ if (auto_compress && accept_gzip && content_encoding == 0 && content_type != 0 && (strncasecmp(content_type, "Content-Type: text/html", 23) == 0 || strncasecmp(content_type, "Content-Type: text/plain", 24) == 0)) { /* Can compress output */ content_encoding = strdup("Content-Encoding: gzip"); if (DEBUG(LOG) && content_length != 0) { fprintf(logfp, "Pre-compression %s\n", content_length); } freez(content_length); csp->compressing_content = 1; } if (content_length) { enlist(csp->headers, content_length); } if (content_type) { enlist(csp->headers, content_type); } if (content_encoding) { enlist(csp->headers, content_encoding); } freez(content_type); freez(content_encoding); freez(content_length); if(DEBUG(LOG)) { fprintf(logfp, "%s result\n", csp->compressing_content ? "Compressing" : "Not compressing"); } hdr = sed( server_patterns, add_server_headers, csp); n = strlen(hdr); /* write the server's (modified) header to * the client (along with anything else that * may be in the buffer) */ iob_len = flush_buffer(csp, &iob_buf); if ((write_compression_header(csp, hdr, n) != 0) || (write_compression_buf(csp, iob_buf, iob_len) != 0)) { if(DEBUG(CON)) { fprintf(logfp, "%s: write header to client failed: ", prog); fperror(logfp, ""); } /* the write failed, so don't bother * mentioning it to the client... * it probably can't hear us anyway. */ freez(hdr); return; } /* we're finished with the server's header */ freez(hdr); server_body = 1; } continue; } return; /* huh? we should never get here */ } } void serve(struct client_state *csp) { char compress_buf[SOCKET_BUF_SIZE]; csp->gz_buffer = compress_buf; csp->gz_buffer_size = sizeof(compress_buf); csp->gz_debug_fd = -1; chat(csp); write_compression_tailer(csp); if (csp->gz_debug_fd >= 0) { close(csp->gz_debug_fd); } if (csp->gz_stream != 0) { deflateEnd(csp->gz_stream); } close_socket(csp->cfd); if(csp->sfd >= 0) { close_socket(csp->sfd); } csp->active = 0; } #ifdef __BEOS__ int32 server_thread(void *data) { serve((struct client_state *) data); return 0; } #endif int main(int argc, char *argv[]) { char buf[BUFSIZ]; int cfd, bfd; char *p, *q; extern char *optarg; extern int optind; struct client_state *csp; char *default_configfile = NULL; char *configfile = NULL; FILE *configfp = NULL; int err = 0; prog = argv[0]; logfp = stdout; init_proxy_args(argc, argv); cfd = -1; #ifdef _WIN32 default_configfile = "junkbstr.ini"; #endif configfile = default_configfile; if(argc > 1) { configfile = argv[1]; } if(configfile) { if((configfp = fopen(configfile, "r")) == NULL) { if(configfile != default_configfile) { fprintf(logfp, "%s: can't open configuration file '%s': ", prog, configfile); fperror(logfp, ""); exit(1); } } } if(configfp) { int line_num = 0; while(fgets(buf, sizeof(buf), configfp)) { char cmd[BUFSIZ]; char arg[BUFSIZ]; char tmp[BUFSIZ]; line_num++; strcpy(tmp, buf); if((p = strpbrk(tmp, "#\r\n"))) *p = '\0'; p = tmp; /* leading skip whitespace */ while(*p && ((*p == ' ') || (*p == '\t'))) p++; q = cmd; while(*p && (*p != ' ') && (*p != '\t')) *q++ = *p++; *q = '\0'; while(*p && ((*p == ' ') || (*p == '\t'))) p++; strcpy(arg, p); p = arg + strlen(arg) - 1; /* ignore trailing whitespace */ while(*p && ((*p == ' ') || (*p == '\t'))) *p-- = '\0'; if(*cmd == '\0') continue; /* insure the command field is lower case */ for(p=cmd; *p; p++) if(isupper(*p)) *p = tolower(*p); savearg(cmd, arg); if(strcmp(cmd, "trustfile") == 0) { trustfile = strdup(arg); continue; } if(strcmp(cmd, "trust_info_url") == 0) { enlist(trust_info, arg); continue; } if(strcmp(cmd, "debug") == 0) { debug |= atoi(arg); continue; } if(strcmp(cmd, "tinygif") == 0) { tinygif = atoi(arg); continue; } if(strcmp(cmd, "add-forwarded-header") == 0) { add_forwarded = 1; continue; } if(strcmp(cmd, "auto-compress") == 0) { auto_compress = 1; continue; } if(strcmp(cmd, "single-threaded") == 0) { multi_threaded = 0; continue; } if(strcmp(cmd, "suppress-vanilla-wafer") == 0) { suppress_vanilla_wafer = 1; continue; } if(strcmp(cmd, "wafer") == 0) { enlist(wafer_list, arg); continue; } if(strcmp(cmd, "add-header") == 0) { enlist(xtra_list, arg); continue; } if(strcmp(cmd, "cookiefile") == 0) { cookiefile = strdup(arg); continue; } if(strcmp(cmd, "logfile") == 0) { logfile = strdup(arg); continue; } if(strcmp(cmd, "blockfile") == 0) { blockfile = strdup(arg); continue; } if(strcmp(cmd, "jarfile") == 0) { jarfile = strdup(arg); continue; } if(strcmp(cmd, "listen-address") == 0) { haddr = strdup(arg); continue; } if(strcmp(cmd, "forwardfile") == 0) { forwardfile = strdup(arg); continue; } if(strcmp(cmd, "aclfile") == 0) { aclfile = strdup(arg); continue; } if(strcmp(cmd, "user-agent") == 0) { uagent = strdup(arg); continue; } if((strcmp(cmd, "referrer") == 0) || (strcmp(cmd, "referer" ) == 0)) { referrer = strdup(arg); continue; } if(strcmp(cmd, "from") == 0) { from = strdup(arg); continue; } if(strcmp(cmd, "hide-console") == 0) { hideConsole = 1; continue; } fprintf(logfp, "%s: unrecognized directive " "in configuration file " "at line number %d:\n%s", prog, line_num, buf); err = 1; } fclose(configfp); } if(err) exit(1); #ifdef _WIN32 InitWin32(); #endif if(logfile) { FILE *tlog = fopen(logfile, "a"); if(tlog == NULL) { fprintf(logfp, "%s: can't open logfile '%s': ", prog, logfile); fperror(logfp, ""); err = 1; } logfp = tlog; } setbuf(logfp, NULL); if(cookiefile) add_loader(load_cookiefile); if(blockfile) add_loader(load_blockfile); if(trustfile) add_loader(load_trustfile); if(forwardfile) add_loader(load_forwardfile); if(aclfile) add_loader(load_aclfile); if(jarfile) { jar = fopen(jarfile, "a"); if(jar == NULL) { fprintf(logfp, "%s: can't open jarfile '%s': ", prog, jarfile); fperror(logfp, ""); err = 1; } setbuf(jar, NULL); } if(haddr) { if((p = strchr(haddr, ':'))) { *p++ = '\0'; if(*p) hport = atoi(p); } if(hport <= 0) { *--p = ':' ; fprintf(logfp, "%s: invalid bind port spec %s", prog, haddr); err = 1; } if(*haddr == '\0') haddr = NULL; } if(run_loader(NULL)) err = 1; if(err) exit(1); /* if we're logging cookies in a cookie jar, * and the user has not supplied any wafers, * and the user has not told us to suppress the vanilla wafer, * then send the vanilla wafer. */ if((jarfile != NULL) && (wafer_list->next == NULL) && (suppress_vanilla_wafer == 0)) { enlist(wafer_list, VANILLA_WAFER); } if(DEBUG(CON)) { fprintf(logfp, "%s: bind (%s, %d)\n", prog, haddr ? haddr : "INADDR_ANY", hport); } bfd = bind_port(haddr, hport); if(bfd < 0) { fprintf(logfp, "%s: can't bind %s:%d: ", prog, haddr ? haddr : "INADDR_ANY", hport); fperror(logfp, ""); fprintf(logfp, "There may be another junkbuster or some other " "proxy running on port %d\n", hport); err = 1; } if(err) exit(1); end_proxy_args(); #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); #endif #ifdef _WIN32 { /* print a verbose messages about FAQ's and such */ extern char *win32_blurb; if(logfp == stdout) fprintf(logfp, win32_blurb); } #endif for(;;) { #if !defined(_WIN32) && !defined(__BEOS__) while(waitpid(-1, NULL, WNOHANG) > 0) { /* zombie children */ } #endif sweep(); if(DEBUG(CON)) { fprintf(logfp, "%s: accept connection ... ", prog); } cfd = accept_connection(bfd); if(cfd < 0) { if(DEBUG(CON)) { fprintf(logfp, "%s: accept failed: ", prog); fperror(logfp, ""); } continue; } else { if(DEBUG(CON)) { fprintf(logfp, "OK\n"); } } csp = (struct client_state *) malloc(sizeof(*csp)); if(csp == NULL) { fprintf(logfp, "%s: malloc(%d) for csp failed: ", prog, sizeof(*csp)); fperror(logfp, ""); close_socket(cfd); continue; } memset(csp, '\0', sizeof(*csp)); csp->active = 1; csp->cfd = cfd; csp->sfd = -1; csp->ip_addr_str = remote_ip_str; csp->ip_addr_long = remote_ip_long; /* add it to the list of clients */ csp->next = clients->next; clients->next = csp; if(run_loader(csp)) { fprintf(logfp, "%s: a loader failed - must exit\n", prog); exit(1); } if(multi_threaded) { int child_id; /* this is a switch() statment in the C preprocessor - ugh */ #undef SELECTED_ONE_OPTION #if defined(_WIN32) && !defined(SELECTED_ONE_OPTION) #define SELECTED_ONE_OPTION child_id = _beginthread( (void*)serve, 64 * 1024, csp); #endif #if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION) #define SELECTED_ONE_OPTION { thread_id tid = spawn_thread (server_thread, "server", B_NORMAL_PRIORITY, csp); if ((tid >= 0) && (resume_thread(tid) == B_OK)) { child_id = (int) tid; } else { child_id = -1; } } #endif #if !defined(SELECTED_ONE_OPTION) child_id = fork(); #endif #undef SELECTED_ONE_OPTION /* end of cpp switch() */ if(child_id < 0) { /* failed */ fprintf(logfp, "%s: can't fork: ", prog); fperror(logfp, ""); sprintf(buf , "%s: can't fork: errno = %d", prog, errno); write_socket(csp->cfd, buf, strlen(buf)); close_socket(csp->cfd); csp->active = 0; sleep(5); continue; } #if !defined(_WIN32) && !defined(__BEOS__) /* This block is only needed when using fork(). * When using threads, the server thread was * created and run by the call to _beginthread(). */ if(child_id == 0) { /* child */ serve(csp); _exit(0); } else { /* parent */ /* in a fork()'d environment, the parent's * copy of the client socket and the CSP * are not used. */ close_socket(csp->cfd); csp->active = 0; } #endif } else { serve(csp); } } /* NOTREACHED */ } char * safe_strerror(int err) { char buf[BUFSIZ]; char *s = NULL; #ifndef NOSTRERROR s = strerror(err); #endif /* NOSTRERROR */ if(s == NULL) { sprintf(buf, "(errno = %d)", err); s = buf; } return(strdup(s)); } void fperror(FILE *fp, char *str) { char *eno = safe_strerror(errno); if(str && *str) { fprintf(fp, "%s: %s\n", str, eno); } else { fprintf(fp, "%s\n", eno); } freez(eno); }