diff -cr stunnel-4.00.orig/src/client.c stunnel-4.00/src/client.c *** stunnel-4.00.orig/src/client.c Mon Aug 12 06:07:51 2002 --- stunnel-4.00/src/client.c Thu Oct 31 20:37:13 2002 *************** *** 76,81 **** --- 76,82 ---- static int connect_remote(CLI *c); static int waitforsocket(int, int, int); static void reset(int, char *); + int connect_to_finaldest(CLI *c, int s); int max_clients; #ifndef USE_WIN32 *************** *** 853,859 **** log(LOG_DEBUG, "%s connecting %s:%d", c->opt->servname, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); leave_critical_section(CRIT_NTOA); ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr))) return s; /* no error -> success */ switch(get_last_socket_error()) { case EINPROGRESS: /* retry */ --- 854,861 ---- log(LOG_DEBUG, "%s connecting %s:%d", c->opt->servname, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); leave_critical_section(CRIT_NTOA); ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr)) ! && !connect_to_finaldest(c, s)) return s; /* no error -> success */ switch(get_last_socket_error()) { case EINPROGRESS: /* retry */ *************** *** 867,873 **** } if(waitforsocket(s, 1 /* write */, c->opt->timeout_busy)<1) continue; /* timeout or error */ ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr))) return s; /* no error -> success */ switch(get_last_socket_error()) { case EINVAL: /* WIN32 is strange... */ --- 869,876 ---- } if(waitforsocket(s, 1 /* write */, c->opt->timeout_busy)<1) continue; /* timeout or error */ ! if(!connect(s, (struct sockaddr *)&addr, sizeof(addr)) ! && !connect_to_finaldest(c, s)) return s; /* no error -> success */ switch(get_last_socket_error()) { case EINVAL: /* WIN32 is strange... */ *************** *** 980,983 **** --- 983,1227 ---- 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 -cr stunnel-4.00.orig/src/options.c stunnel-4.00/src/options.c *** stunnel-4.00.orig/src/options.c Sun Aug 11 04:51:22 2002 --- stunnel-4.00/src/options.c Thu Oct 31 20:37:13 2002 *************** *** 632,637 **** --- 632,713 ---- } #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: *************** *** 746,752 **** else return "Illegal close timeout"; return NULL; /* OK */ ! case CMD_DEFAULT: log_raw("%-15s = %d seconds", "TIMEOUTclose", section->timeout_close); break; case CMD_HELP: --- 822,828 ---- else return "Illegal close timeout"; return NULL; /* OK */ ! case CMD_DEFAULT: log_raw("%-15s = %d seconds", "TIMEOUTclose", section->timeout_close); break; case CMD_HELP: diff -cr stunnel-4.00.orig/src/prototypes.h stunnel-4.00/src/prototypes.h *** stunnel-4.00.orig/src/prototypes.h Sun Aug 11 04:38:02 2002 --- stunnel-4.00/src/prototypes.h Thu Oct 31 20:37:13 2002 *************** *** 123,134 **** /* 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 */ --- 123,135 ---- /* 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 */ *************** *** 140,145 **** --- 141,147 ---- struct { int delayed_lookup:1; int remote:1; + int httpsproxy:1; #ifndef USE_WIN32 int program:1; int pty:1;