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 <bob@virtualunlimited.com>
+  * Modified by Daniel Savard <savardd@gnulinux.ca> 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 <bob@virtualunlimited.com> 
+  *  Modified by Daniel Savard <savardd@gnulinux.ca> 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 <sweeheng@srikant.org>
+  * Modified by Daniel Savard <savardd@gnulinux.ca> 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 <savardd@gnulinux.ca>
+      * 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 <savardd@gnulinux.ca>
+      * 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",
+                 &section->httpsproxy_dest_names, &section->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 <savardd@gnulinux.ca>
+      * 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;
