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,