diff options
Diffstat (limited to 'libsylph/socks.c')
| -rw-r--r-- | libsylph/socks.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/libsylph/socks.c b/libsylph/socks.c new file mode 100644 index 0000000..b725ba7 --- /dev/null +++ b/libsylph/socks.c | |||
| @@ -0,0 +1,267 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2014 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | |||
| 26 | #ifdef G_OS_WIN32 | ||
| 27 | # include <winsock2.h> | ||
| 28 | # include <ws2tcpip.h> | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #include "socket.h" | ||
| 32 | #include "socks.h" | ||
| 33 | #include "utils.h" | ||
| 34 | |||
| 35 | |||
| 36 | SocksInfo *socks_info_new(SocksType type, const gchar *proxy_host, | ||
| 37 | gushort proxy_port, const gchar *proxy_name, | ||
| 38 | const gchar *proxy_pass) | ||
| 39 | { | ||
| 40 | SocksInfo *socks_info; | ||
| 41 | |||
| 42 | socks_info = g_new0(SocksInfo, 1); | ||
| 43 | socks_info->type = type; | ||
| 44 | socks_info->proxy_host = g_strdup(proxy_host); | ||
| 45 | socks_info->proxy_port = proxy_port; | ||
| 46 | socks_info->proxy_name = g_strdup(proxy_name); | ||
| 47 | socks_info->proxy_pass = g_strdup(proxy_pass); | ||
| 48 | |||
| 49 | return socks_info; | ||
| 50 | } | ||
| 51 | |||
| 52 | void socks_info_free(SocksInfo *socks_info) | ||
| 53 | { | ||
| 54 | if (!socks_info) | ||
| 55 | return; | ||
| 56 | g_free(socks_info->proxy_host); | ||
| 57 | g_free(socks_info->proxy_name); | ||
| 58 | g_free(socks_info->proxy_pass); | ||
| 59 | g_free(socks_info); | ||
| 60 | } | ||
| 61 | |||
| 62 | gint socks_connect(SockInfo *sock, const gchar *hostname, gushort port, | ||
| 63 | SocksInfo *socks_info) | ||
| 64 | { | ||
| 65 | g_return_val_if_fail(sock != NULL, -1); | ||
| 66 | g_return_val_if_fail(hostname != NULL, -1); | ||
| 67 | g_return_val_if_fail(socks_info != NULL, -1); | ||
| 68 | |||
| 69 | debug_print("socks_connect: connect to %s:%u via %s:%u\n", | ||
| 70 | hostname, port, | ||
| 71 | socks_info->proxy_host, socks_info->proxy_port); | ||
| 72 | |||
| 73 | if (socks_info->type == SOCKS_SOCKS5) | ||
| 74 | return socks5_connect(sock, hostname, port, | ||
| 75 | socks_info->proxy_name, | ||
| 76 | socks_info->proxy_pass); | ||
| 77 | else if (socks_info->type == SOCKS_SOCKS4) | ||
| 78 | return socks4_connect(sock, hostname, port); | ||
| 79 | else | ||
| 80 | g_warning("socks_connect: unknown SOCKS type: %d\n", | ||
| 81 | socks_info->type); | ||
| 82 | |||
| 83 | return -1; | ||
| 84 | } | ||
| 85 | |||
| 86 | gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port) | ||
| 87 | { | ||
| 88 | guchar socks_req[1024]; | ||
| 89 | struct hostent *hp; | ||
| 90 | |||
| 91 | g_return_val_if_fail(sock != NULL, -1); | ||
| 92 | g_return_val_if_fail(hostname != NULL, -1); | ||
| 93 | |||
| 94 | debug_print("socks4_connect: connect to %s:%u\n", hostname, port); | ||
| 95 | |||
| 96 | socks_req[0] = 4; | ||
| 97 | socks_req[1] = 1; | ||
| 98 | *((gushort *)(socks_req + 2)) = htons(port); | ||
| 99 | |||
| 100 | /* lookup */ | ||
| 101 | if ((hp = my_gethostbyname(hostname)) == NULL) { | ||
| 102 | g_warning("socks4_connect: cannot lookup host: %s", hostname); | ||
| 103 | return -1; | ||
| 104 | } | ||
| 105 | if (hp->h_length != 4) { | ||
| 106 | g_warning("socks4_connect: invalid address length for host: %s", hostname); | ||
| 107 | return -1; | ||
| 108 | } | ||
| 109 | memcpy(socks_req + 4, (guchar *)hp->h_addr, 4); | ||
| 110 | |||
| 111 | /* userid (empty) */ | ||
| 112 | socks_req[8] = 0; | ||
| 113 | |||
| 114 | if (sock_write_all(sock, (gchar *)socks_req, 9) != 9) { | ||
| 115 | g_warning("socks4_connect: SOCKS4 initial request write failed"); | ||
| 116 | return -1; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (sock_read(sock, (gchar *)socks_req, 8) != 8) { | ||
| 120 | g_warning("socks4_connect: SOCKS4 response read failed"); | ||
| 121 | return -1; | ||
| 122 | } | ||
| 123 | if (socks_req[0] != 0) { | ||
| 124 | g_warning("socks4_connect: SOCKS4 response has invalid version"); | ||
| 125 | return -1; | ||
| 126 | } | ||
| 127 | if (socks_req[1] != 90) { | ||
| 128 | 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]); | ||
| 129 | return -1; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* replace sock->hostname with endpoint */ | ||
| 133 | if (sock->hostname != hostname) { | ||
| 134 | g_free(sock->hostname); | ||
| 135 | sock->hostname = g_strdup(hostname); | ||
| 136 | sock->port = port; | ||
| 137 | } | ||
| 138 | |||
| 139 | debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname, port); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port, | ||
| 145 | const gchar *proxy_name, const gchar *proxy_pass) | ||
| 146 | { | ||
| 147 | guchar socks_req[1024]; | ||
| 148 | size_t len; | ||
| 149 | size_t size; | ||
| 150 | |||
| 151 | g_return_val_if_fail(sock != NULL, -1); | ||
| 152 | g_return_val_if_fail(hostname != NULL, -1); | ||
| 153 | |||
| 154 | debug_print("socks5_connect: connect to %s:%u\n", hostname, port); | ||
| 155 | |||
| 156 | len = strlen(hostname); | ||
| 157 | if (len > 255) { | ||
| 158 | g_warning("socks5_connect: hostname too long"); | ||
| 159 | return -1; | ||
| 160 | } | ||
| 161 | |||
| 162 | socks_req[0] = 5; | ||
| 163 | socks_req[1] = proxy_name ? 2 : 1; | ||
| 164 | socks_req[2] = 0; | ||
| 165 | socks_req[3] = 2; | ||
| 166 | |||
| 167 | if (sock_write_all(sock, (gchar *)socks_req, 2 + socks_req[1]) != 2 + socks_req[1]) { | ||
| 168 | g_warning("socks5_connect: SOCKS5 initial request write failed"); | ||
| 169 | return -1; | ||
| 170 | } | ||
| 171 | |||
| 172 | if (sock_read(sock, (gchar *)socks_req, 2) != 2) { | ||
| 173 | g_warning("socks5_connect: SOCKS5 response read failed"); | ||
| 174 | return -1; | ||
| 175 | } | ||
| 176 | if (socks_req[0] != 5) { | ||
| 177 | g_warning("socks5_connect: SOCKS5 response has invalid version"); | ||
| 178 | return -1; | ||
| 179 | } | ||
| 180 | if (socks_req[1] == 2) { | ||
| 181 | /* auth */ | ||
| 182 | size_t userlen, passlen; | ||
| 183 | gint reqlen; | ||
| 184 | |||
| 185 | if (proxy_name && proxy_pass) { | ||
| 186 | userlen = strlen(proxy_name); | ||
| 187 | passlen = strlen(proxy_pass); | ||
| 188 | } else | ||
| 189 | userlen = passlen = 0; | ||
| 190 | |||
| 191 | socks_req[0] = 1; | ||
| 192 | socks_req[1] = (guchar)userlen; | ||
| 193 | if (proxy_name && userlen > 0) | ||
| 194 | memcpy(socks_req + 2, proxy_name, userlen); | ||
| 195 | socks_req[2 + userlen] = (guchar)passlen; | ||
| 196 | if (proxy_pass && passlen > 0) | ||
| 197 | memcpy(socks_req + 2 + userlen + 1, proxy_pass, passlen); | ||
| 198 | |||
| 199 | reqlen = 2 + userlen + 1 + passlen; | ||
| 200 | if (sock_write_all(sock, (gchar *)socks_req, reqlen) != reqlen) { | ||
| 201 | g_warning("socks5_connect: SOCKS5 auth write failed"); | ||
| 202 | return -1; | ||
| 203 | } | ||
| 204 | if (sock_read(sock, (gchar *)socks_req, 2) != 2) { | ||
| 205 | g_warning("socks5_connect: SOCKS5 auth response read failed"); | ||
| 206 | return -1; | ||
| 207 | } | ||
| 208 | if (socks_req[1] != 0) { | ||
| 209 | g_warning("socks5_connect: SOCKS5 authentication failed: user: %s (%u %u)", proxy_name ? proxy_name : "(none)", socks_req[0], socks_req[1]); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | } else if (socks_req[1] != 0) { | ||
| 213 | g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req[1]); | ||
| 214 | return -1; | ||
| 215 | } | ||
| 216 | |||
| 217 | socks_req[0] = 5; | ||
| 218 | socks_req[1] = 1; | ||
| 219 | socks_req[2] = 0; | ||
| 220 | |||
| 221 | socks_req[3] = 3; | ||
| 222 | socks_req[4] = (guchar)len; | ||
| 223 | memcpy(socks_req + 5, hostname, len); | ||
| 224 | *((gushort *)(socks_req + 5 + len)) = htons(port); | ||
| 225 | |||
| 226 | if (sock_write_all(sock, (gchar *)socks_req, 5 + len + 2) != 5 + len + 2) { | ||
| 227 | g_warning("socks5_connect: SOCKS5 connect request write failed"); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (sock_read(sock, (gchar *)socks_req, 10) != 10) { | ||
| 232 | g_warning("socks5_connect: SOCKS5 connect request response read failed"); | ||
| 233 | return -1; | ||
| 234 | } | ||
| 235 | if (socks_req[0] != 5) { | ||
| 236 | g_warning("socks5_connect: SOCKS5 response has invalid version"); | ||
| 237 | return -1; | ||
| 238 | } | ||
| 239 | if (socks_req[1] != 0) { | ||
| 240 | 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]); | ||
| 241 | return -1; | ||
| 242 | } | ||
| 243 | |||
| 244 | size = 10; | ||
| 245 | if (socks_req[3] == 3) | ||
| 246 | size = 5 + socks_req[4] + 2; | ||
| 247 | else if (socks_req[3] == 4) | ||
| 248 | size = 4 + 16 + 2; | ||
| 249 | if (size > 10) { | ||
| 250 | size -= 10; | ||
| 251 | if (sock_read(sock, (gchar *)socks_req + 10, size) != size) { | ||
| 252 | g_warning("socks5_connect: SOCKS5 connect request response read failed"); | ||
| 253 | return -1; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | /* replace sock->hostname with endpoint */ | ||
| 258 | if (sock->hostname != hostname) { | ||
| 259 | g_free(sock->hostname); | ||
| 260 | sock->hostname = g_strdup(hostname); | ||
| 261 | sock->port = port; | ||
| 262 | } | ||
| 263 | |||
| 264 | debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname, port); | ||
| 265 | |||
| 266 | return 0; | ||
| 267 | } | ||
