tBasic SOCKS5 (including user/passwd auth) support - vaccinewars - be a doctor … | |
git clone git://src.adamsgaard.dk/vaccinewars | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 31bbdaef2e545842887bc2b5daad838f7f253ed8 | |
parent 94c686d2f6a55c9e1aba1286e3681c3be61fb0ca | |
Author: Ben Webb <[email protected]> | |
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(-) | |
--- | |
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 | |
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 | |
diff --git a/src/gtkport.c b/src/gtkport.c | |
t@@ -1567,7 +1567,12 @@ GtkWidget *gtk_scrolled_text_new(GtkAdjustment *hadj,Gt… | |
} | |
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 *te… | |
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) { | |
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(Gtk… | |
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, | |
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 Terminat… | |
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,NBCal… | |
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 *erro… | |
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,LastEr… | |
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… | |
} | |
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,gch… | |
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; | |
} | |
} | |
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 Terminat… | |
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, |