diff -urN stunnel-4.05.orig/src/client.c stunnel-4.05/src/client.c --- stunnel-4.05.orig/src/client.c 2004-02-10 19:17:54.000000000 +0000 +++ stunnel-4.05/src/client.c 2004-03-08 18:39:51.000000000 +0000 @@ -74,6 +74,7 @@ #endif static int connect_remote(CLI *c); static void reset(int, char *); +int connect_to_finaldest(CLI *c, int s); int max_clients; #ifndef USE_WIN32 @@ -911,7 +912,8 @@ 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))) + 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) { @@ -936,7 +938,8 @@ } /* try to connect for the 2nd time */ - if(!connect(s, (struct sockaddr *)&addr, sizeof(addr))) + 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) { @@ -965,4 +968,245 @@ 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' -> '%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\n%s%s\r\n", + 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 -urN stunnel-4.05.orig/src/options.c stunnel-4.05/src/options.c --- stunnel-4.05.orig/src/options.c 2004-03-08 17:34:41.000000000 +0000 +++ stunnel-4.05/src/options.c 2004-03-08 18:36:33.000000000 +0000 @@ -770,6 +770,82 @@ } #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: @@ -884,7 +960,7 @@ else return "Illegal close timeout"; return NULL; /* OK */ - case CMD_DEFAULT: + case CMD_DEFAULT: log_raw("%-15s = %d seconds", "TIMEOUTclose", section->timeout_close); break; case CMD_HELP: diff -urN stunnel-4.05.orig/src/prototypes.h stunnel-4.05/src/prototypes.h --- stunnel-4.05.orig/src/prototypes.h 2004-03-08 17:34:41.000000000 +0000 +++ stunnel-4.05/src/prototypes.h 2004-03-08 18:40:48.000000000 +0000 @@ -161,12 +161,13 @@ /* service-specific data for client.c */ int fd; /* file descriptor accepting connections for this service */ - unsigned short localport, remoteport; + unsigned short localport, remoteport, httpsproxy_dest_port; char *execname, **execargs; /* program name and arguments for local mode */ - u32 *localnames, *remotenames; + 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 */ @@ -179,6 +180,7 @@ 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;