tBasic SOCKS5 (including user/passwd auth) support - vaccinewars - be a doctor and try to vaccinate the world
 (HTM) git clone git://src.adamsgaard.dk/vaccinewars
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 31bbdaef2e545842887bc2b5daad838f7f253ed8
 (DIR) parent 94c686d2f6a55c9e1aba1286e3681c3be61fb0ca
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Sun,  7 Oct 2001 22:40:11 +0000
       
       Basic SOCKS5 (including user/passwd auth) support
       
       
       Diffstat:
         M ChangeLog                           |       2 ++
         M TODO                                |       4 ++--
         M src/gtkport.c                       |      15 ++++++++++++++-
         M src/gtkport.h                       |       2 ++
         M src/network.c                       |     147 +++++++++++++++++++++++++++----
         M src/network.h                       |      42 ++++++++++++++++++-------------
       
       6 files changed, 175 insertions(+), 37 deletions(-)
       ---
 (DIR) diff --git a/ChangeLog b/ChangeLog
       t@@ -1,4 +1,6 @@
        cvs
       +    - Support for HTTP proxies and authentication
       +    - SOCKS4 and SOCKS5 (user/password) support
            - French translation added by leonard
            - Boolean configuration variables (TRUE/FALSE) now supported
            - Metaserver code is now non-blocking (and should soon support more
 (DIR) diff --git a/TODO b/TODO
       t@@ -1,5 +1,5 @@
       -- Support for HTTP authentication; report metaserver errors more clearly
       -- SOCKS support?
       +- Improve error reporting for network operations (e.g. metaserver)
       +- GSS_API SOCKS support?
        - Busy loop in GTK+ client on server crash - seems to be a GLib bug
        - Fix problem with dialogs popping up while menus are open
        - Fix problem with Jet dialog during fights
 (DIR) diff --git a/src/gtkport.c b/src/gtkport.c
       t@@ -1567,7 +1567,12 @@ GtkWidget *gtk_scrolled_text_new(GtkAdjustment *hadj,GtkAdjustment *vadj,
        }
        
        GtkWidget *gtk_entry_new() {
       -   return GTK_WIDGET(GtkNewObject(&GtkEntryClass));
       +   GtkEntry *entry;
       +
       +   entry = GTK_ENTRY(GtkNewObject(&GtkEntryClass));
       +   entry->is_visible = TRUE;
       +
       +   return GTK_WIDGET(entry);
        }
        
        GtkWidget *gtk_clist_new(gint columns) {
       t@@ -2035,6 +2040,7 @@ void gtk_entry_realize(GtkWidget *widget) {
           gtk_set_default_font(widget->hWnd);
           gtk_editable_set_editable(GTK_EDITABLE(widget),
                                     GTK_EDITABLE(widget)->is_editable);
       +   gtk_entry_set_visibility(GTK_ENTRY(widget),GTK_ENTRY(widget)->is_visible);
           SendMessage(widget->hWnd,WM_SETTEXT,0,
                       (LPARAM)GTK_EDITABLE(widget)->text->str);
        }
       t@@ -3796,6 +3802,13 @@ void gtk_entry_set_text(GtkEntry *entry,const gchar *text) {
           gtk_editable_insert_text(GTK_EDITABLE(entry),text,strlen(text),&pos);
        }
        
       +void gtk_entry_set_visibility(GtkEntry *entry,gboolean visible) {
       +   HWND hWnd;
       +   entry->is_visible = visible;
       +   hWnd=GTK_WIDGET(entry)->hWnd;
       +   if (hWnd) SendMessage(hWnd,EM_SETPASSWORDCHAR,visible ? 0 : (WPARAM)'*',0);
       +}
       +
        guint SetAccelerator(GtkWidget *labelparent,gchar *Text,
                             GtkWidget *sendto,gchar *signal,
                             GtkAccelGroup *accel_group) {
 (DIR) diff --git a/src/gtkport.h b/src/gtkport.h
       t@@ -239,6 +239,7 @@ struct _GtkEditable {
        
        struct _GtkEntry {
           GtkEditable editable;
       +   gint is_visible : 1;
        };
        
        struct _GtkSpinButton {
       t@@ -533,6 +534,7 @@ GtkWidget *gtk_radio_button_new_with_label_from_widget(GtkRadioButton *group,
        GtkWidget *gtk_frame_new(const gchar *text);
        GtkWidget *gtk_text_new(GtkAdjustment *hadj,GtkAdjustment *vadj);
        GtkWidget *gtk_entry_new();
       +void gtk_entry_set_visibility(GtkEntry *entry,gboolean visible);
        GtkWidget *gtk_table_new(guint rows,guint cols,gboolean homogeneous);
        void gtk_table_resize(GtkTable *table,guint rows,guint cols);
        GtkItemFactory *gtk_item_factory_new(GtkType container_type,
 (DIR) diff --git a/src/network.c b/src/network.c
       t@@ -59,14 +59,15 @@
        
        /* SOCKS5 authentication method codes */
        typedef enum {
       -   SM_NOAUTH =  0,   /* No authentication required */
       -   SM_GSSAPI =  1,   /* GSSAPI */
       -   SM_USERPWD = 2    /* Username/password authentication */
       +   SM_NOAUTH =  0,      /* No authentication required */
       +   SM_GSSAPI =  1,      /* GSSAPI */
       +   SM_USERPASSWD = 2    /* Username/password authentication */
        } SocksMethods;
        
        static gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
                                              unsigned RemotePort);
        static gchar *GetWaitingData(NetworkBuffer *NetBuf,int numbytes);
       +static gchar *PeekWaitingData(NetworkBuffer *NetBuf,int numbytes);
        static gchar *ExpandWriteBuffer(ConnBuf *conn,int numbytes);
        
        #ifdef CYGWIN
       t@@ -161,6 +162,7 @@ void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar,
           NetBuf->status = NBS_PRECONNECT;
           NetBuf->socks = socks;
           NetBuf->host = NULL;
       +   NetBuf->userpasswd = NULL;
           ClearError(&NetBuf->error);
        }
        
       t@@ -172,6 +174,11 @@ void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
           NetBufCallBack(NetBuf);
        }
        
       +void SetNetworkBufferUserPasswdFunc(NetworkBuffer *NetBuf,
       +                                    NBUserPasswd userpasswd) {
       +   NetBuf->userpasswd=userpasswd;
       +}
       +
        void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd) {
        /* Sets up the given network buffer to handle data being sent/received */
        /* through the given socket                                            */
       t@@ -271,6 +278,9 @@ typedef enum {
           SEC_IDMISMATCH = 93,
        
           SEC_UNKNOWN = 200,
       +   SEC_AUTHFAILED,
       +   SEC_USERCANCEL,
       +   SEC_ADDRTYPE,
           SEC_REPLYVERSION,
           SEC_VERSION,
           SEC_NOMETHODS
       t@@ -287,6 +297,9 @@ static ErrTable SocksErrStr[] = {
          { SEC_5COMMNOSUPP,N_("SOCKS: Command not supported") },
          { SEC_5ADDRNOSUPP,N_("SOCKS: Address type not supported") },
          { SEC_NOMETHODS,N_("SOCKS server rejected all offered methods") },
       +  { SEC_ADDRTYPE,N_("Unknown SOCKS address type returned") },
       +  { SEC_AUTHFAILED,N_("SOCKS authentication failed") },
       +  { SEC_USERCANCEL,N_("SOCKS authentication cancelled by user") },
        
        /* SOCKS version 4 error messages */
          { SEC_REJECT,N_("SOCKS: Request rejected or failed") },
       t@@ -306,6 +319,44 @@ static void SocksAppendError(GString *str,LastError *error) {
        
        static ErrorType ETSocks = { SocksAppendError };
        
       +static gboolean Socks5UserPasswd(NetworkBuffer *NetBuf) {
       +   gchar *user,*password;
       +   gchar *addpt;
       +   guint addlen;
       +   ConnBuf *conn;
       +
       +   if (!NetBuf->userpasswd) {
       +      SetError(&NetBuf->error,&ETSocks,SEC_NOMETHODS);
       +      return FALSE;
       +   }
       +   if (!(*NetBuf->userpasswd)(NetBuf,&user,&password)) {
       +      SetError(&NetBuf->error,&ETSocks,SEC_USERCANCEL);
       +      return FALSE;
       +   }
       +
       +   conn=&NetBuf->negbuf;
       +   addlen = 3 + strlen(user) + strlen(password);
       +   addpt = ExpandWriteBuffer(conn,addlen);
       +   if (!addpt || strlen(user)>255 || strlen(password)>255) {
       +      g_print("FIXME: buffer size exceeded\n"); return FALSE;
       +   }
       +   addpt[0] = 1;  /* Subnegotiation version code */
       +   addpt[1] = strlen(user);
       +   strcpy(&addpt[2],user);
       +   addpt[2+strlen(user)] = strlen(password);
       +   strcpy(&addpt[3+strlen(user)],password);
       +   g_free(user); g_free(password);
       +
       +   NetBuf->sockstat = NBSS_USERPASSWD;
       +   conn->DataPresent+=addlen;
       +
       +/* If the buffer was empty before, we may need to tell the owner to check
       +   the socket for write-ready status */
       +   if ((gchar *)addpt==conn->Data) NetBufCallBack(NetBuf);
       +
       +   return TRUE;
       +}
       +
        static gboolean Socks5Connect(NetworkBuffer *NetBuf) {
           guchar *addpt;
           guint addlen,hostlen;
       t@@ -325,11 +376,11 @@ static gboolean Socks5Connect(NetworkBuffer *NetBuf) {
           if (!addpt) {
              g_print("FIXME: buffer size exceeded\n"); return FALSE;
           }
       -   addpt[0] = 5;
       -   addpt[1] = 1;
       -   addpt[2] = 0;
       -   addpt[3] = 3;
       -   addpt[4] = hostlen;
       +   addpt[0] = 5;       /* SOCKS version 5 */
       +   addpt[1] = 1;       /* CONNECT */
       +   addpt[2] = 0;       /* reserved - must be zero */
       +   addpt[3] = 3;       /* Address type - FQDN */
       +   addpt[4] = hostlen; /* Length of address */
           strcpy(&addpt[5],NetBuf->host);
           memcpy(&addpt[5+hostlen],&netport,sizeof(netport));
        
       t@@ -347,6 +398,8 @@ static gboolean Socks5Connect(NetworkBuffer *NetBuf) {
        
        static gboolean HandleSocksReply(NetworkBuffer *NetBuf) {
           guchar *data;
       +   guchar addrtype;
       +   guint replylen;
           gboolean retval=TRUE;
           if (NetBuf->socks->version==5) {
              if (NetBuf->sockstat == NBSS_METHODS) {
       t@@ -362,12 +415,53 @@ static gboolean HandleSocksReply(NetworkBuffer *NetBuf) {
                       g_print("FIXME: Using SOCKS5 method %d\n",data[1]);
                       if (data[1]==SM_NOAUTH) {
                          retval=Socks5Connect(NetBuf);
       +               } else if (data[1]==SM_USERPASSWD) {
       +                  retval=Socks5UserPasswd(NetBuf);
                       }
                    }
                    g_free(data);
                 }
       +      } else if (NetBuf->sockstat == NBSS_USERPASSWD) {
       +         data = GetWaitingData(NetBuf,2);
       +         if (data) {
       +            retval=FALSE;
       +            if (data[0]!=5) {
       +               SetError(&NetBuf->error,&ETSocks,SEC_VERSION);
       +            } else if (data[1]!=0) {
       +               SetError(&NetBuf->error,&ETSocks,SEC_AUTHFAILED);
       +            } else {
       +               retval=Socks5Connect(NetBuf);
       +            }
       +            g_free(data);
       +         }
              } else if (NetBuf->sockstat == NBSS_CONNECT) {
        g_print("FIXME: SOCKS5 connect reply\n");
       +         data = PeekWaitingData(NetBuf,5);
       +         if (data) {
       +            retval=FALSE;
       +            addrtype = data[3];
       +            if (data[0]!=5) {
       +               SetError(&NetBuf->error,&ETSocks,SEC_VERSION);
       +            } else if (data[1]>8) {
       +               SetError(&NetBuf->error,&ETSocks,SEC_UNKNOWN);
       +            } else if (data[1]!=0) {
       +               SetError(&NetBuf->error,&ETSocks,data[1]);
       +            } else if (addrtype!=1 && addrtype!=3 && addrtype!=4) {
       +               SetError(&NetBuf->error,&ETSocks,SEC_ADDRTYPE);
       +            } else {
       +               retval=TRUE;
       +               replylen = 6;
       +               if (addrtype==1) replylen+=4;   /* IPv4 address */
       +               else if (addrtype==4) replylen+=16;   /* IPv6 address */
       +               else replylen+=data[4];   /* FQDN */
       +               data = GetWaitingData(NetBuf,replylen);
       +               if (data) {
       +                  g_print("FIXME: SOCKS5 sucessful connect\n");
       +                  NetBuf->status = NBS_CONNECTED;
       +                  g_free(data);
       +               }
       +            }
       +         }
              }
              return retval;
           } else {
       t@@ -505,6 +599,13 @@ gint CountWaitingMessages(NetworkBuffer *NetBuf) {
           return msgs;
        }
        
       +gchar *PeekWaitingData(NetworkBuffer *NetBuf,int numbytes) {
       +   ConnBuf *conn;
       +   conn=&NetBuf->ReadBuf;
       +   if (!conn->Data || conn->DataPresent < numbytes) return NULL;
       +   else return conn->Data;
       +}
       +
        gchar *GetWaitingData(NetworkBuffer *NetBuf,int numbytes) {
           ConnBuf *conn;
           gchar *data;
       t@@ -645,21 +746,22 @@ static struct hostent *LookupHostname(gchar *host,LastError *error) {
        
        gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
                                       unsigned RemotePort) {
       -   static SocksMethods methods[] = {
       -      SM_NOAUTH, SM_GSSAPI, SM_USERPWD
       -   };
       -   guint num_methods = sizeof(methods)/sizeof(methods[0]);
       +   guint num_methods;
           ConnBuf *conn;
           struct hostent *he;
           guchar *addpt;
           guint addlen,i;
           struct in_addr *h_addr;
           unsigned short int netport;
       +#ifndef CYGWIN
           struct passwd *pwd;
       +#endif
        
           conn=&NetBuf->negbuf;
        
           if (NetBuf->socks->version==5) {
       +      num_methods=1;
       +      if (NetBuf->userpasswd) num_methods++;
              addlen=2+num_methods;
              addpt = ExpandWriteBuffer(conn,addlen);
              if (!addpt) {
       t@@ -667,9 +769,9 @@ gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
              }
              addpt[0] = 5;   /* SOCKS version 5 */
              addpt[1] = num_methods;
       -      for (i=0;i<num_methods;i++) {
       -         addpt[2+i] = (guchar)methods[i];
       -      }
       +      i=2;
       +      addpt[i++] = SM_NOAUTH;
       +      if (NetBuf->userpasswd) addpt[i++] = SM_USERPASSWD;
              conn->DataPresent+=addlen;
        
              g_free(NetBuf->host);
       t@@ -686,11 +788,15 @@ gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,gchar *RemoteHost,
           he = LookupHostname(RemoteHost,&NetBuf->error);
           if (!he) return FALSE;
        
       +#ifndef CYGWIN
           pwd = getpwuid(getuid());
           if (!pwd || !pwd->pw_name) return FALSE;
        g_print("username %s\n",pwd->pw_name);
       -
           addlen=9+strlen(pwd->pw_name);
       +#else
       +   addlen=13;
       +#endif
       +
        
           h_addr = (struct in_addr *)he->h_addr;
           g_assert(sizeof(struct in_addr)==4);
       t@@ -707,10 +813,14 @@ g_print("username %s\n",pwd->pw_name);
           addpt[1] = 1;  /* CONNECT */
           memcpy(&addpt[2],&netport,sizeof(netport));
           memcpy(&addpt[4],h_addr,sizeof(struct in_addr));
       +#ifdef CYGWIN
       +   strcpy(&addpt[8],"user");
       +#else
           strcpy(&addpt[8],pwd->pw_name);
       +#endif
           addpt[addlen-1] = '\0';
        
       -   g_print("FIXME: SOCKS CONNECT request sent\n");
       +   g_print("FIXME: SOCKS4 CONNECT request sent\n");
           
           conn->DataPresent+=addlen;
        
       t@@ -982,6 +1092,7 @@ gchar *ReadHttpResponse(HttpConnection *conn) {
        gboolean HandleHttpCompletion(HttpConnection *conn) {
           NBCallBack CallBack;
           gpointer CallBackData;
       +   NBUserPasswd userpasswd;
           gboolean retry=FALSE;
        
           if (conn->Tries>=5) {
       t@@ -1005,11 +1116,13 @@ gboolean HandleHttpCompletion(HttpConnection *conn) {
        
           if (retry) {
              CallBack=conn->NetBuf.CallBack;
       +      userpasswd=conn->NetBuf.userpasswd;
              CallBackData=conn->NetBuf.CallBackData;
              ShutdownNetworkBuffer(&conn->NetBuf);
              if (StartHttpConnect(conn)) {
                 SendHttpRequest(conn);
                 SetNetworkBufferCallBack(&conn->NetBuf,CallBack,CallBackData);
       +         SetNetworkBufferUserPasswdFunc(&conn->NetBuf,userpasswd);
                 return FALSE;
              }
           }
 (DIR) diff --git a/src/network.h b/src/network.h
       t@@ -65,6 +65,9 @@ typedef struct _NetworkBuffer NetworkBuffer;
        
        typedef void (*NBCallBack)(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
        
       +typedef gboolean (*NBUserPasswd)(NetworkBuffer *NetBuf,
       +                                 gchar **user,gchar **password);
       +
        /* Information about a SOCKS server */
        typedef struct _SocksServer {
           gchar *name;     /* hostname */
       t@@ -82,28 +85,31 @@ typedef enum {
        /* Status of a SOCKS v5 negotiation */
        typedef enum {
           NBSS_METHODS,      /* Negotiation of available methods */
       +   NBSS_USERPASSWD,   /* Username-password request is being sent */
           NBSS_CONNECT       /* CONNECT request is being sent */
        } NBSocksStatus;
        
        /* Handles reading and writing messages from/to a network connection */
        struct _NetworkBuffer {
       -   int fd;                 /* File descriptor of the socket */
       -   gint InputTag;          /* Identifier for gdk_input routines */
       -   NBCallBack CallBack;    /* Function called when the socket read- or
       -                              write-able status changes */
       -   gpointer CallBackData;  /* Data accessible to the callback function */
       -   char Terminator;        /* Character that separates messages */
       -   char StripChar;         /* Character that should be removed from messages */
       -   ConnBuf ReadBuf;        /* New data, waiting for the application */
       -   ConnBuf WriteBuf;       /* Data waiting to be written to the wire */
       -   ConnBuf negbuf;         /* Output for protocol negotiation (e.g. SOCKS) */
       -   gboolean WaitConnect;   /* TRUE if a non-blocking connect is in progress */
       -   NBStatus status;        /* Status of the connection (if any) */
       -   NBSocksStatus sockstat; /* Status of SOCKS negotiation (if any) */
       -   SocksServer *socks;     /* If non-NULL, a SOCKS server to use */
       -   gchar *host;            /* If non-NULL, the host to connect to */
       -   unsigned port;          /* If non-NULL, the port to connect to */
       -   LastError error;        /* Any error from the last operation */
       +   int fd;                  /* File descriptor of the socket */
       +   gint InputTag;           /* Identifier for gdk_input routines */
       +   NBCallBack CallBack;     /* Function called when the socket read- or
       +                               write-able status changes */
       +   gpointer CallBackData;   /* Data accessible to the callback function */
       +   char Terminator;         /* Character that separates messages */
       +   char StripChar;          /* Char that should be removed from messages */
       +   ConnBuf ReadBuf;         /* New data, waiting for the application */
       +   ConnBuf WriteBuf;        /* Data waiting to be written to the wire */
       +   ConnBuf negbuf;          /* Output for protocol negotiation (e.g. SOCKS) */
       +   gboolean WaitConnect;    /* TRUE if a non-blocking connect is in progress */
       +   NBStatus status;         /* Status of the connection (if any) */
       +   NBSocksStatus sockstat;  /* Status of SOCKS negotiation (if any) */
       +   SocksServer *socks;      /* If non-NULL, a SOCKS server to use */
       +   NBUserPasswd userpasswd; /* Function to supply username and password for
       +                               SOCKS5 authentication */  
       +   gchar *host;             /* If non-NULL, the host to connect to */
       +   unsigned port;           /* If non-NULL, the port to connect to */
       +   LastError error;         /* Any error from the last operation */
        };
        
        /* Keeps track of the progress of an HTTP connection */
       t@@ -144,6 +150,8 @@ void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar,
                               SocksServer *socks);
        void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
                                      gpointer CallBackData);
       +void SetNetworkBufferUserPasswdFunc(NetworkBuffer *NetBuf,
       +                                    NBUserPasswd userpasswd);
        gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf);
        void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd);
        gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,