diff -cr stunnel-4.05/src/Makefile.w32 stunnel-4.05-proxy-patch/src/Makefile.w32 *** stunnel-4.05/src/Makefile.w32 Sun Jan 25 16:18:46 2004 --- stunnel-4.05-proxy-patch/src/Makefile.w32 Sat Apr 17 20:34:43 2004 *************** *** 8,14 **** # Modify this to point to your actual openssl compile directory # (You did already compile openssl, didn't you???) ! SSLDIR=../openssl-0.9.7 DEFINES=-DUSE_WIN32 -DHAVE_OPENSSL --- 8,14 ---- # Modify this to point to your actual openssl compile directory # (You did already compile openssl, didn't you???) ! SSLDIR=../../openssl-0.9.7d DEFINES=-DUSE_WIN32 -DHAVE_OPENSSL diff -cr stunnel-4.05/src/client.c stunnel-4.05-proxy-patch/src/client.c *** stunnel-4.05/src/client.c Tue Feb 10 18:17:54 2004 --- stunnel-4.05-proxy-patch/src/client.c Tue Apr 20 12:05:10 2004 *************** *** 73,79 **** --- 73,81 ---- static int make_sockets(int [2]); #endif static int connect_remote(CLI *c); + int waitforsocket(int, int, int); static void reset(int, char *); + int connect_to_finaldest(CLI *c, int s); int max_clients; #ifndef USE_WIN32 *************** *** 230,235 **** --- 232,238 ---- c->remote_fd.is_socket=1; /* Always! */ if(set_socket_options(fd, 2)<0) return -1; + return 0; /* OK */ } *************** *** 304,309 **** --- 307,313 ---- return -1; } print_cipher(c); + return 0; /* OK */ } *************** *** 870,876 **** int error; int s; /* destination socket */ u16 dport; ! memset(&addr, 0, sizeof(addr)); addr.sin_family=AF_INET; --- 874,880 ---- int error; int s; /* destination socket */ u16 dport; ! memset(&addr, 0, sizeof(addr)); addr.sin_family=AF_INET; *************** *** 888,899 **** --- 892,906 ---- /* connect each host from the list */ for(; *list+1; list++) { /* same as (signed)*list!=-1 */ + if((s=socket(AF_INET, SOCK_STREAM, 0))<0) { sockerror("remote socket"); return -1; } if(alloc_fd(s)) + { return -1; + } if(c->bind_ip) { /* explicit local bind or transparent proxy */ addr.sin_addr.s_addr=c->bind_ip; *************** *** 911,919 **** safe_ntoa(c->connecting_address, addr.sin_addr); log(LOG_DEBUG, "%s connecting %s:%d", c->opt->servname, c->connecting_address, ntohs(addr.sin_port)); ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr))) return s; /* no error -> success */ error=get_last_socket_error(); switch(error) { case EINPROGRESS: /* retry */ log(LOG_DEBUG, "remote connect #1: EINPROGRESS: retrying"); --- 918,930 ---- safe_ntoa(c->connecting_address, addr.sin_addr); log(LOG_DEBUG, "%s connecting %s:%d", c->opt->servname, c->connecting_address, ntohs(addr.sin_port)); ! ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr)) ! && !connect_to_finaldest(c, s)) return s; /* no error -> success */ + error=get_last_socket_error(); + switch(error) { case EINPROGRESS: /* retry */ log(LOG_DEBUG, "remote connect #1: EINPROGRESS: retrying"); *************** *** 936,948 **** } /* try to connect for the 2nd time */ ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr))) return s; /* no error -> success */ error=get_last_socket_error(); switch(error) { case EINVAL: /* WIN32 is strange... */ log(LOG_DEBUG, "remote connect #2: EINVAL: ok"); case EISCONN: /* ok */ return s; /* success */ default: log(LOG_ERR, "remote connect #2 (%s:%d): %s (%d)", --- 947,976 ---- } /* try to connect for the 2nd time */ ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr)) ! && !connect_to_finaldest(c, s)) return s; /* no error -> success */ + error=get_last_socket_error(); + switch(error) { case EINVAL: /* WIN32 is strange... */ log(LOG_DEBUG, "remote connect #2: EINVAL: ok"); case EISCONN: /* ok */ + /* + * As this can also be a legal return point we must + * send the http connect stuff before returning if + * we want to use a proxy. + */ + if(connect_to_finaldest(c, s)) + { + log(LOG_ERR, "remote connect #2 (%s:%d): %s (%d)", + c->connecting_address, ntohs(addr.sin_port), + my_strerror(error), error); + closesocket(s); + continue; /* Next IP */ + } + return s; /* success */ default: log(LOG_ERR, "remote connect #2 (%s:%d): %s (%d)", *************** *** 952,957 **** --- 980,986 ---- continue; /* Next IP */ } } + return -1; } *************** *** 963,968 **** --- 992,1239 ---- l.l_linger=0; if(setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof(l))) log_error(LOG_DEBUG, get_last_socket_error(), txt); + } + + /* + * Base 64 encoding algorithm from: Bob Deblier + * Modified by Daniel Savard to accept char * + */ + static const char* to_b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char* b64enc(const char* data) { + int div = strlen(data) / 3; + int rem = strlen(data) % 3; + int chars = div*4 + rem + 1; + char* string = (char*) malloc(chars + 1); + + if (string) { + register char* buf = string; + chars = 0; + while (div > 0) { + buf[0] = to_b64[ (data[0] >> 2) & 0x3f]; + buf[1] = to_b64[((data[0] << 4) & 0x30) + ((data[1] >> 4) & 0xf)]; + buf[2] = to_b64[((data[1] << 2) & 0x3c) + ((data[2] >> 6) & 0x3)]; + buf[3] = to_b64[ data[2] & 0x3f]; + data += 3; + buf += 4; + div--; + chars += 4; + } + + switch (rem) { + case 2: + buf[0] = to_b64[ (data[0] >> 2) & 0x3f]; + buf[1] = to_b64[((data[0] << 4) & 0x30) + ((data[1] >> 4) & 0xf)]; + buf[2] = to_b64[ (data[1] << 2) & 0x3c]; + buf[3] = '='; + buf += 4; + chars += 4; + break; + case 1: + buf[0] = to_b64[ (data[0] >> 2) & 0x3f]; + buf[1] = to_b64[ (data[0] << 4) & 0x30]; + buf[2] = '='; + buf[3] = '='; + buf += 4; + chars += 4; + break; + } + + *buf = '\0'; + } + + return string; + } + + /* + * Base 64 decoding algorithm from: Bob Deblier + * Modified by Daniel Savard to return char * + */ + char* b64dec(const char* string) + { + /* return a decoded char string, or a null pointer in case of failure */ + char* data = NULL; + + if (string) { + register int length = strlen(string); + + /* do a format verification first */ + if (length > 0) { + register int count = 0, rem = 0; + register const char* tmp = string; + + while (length > 0) { + register int skip = strspn(tmp, to_b64); + count += skip; + length -= skip; + tmp += skip; + if (length > 0) { + register int i, vrfy = strcspn(tmp, to_b64); + + for (i = 0; i < vrfy; i++) { + if (isspace(tmp[i])) + continue; + + if (tmp[i] == '=') { + /* we should check if we're close to the end of the string */ + rem = count % 4; + + /* rem must be either 2 or 3, otherwise no '=' should be here */ + if (rem < 2) + return NULL; + + /* end-of-message recognized */ + break; + } else { + /* Transmission error; RFC tells us to ignore this, but: + * - the rest of the message is going to even more corrupt since we're sliding bits out of place + * If a message is corrupt, it should be dropped. Period. + */ + return NULL; + } + } + + length -= vrfy; + tmp += vrfy; + } + } + + data = (unsigned char *)malloc((count / 4) * 3 + (rem ? (rem - 1) : 0)); + + if (data) { + if (count > 0) { + register int i, qw = 0, tw = 0; + + length = strlen(tmp = string); + + for (i = 0; i < length; i++) { + register char ch = string[i]; + register char bits = 0; + + if (isspace(ch)) + continue; + + if ((ch >= 'A') && (ch <= 'Z')) { + bits = (ch - 'A'); + } else if ((ch >= 'a') && (ch <= 'z')) { + bits = (ch - 'a' + 26); + } else if ((ch >= '0') && (ch <= '9')) { + bits = (ch - '0' + 52); + } else if (ch == '=') { + break; + } + + switch (qw++) { + case 0: + data[tw+0] = (bits << 2) & 0xfc; + break; + case 1: + data[tw+0] |= (bits >> 4) & 0x03; + data[tw+1] = (bits << 4) & 0xf0; + break; + case 2: + data[tw+1] |= (bits >> 2) & 0x0f; + data[tw+2] = (bits << 6) & 0xc0; + break; + case 3: + data[tw+2] |= bits & 0x3f; + break; + } + + if (qw == 4) { + qw = 0; + tw += 3; + } + } + + data[tw] = '\0'; + } + } + } + } + + return data; + } + + /* + * Original https proxy algorithm from: Tan Swee Heng + * Modified by Daniel Savard to support basic authentication + */ + int connect_to_finaldest(CLI *c, int s) { + char buff[STRLEN]; + int len, code; + char httpsproxy_auth[STRLEN] = ""; + char httpsproxy_useragent[STRLEN] = ""; + + if (!c->opt->option.httpsproxy) + return 0; + + if (c->opt->httpsproxy_auth != NULL) { + if (strchr(c->opt->httpsproxy_auth,':')) { + /* httpsproxy_auth in the form name:password' */ + char *base64_auth = b64enc(c->opt->httpsproxy_auth); + log(LOG_DEBUG,"proxy: authenticate with '%s' -> '%s'\n",c->opt->httpsproxy_auth,base64_auth); + sprintf(httpsproxy_auth,"Proxy-Authorization: Basic %s\r\n",base64_auth); + free(base64_auth); + } else { + /* httpsproxy_auth already base64 encoded */ + char *normal_auth = b64dec(c->opt->httpsproxy_auth); + log(LOG_DEBUG,"proxy: authenticate with '%s' -> '%s'\n",normal_auth,c->opt->httpsproxy_auth); + sprintf(httpsproxy_auth,"Proxy-Authorization: Basic %s\r\n",c->opt->httpsproxy_auth); + free(normal_auth); + } + } else { + log(LOG_DEBUG,"proxy: no authentication specified"); + } + + if (c->opt->httpsproxy_useragent != NULL) { + log(LOG_DEBUG,"proxy: useragent '%s'\n",c->opt->httpsproxy_useragent); + sprintf(httpsproxy_useragent,"User-Agent: %s\r\n",c->opt->httpsproxy_useragent); + } else { + log(LOG_DEBUG,"proxy: no useragent specified"); + } + + #ifdef HAVE_SNPRINTF + len=snprintf(buff, STRLEN, + #else + len=sprintf(buff, + #endif + "CONNECT %s HTTP/1.0\r\nHost: %s\r\nContent-Length: 0\r\n%s%sProxy-Connection: Keep-Alive\r\nPragma: no-cache\r\n\r\n", + c->opt->httpsproxy_dest_address, + c->opt->httpsproxy_dest_address, + httpsproxy_auth, + httpsproxy_useragent); + + len=writesocket(s, buff, len); + if(len<0) { + sockerror("writesocket (httpsproxy)"); + closesocket(s); + return -1; + } + log(LOG_DEBUG, "me ---> proxy: %s", buff); + + waitforsocket(s, 0, c->opt->timeout_busy); + len=readsocket(s, buff, STRLEN-1); + + if(len<0) { + sockerror("readsocket (httpsproxy)"); + closesocket(s); + return -1; + } + buff[len]='\0'; + log(LOG_DEBUG, "proxy ---> me: %s", buff); + + code = 0; + if(sscanf(buff, "HTTP/%*s %d %*s", &code) != 1) { + log(LOG_ERR, "error: %s", buff); + return -1; + } + + if(code != 200) { + log(LOG_WARNING, "return code not 200: %s", buff); + return -1; + } + + return 0; } /* End of client.c */ diff -cr stunnel-4.05/src/options.c stunnel-4.05-proxy-patch/src/options.c *** stunnel-4.05/src/options.c Sun Jan 25 16:25:52 2004 --- stunnel-4.05-proxy-patch/src/options.c Thu Apr 15 18:36:57 2004 *************** *** 746,751 **** --- 746,827 ---- } #endif + /* Daniel Savard + * httpsproxy_auth + * Optional parameter to httpsproxy_dest to specify authentication + * credential to the https proxy. Value must be in form name:password + * or the base64 encoded value of the preceding form. + */ + switch(cmd) { + case CMD_INIT: + section->httpsproxy_auth=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "httpsproxy_auth")) + break; + section->httpsproxy_auth=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + log_raw("%-15s = authentication for 'httpsproxy' must be userid:password", + "httpsproxy_auth"); + break; + } + + /* Daniel Savard + * httpsproxy_dest + * When specified, the connect parameter will specify the name of a https + * proxy server and this parameter will be the final destination. + */ + switch(cmd) { + case CMD_INIT: + section->option.httpsproxy=0; + section->httpsproxy_dest_address=NULL; + section->httpsproxy_dest_names=NULL; + section->httpsproxy_dest_port=0; + break; + case CMD_EXEC: + if(strcasecmp(opt, "httpsproxy_dest")) + break; + section->option.httpsproxy=1; + section->httpsproxy_dest_address=stralloc(arg); + if(!section->option.delayed_lookup && !name2nums(arg, "127.0.0.1", + §ion->httpsproxy_dest_names, §ion->httpsproxy_dest_port)) { + log_raw("Cannot resolve '%s' - delaying DNS lookup", arg); + section->option.delayed_lookup=1; + } + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + log_raw("%-15s = [host:]port https proxy connect destination host:port", + "httpsproxy_dest"); + break; + } + + /* Daniel Savard + * httpsproxy_useragent + * Optional parameter to httpsproxy_dest. When specified, the specified + * user-agent will be sent to the proxy + */ + switch(cmd) { + case CMD_INIT: + section->httpsproxy_useragent=NULL; + break; + case CMD_EXEC: + if(strcasecmp(opt, "httpsproxy_useragent")) + break; + section->httpsproxy_useragent=stralloc(arg); + return NULL; /* OK */ + case CMD_DEFAULT: + break; + case CMD_HELP: + log_raw("%-15s = useragent for 'httpsproxy'", + "httpsproxy_useragent"); + break; + } + /* ident */ switch(cmd) { case CMD_INIT: diff -cr stunnel-4.05/src/prototypes.h stunnel-4.05-proxy-patch/src/prototypes.h *** stunnel-4.05/src/prototypes.h Tue Feb 10 18:15:06 2004 --- stunnel-4.05-proxy-patch/src/prototypes.h Thu Apr 15 22:40:02 2004 *************** *** 158,169 **** /* service-specific data for client.c */ int fd; /* file descriptor accepting connections for this service */ ! unsigned short localport, remoteport; char *execname, **execargs; /* program name and arguments for local mode */ ! u32 *localnames, *remotenames; u32 *local_ip; char *username; char *remote_address; int timeout_busy; /* Maximum waiting for data time */ int timeout_idle; /* Maximum idle connection time */ int timeout_close; /* Maximum close_notify time */ --- 158,170 ---- /* service-specific data for client.c */ int fd; /* file descriptor accepting connections for this service */ ! unsigned short localport, remoteport, httpsproxy_dest_port; char *execname, **execargs; /* program name and arguments for local mode */ ! u32 *localnames, *remotenames, *httpsproxy_dest_names; u32 *local_ip; char *username; char *remote_address; + char *httpsproxy_dest_address, *httpsproxy_auth, *httpsproxy_useragent; int timeout_busy; /* Maximum waiting for data time */ int timeout_idle; /* Maximum idle connection time */ int timeout_close; /* Maximum close_notify time */ *************** *** 176,181 **** --- 177,183 ---- unsigned int delayed_lookup:1; unsigned int accept:1; unsigned int remote:1; + unsigned int httpsproxy:1; #ifndef USE_WIN32 unsigned int program:1; unsigned int pty:1;