aboutsummaryrefslogtreecommitdiff
path: root/libsylph
diff options
context:
space:
mode:
authorhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2010-12-07 04:48:14 +0000
committerhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2010-12-07 04:48:14 +0000
commit14c652150c9a6339c792eb1911da809327237123 (patch)
treead1bfa3e8818651ae8e4026f7571ec6392fb45b0 /libsylph
parent3a54dc9a9d8e28b9415552512d45d172528d1a78 (diff)
added SOCKS4/5 proxy support.
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@2733 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'libsylph')
-rw-r--r--libsylph/Makefile.am2
-rw-r--r--libsylph/prefs_account.c11
-rw-r--r--libsylph/prefs_account.h13
-rw-r--r--libsylph/session.c79
-rw-r--r--libsylph/session.h7
-rw-r--r--libsylph/socks.c251
-rw-r--r--libsylph/socks.h59
7 files changed, 415 insertions, 7 deletions
diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am
index aef0e3cb..1aeb5e6b 100644
--- a/libsylph/Makefile.am
+++ b/libsylph/Makefile.am
@@ -37,6 +37,7 @@ libsylph_0_la_SOURCES = \
session.c \
smtp.c \
socket.c \
+ socks.c \
ssl.c \
stringtable.c \
sylmain.c \
@@ -78,6 +79,7 @@ libsylph_0include_HEADERS = \
session.h \
smtp.h \
socket.h \
+ socks.h \
ssl.h \
stringtable.h \
sylmain.h \
diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c
index e60a3bec..1eaa7231 100644
--- a/libsylph/prefs_account.c
+++ b/libsylph/prefs_account.c
@@ -132,6 +132,17 @@ static PrefParam param[] = {
{"set_trash_folder", "FALSE", &tmp_ac_prefs.set_trash_folder, P_BOOL},
{"trash_folder", NULL, &tmp_ac_prefs.trash_folder, P_STRING},
+ /* SOCKS proxy */
+ {"use_socks", "FALSE", &tmp_ac_prefs.use_socks, P_BOOL},
+ {"use_socks_for_recv", "TRUE", &tmp_ac_prefs.use_socks_for_recv, P_BOOL},
+ {"use_socks_for_send", "TRUE", &tmp_ac_prefs.use_socks_for_send, P_BOOL},
+ {"socks_type", "1", &tmp_ac_prefs.socks_type, P_ENUM},
+ {"proxy_host", NULL, &tmp_ac_prefs.proxy_host, P_STRING},
+ {"proxy_port", "1080", &tmp_ac_prefs.proxy_port, P_USHORT},
+ {"use_proxy_auth", "FALSE", &tmp_ac_prefs.use_proxy_auth, P_BOOL},
+ {"proxy_name", NULL, &tmp_ac_prefs.proxy_name, P_STRING},
+ {"proxy_pass", NULL, &tmp_ac_prefs.proxy_pass, P_STRING},
+
{NULL, NULL, NULL, P_OTHER}
};
diff --git a/libsylph/prefs_account.h b/libsylph/prefs_account.h
index d666fd1b..53295801 100644
--- a/libsylph/prefs_account.h
+++ b/libsylph/prefs_account.h
@@ -1,6 +1,6 @@
/*
* LibSylph -- E-Mail client library
- * Copyright (C) 1999-2007 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2010 Hiroyuki Yamamoto
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -169,6 +169,17 @@ struct _PrefsAccount
/* Compose */
gboolean sig_before_quote;
+
+ /* Advanced - SOCKS proxy */
+ gboolean use_socks;
+ gboolean use_socks_for_recv;
+ gboolean use_socks_for_send;
+ gint socks_type;
+ gchar *proxy_host;
+ gushort proxy_port;
+ gboolean use_proxy_auth;
+ gchar *proxy_name;
+ gchar *proxy_pass;
};
PrefsAccount *prefs_account_new (void);
diff --git a/libsylph/session.c b/libsylph/session.c
index 0b561eda..f45138f0 100644
--- a/libsylph/session.c
+++ b/libsylph/session.c
@@ -1,6 +1,6 @@
/*
* LibSylph -- E-Mail client library
- * Copyright (C) 1999-2009 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2010 Hiroyuki Yamamoto
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -35,6 +35,18 @@
#include "session.h"
#include "utils.h"
+typedef struct _SessionPrivData SessionPrivData;
+
+struct _SessionPrivData {
+ Session *session;
+ SocksInfo *socks_info;
+ gpointer data;
+};
+
+static GList *priv_list = NULL;
+
+static SessionPrivData *session_get_priv(Session *session);
+
static gint session_connect_cb (SockInfo *sock,
gpointer data);
static gint session_close (Session *session);
@@ -117,8 +129,30 @@ void session_init(Session *session)
session->data = NULL;
}
+static SessionPrivData *session_get_priv(Session *session)
+{
+ SessionPrivData *priv;
+ GList *cur;
+
+ g_return_val_if_fail(session != NULL, NULL);
+
+ for (cur = priv_list; cur != NULL; cur = cur->next) {
+ priv = (SessionPrivData *)cur->data;
+ if (priv->session == session)
+ return priv;
+ }
+
+ return NULL;
+}
+
gint session_connect(Session *session, const gchar *server, gushort port)
{
+ return session_connect_full(session, server, port, NULL);
+}
+
+gint session_connect_full(Session *session, const gchar *server, gushort port,
+ SocksInfo *socks_info)
+{
#ifndef G_OS_UNIX
SockInfo *sock = NULL;
#endif
@@ -132,6 +166,11 @@ gint session_connect(Session *session, const gchar *server, gushort port)
}
session->port = port;
+ if (socks_info) {
+ server = socks_info->proxy_host;
+ port = socks_info->proxy_port;
+ }
+
#ifdef G_OS_UNIX
session->conn_id = sock_connect_async(server, port, session_connect_cb,
session);
@@ -140,8 +179,6 @@ gint session_connect(Session *session, const gchar *server, gushort port)
session->state = SESSION_ERROR;
return -1;
}
-
- return 0;
#elif USE_THREADS
session->conn_id = sock_connect_async_thread(server, port);
if (session->conn_id < 0) {
@@ -154,8 +191,6 @@ gint session_connect(Session *session, const gchar *server, gushort port)
session->state = SESSION_ERROR;
return -1;
}
-
- return session_connect_cb(sock, session);
#else /* !USE_THREADS */
sock = sock_connect(server, port);
if (sock == NULL) {
@@ -163,7 +198,20 @@ gint session_connect(Session *session, const gchar *server, gushort port)
session->state = SESSION_ERROR;
return -1;
}
+#endif
+
+ if (socks_info) {
+ SessionPrivData *priv;
+
+ priv = g_new0(SessionPrivData, 1);
+ priv->session = session;
+ priv->socks_info = socks_info;
+ priv_list = g_list_prepend(priv_list, priv);
+ }
+#ifdef G_OS_UNIX
+ return 0;
+#else
return session_connect_cb(sock, session);
#endif
}
@@ -171,6 +219,7 @@ gint session_connect(Session *session, const gchar *server, gushort port)
static gint session_connect_cb(SockInfo *sock, gpointer data)
{
Session *session = SESSION(data);
+ SessionPrivData *priv;
session->conn_id = 0;
@@ -182,6 +231,17 @@ static gint session_connect_cb(SockInfo *sock, gpointer data)
session->sock = sock;
+ priv = session_get_priv(session);
+ if (priv && priv->socks_info) {
+ sock_set_nonblocking_mode(sock, FALSE);
+ if (socks_connect(sock, session->server, session->port,
+ priv->socks_info) < 0) {
+ g_warning("can't establish SOCKS connection.");
+ session->state = SESSION_ERROR;
+ return -1;
+ }
+ }
+
#if USE_SSL
if (session->ssl_type == SSL_TUNNEL) {
sock_set_nonblocking_mode(sock, FALSE);
@@ -217,6 +277,8 @@ gint session_disconnect(Session *session)
void session_destroy(Session *session)
{
+ SessionPrivData *priv;
+
g_return_if_fail(session != NULL);
g_return_if_fail(session->destroy != NULL);
@@ -230,6 +292,13 @@ void session_destroy(Session *session)
fclose(session->read_data_fp);
g_free(session->write_buf);
+ priv = session_get_priv(session);
+ if (priv) {
+ priv_list = g_list_remove(priv_list, priv);
+ socks_info_free(priv->socks_info);
+ g_free(priv);
+ }
+
debug_print("session (%p): destroyed\n", session);
g_free(session);
diff --git a/libsylph/session.h b/libsylph/session.h
index b5127c0e..a807a4ae 100644
--- a/libsylph/session.h
+++ b/libsylph/session.h
@@ -1,6 +1,6 @@
/*
* LibSylph -- E-Mail client library
- * Copyright (C) 1999-2007 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2010 Hiroyuki Yamamoto
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -30,6 +30,7 @@
#include <unistd.h>
#include "socket.h"
+#include "socks.h"
#define SESSION_BUFFSIZE 8192
@@ -181,6 +182,10 @@ void session_init (Session *session);
gint session_connect (Session *session,
const gchar *server,
gushort port);
+gint session_connect_full (Session *session,
+ const gchar *server,
+ gushort port,
+ SocksInfo *socks_info);
gint session_disconnect (Session *session);
void session_destroy (Session *session);
gboolean session_is_connected (Session *session);
diff --git a/libsylph/socks.c b/libsylph/socks.c
new file mode 100644
index 00000000..7c799b5c
--- /dev/null
+++ b/libsylph/socks.c
@@ -0,0 +1,251 @@
+/*
+ * LibSylph -- E-Mail client library
+ * Copyright (C) 1999-2010 Hiroyuki Yamamoto
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#include "socks.h"
+#include "utils.h"
+
+
+SocksInfo *socks_info_new(SocksType type, const gchar *proxy_host,
+ gushort proxy_port, const gchar *proxy_name,
+ const gchar *proxy_pass)
+{
+ SocksInfo *socks_info;
+
+ socks_info = g_new0(SocksInfo, 1);
+ socks_info->type = type;
+ socks_info->proxy_host = g_strdup(proxy_host);
+ socks_info->proxy_port = proxy_port;
+ socks_info->proxy_name = g_strdup(proxy_name);
+ socks_info->proxy_pass = g_strdup(proxy_pass);
+
+ return socks_info;
+}
+
+void socks_info_free(SocksInfo *socks_info)
+{
+ if (!socks_info)
+ return;
+ g_free(socks_info->proxy_host);
+ g_free(socks_info->proxy_name);
+ g_free(socks_info->proxy_pass);
+ g_free(socks_info);
+}
+
+gint socks_connect(SockInfo *sock, const gchar *hostname, gushort port,
+ SocksInfo *socks_info)
+{
+ g_return_val_if_fail(sock != NULL, -1);
+ g_return_val_if_fail(hostname != NULL, -1);
+ g_return_val_if_fail(socks_info != NULL, -1);
+
+ debug_print("socks_connect: connect to %s:%u via %s:%u\n",
+ hostname, port,
+ socks_info->proxy_host, socks_info->proxy_port);
+
+ if (socks_info->type == SOCKS_SOCKS5)
+ return socks5_connect(sock, hostname, port,
+ socks_info->proxy_name,
+ socks_info->proxy_pass);
+ else if (socks_info->type == SOCKS_SOCKS4)
+ return socks4_connect(sock, hostname, port);
+ else
+ g_warning("socks_connect: unknown SOCKS type: %d\n",
+ socks_info->type);
+
+ return -1;
+}
+
+gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port)
+{
+ guchar socks_req[1024];
+ struct hostent *hp;
+
+ g_return_val_if_fail(sock != NULL, -1);
+ g_return_val_if_fail(hostname != NULL, -1);
+
+ debug_print("socks4_connect: connect to %s:%u\n", hostname, port);
+
+ socks_req[0] = 4;
+ socks_req[1] = 1;
+ *((gushort *)(socks_req + 2)) = htons(port);
+
+ /* lookup */
+ if ((hp = my_gethostbyname(hostname)) == NULL) {
+ g_warning("socks4_connect: cannot lookup host: %s", hostname);
+ return -1;
+ }
+ if (hp->h_length != 4) {
+ g_warning("socks4_connect: invalid address length for host: %s", hostname);
+ return -1;
+ }
+ memcpy(socks_req + 4, (guchar *)hp->h_addr, 4);
+ g_print("addr = %u.", ((guchar *)(hp->h_addr))[0]);
+ g_print("%u.", ((guchar *)(hp->h_addr))[1]);
+ g_print("%u.", ((guchar *)(hp->h_addr))[2]);
+ g_print("%u\n", ((guchar *)(hp->h_addr))[3]);
+
+ /* userid (empty) */
+ socks_req[8] = 0;
+
+ if (sock_write_all(sock, (gchar *)socks_req, 9) != 9) {
+ g_warning("socks4_connect: SOCKS4 initial request write failed");
+ return -1;
+ }
+
+ if (sock_read(sock, (gchar *)socks_req, 8) != 8) {
+ g_warning("socks4_connect: SOCKS4 response read failed");
+ return -1;
+ }
+ if (socks_req[0] != 0) {
+ g_warning("socks4_connect: SOCKS4 response has invalid version");
+ return -1;
+ }
+ if (socks_req[1] != 90) {
+ g_warning("socks4_connect: SOCKS4 connection to %u.%u.%u.%u:%u failed. (%u)", socks_req[4], socks_req[5], socks_req[6], socks_req[7], ntohs(*(gushort *)(socks_req + 2)), socks_req[1]);
+ return -1;
+ }
+
+ debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname, port);
+
+ return 0;
+}
+
+gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port,
+ const gchar *proxy_name, const gchar *proxy_pass)
+{
+ guchar socks_req[1024];
+ size_t len;
+ size_t size;
+
+ g_return_val_if_fail(sock != NULL, -1);
+ g_return_val_if_fail(hostname != NULL, -1);
+
+ debug_print("socks5_connect: connect to %s:%u\n", hostname, port);
+
+ len = strlen(hostname);
+ if (len > 255) {
+ g_warning("socks5_connect: hostname too long");
+ return -1;
+ }
+
+ socks_req[0] = 5;
+ socks_req[1] = proxy_name ? 2 : 1;
+ socks_req[2] = 0;
+ socks_req[3] = 2;
+
+ if (sock_write_all(sock, (gchar *)socks_req, 2 + socks_req[1]) != 2 + socks_req[1]) {
+ g_warning("socks5_connect: SOCKS5 initial request write failed");
+ return -1;
+ }
+
+ if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
+ g_warning("socks5_connect: SOCKS5 response read failed");
+ return -1;
+ }
+ if (socks_req[0] != 5) {
+ g_warning("socks5_connect: SOCKS5 response has invalid version");
+ return -1;
+ }
+ if (socks_req[1] == 2) {
+ /* auth */
+ size_t userlen, passlen;
+ gint reqlen;
+
+ if (proxy_name && proxy_pass) {
+ userlen = strlen(proxy_name);
+ passlen = strlen(proxy_pass);
+ } else
+ userlen = passlen = 0;
+
+ socks_req[0] = 1;
+ socks_req[1] = (guchar)userlen;
+ if (proxy_name && userlen > 0)
+ memcpy(socks_req + 2, proxy_name, userlen);
+ socks_req[2 + userlen] = (guchar)passlen;
+ if (proxy_pass && passlen > 0)
+ memcpy(socks_req + 2 + userlen + 1, proxy_pass, passlen);
+
+ reqlen = 2 + userlen + 1 + passlen;
+ if (sock_write_all(sock, (gchar *)socks_req, reqlen) != reqlen) {
+ g_warning("socks5_connect: SOCKS5 auth write failed");
+ return -1;
+ }
+ if (sock_read(sock, (gchar *)socks_req, 2) != 2) {
+ g_warning("socks5_connect: SOCKS5 auth response read failed");
+ return -1;
+ }
+ if (socks_req[1] != 0) {
+ g_warning("socks5_connect: SOCKS5 authentication failed: user: %s (%u %u)", proxy_name ? proxy_name : "(none)", socks_req[0], socks_req[1]);
+ return -1;
+ }
+ } else if (socks_req[1] != 0) {
+ g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req[1]);
+ return -1;
+ }
+
+ socks_req[0] = 5;
+ socks_req[1] = 1;
+ socks_req[2] = 0;
+
+ socks_req[3] = 3;
+ socks_req[4] = (guchar)len;
+ memcpy(socks_req + 5, hostname, len);
+ *((gushort *)(socks_req + 5 + len)) = htons(port);
+
+ if (sock_write_all(sock, (gchar *)socks_req, 5 + len + 2) != 5 + len + 2) {
+ g_warning("socks5_connect: SOCKS5 connect request write failed");
+ return -1;
+ }
+
+ if (sock_read(sock, (gchar *)socks_req, 10) != 10) {
+ g_warning("socks5_connect: SOCKS5 connect request response read failed");
+ return -1;
+ }
+ if (socks_req[0] != 5) {
+ g_warning("socks5_connect: SOCKS5 response has invalid version");
+ return -1;
+ }
+ if (socks_req[1] != 0) {
+ g_warning("socks5_connect: SOCKS5 connection to %u.%u.%u.%u:%u failed. (%u)", socks_req[4], socks_req[5], socks_req[6], socks_req[7], ntohs(*(gushort *)(socks_req + 8)), socks_req[1]);
+ return -1;
+ }
+
+ size = 10;
+ if (socks_req[3] == 3)
+ size = 5 + socks_req[4] + 2;
+ else if (socks_req[3] == 4)
+ size = 4 + 16 + 2;
+ if (size > 10) {
+ size -= 10;
+ if (sock_read(sock, (gchar *)socks_req + 10, size) != size) {
+ g_warning("socks5_connect: SOCKS5 connect request response read failed");
+ return -1;
+ }
+ }
+
+ debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname, port);
+
+ return 0;
+}
diff --git a/libsylph/socks.h b/libsylph/socks.h
new file mode 100644
index 00000000..afa34c5b
--- /dev/null
+++ b/libsylph/socks.h
@@ -0,0 +1,59 @@
+/*
+ * LibSylph -- E-Mail client library
+ * Copyright (C) 1999-2010 Hiroyuki Yamamoto
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __SOCKS_H__
+#define __SOCKS_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#include "socket.h"
+
+typedef struct _SocksInfo SocksInfo;
+
+typedef enum {
+ SOCKS_SOCKS4,
+ SOCKS_SOCKS5
+} SocksType;
+
+struct _SocksInfo
+{
+ SocksType type;
+ gchar *proxy_host;
+ gushort proxy_port;
+ gchar *proxy_name;
+ gchar *proxy_pass;
+};
+
+SocksInfo *socks_info_new(SocksType type, const gchar *proxy_host,
+ gushort proxy_port, const gchar *proxy_name,
+ const gchar *proxy_pass);
+void socks_info_free(SocksInfo *socks_info);
+
+gint socks_connect(SockInfo *sock, const gchar *hostname, gushort port,
+ SocksInfo *socks_info);
+
+gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port);
+gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port,
+ const gchar *proxy_name, const gchar *proxy_pass);
+
+#endif /* __SOCKS_H__ */