1/* 2 SOCKS proxy support for neon 3 Copyright (C) 2008, Joe Orton <joe@manyfish.co.uk> 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 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 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the Free 17 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 18 MA 02111-1307, USA 19*/ 20 21#include "config.h" 22 23#include "ne_internal.h" 24#include "ne_string.h" 25#include "ne_socket.h" 26#include "ne_utils.h" 27 28#include <string.h> 29 30/* SOCKS protocol reference: 31 v4: http://www.ufasoft.com/doc/socks4_protocol.htm 32 v4a http://www.smartftp.com/Products/SmartFTP/RFC/socks4a.protocol 33 v5: http://tools.ietf.org/html/rfc1928 34 ...v5 auth: http://tools.ietf.org/html/rfc1929 35*/ 36 37#define V5_REPLY_OK 0 38#define V5_REPLY_FAIL 1 39#define V5_REPLY_DISALLOW 2 40#define V5_REPLY_NET_UNREACH 3 41#define V5_REPLY_HOST_UNREACH 4 42#define V5_REPLY_CONN_REFUSED 5 43#define V5_REPLY_TTL_EXPIRED 6 44#define V5_REPLY_CMD_UNSUPPORTED 7 45#define V5_REPLY_TYPE_UNSUPPORTED 8 46 47#define V5_VERSION 0x05 48#define V5_ADDR_IPV4 0x01 49#define V5_ADDR_FQDN 0x03 50#define V5_ADDR_IPV6 0x04 51 52#define V5_CMD_CONNECT 0x01 53 54#define V5_AUTH_NONE 0x00 55#define V5_AUTH_USER 0x02 56#define V5_AUTH_NOMETH 0xFF 57 58/* Fail with given V5 error code in given context. */ 59static int v5fail(ne_socket *sock, unsigned int code, const char *context) 60{ 61 const char *err; 62 63 switch (code) { 64 case V5_REPLY_FAIL: 65 err = _("failure"); 66 break; 67 case V5_REPLY_DISALLOW: 68 err = _("connection not permitted"); 69 break; 70 case V5_REPLY_NET_UNREACH: 71 err = _("network unreachable"); 72 break; 73 case V5_REPLY_HOST_UNREACH: 74 err = _("host unreachable"); 75 break; 76 case V5_REPLY_TTL_EXPIRED: 77 err = _("TTL expired"); 78 break; 79 case V5_REPLY_CMD_UNSUPPORTED: 80 err = _("command not supported"); 81 break; 82 case V5_REPLY_TYPE_UNSUPPORTED: 83 err = _("address type not supported"); 84 break; 85 default: 86 ne_sock_set_error(sock, _("%s: unrecognized error (%u)"), context, code); 87 return NE_SOCK_ERROR; 88 } 89 90 ne_sock_set_error(sock, "%s: %s", context, err); 91 return NE_SOCK_ERROR; 92} 93 94/* Fail with given error string. */ 95static int fail(ne_socket *sock, const char *error) 96{ 97 ne_sock_set_error(sock, "%s", error); 98 return NE_SOCK_ERROR; 99} 100 101/* Fail with given NE_SOCK_* error code and given context. */ 102static int sofail(ne_socket *sock, ssize_t ret, const char *context) 103{ 104 char *err = ne_strdup(ne_sock_error(sock)); 105 ne_sock_set_error(sock, "%s: %s", context, err); 106 ne_free(err); 107 return NE_SOCK_ERROR; 108} 109 110/* SOCKSv5 proxy. */ 111static int v5_proxy(ne_socket *sock, const ne_inet_addr *addr, 112 const char *hostname, unsigned int port, 113 const char *username, const char *password) 114{ 115 unsigned char msg[1024], *p; 116 unsigned int len; 117 int ret; 118 ssize_t n; 119 120 p = msg; 121 *p++ = V5_VERSION; 122 *p++ = 2; /* Two supported auth protocols; none and user. */ 123 *p++ = V5_AUTH_NONE; 124 *p++ = V5_AUTH_USER; 125 126 ret = ne_sock_fullwrite(sock, (char *)msg, p - msg); 127 if (ret) { 128 return sofail(sock, ret, _("Could not send message to proxy")); 129 } 130 131 n = ne_sock_fullread(sock, (char *)msg, 2); 132 if (n) { 133 return sofail(sock, ret, _("Could not read initial response from proxy")); 134 } 135 else if (msg[0] != V5_VERSION) { 136 return fail(sock, _("Invalid version in proxy response")); 137 } 138 139 /* Authenticate, if necessary. */ 140 switch (msg[1]) { 141 case V5_AUTH_NONE: 142 break; 143 case V5_AUTH_USER: 144 p = msg; 145 *p++ = 0x01; 146 len = strlen(username) & 0xff; 147 *p++ = len; 148 memcpy(p, username, len); 149 p += len; 150 len = strlen(password) & 0xff; 151 *p++ = len; 152 memcpy(p, password, len); 153 p += len; 154 155 ret = ne_sock_fullwrite(sock, (char *)msg, p - msg); 156 if (ret) { 157 return sofail(sock, ret, _("Could not send login message")); 158 } 159 160 n = ne_sock_fullread(sock, (char *)msg, 2); 161 if (n) { 162 return sofail(sock, ret, _("Could not read login reply")); 163 } 164 else if (msg[0] != 1) { 165 return fail(sock, _("Invalid version in login reply")); 166 } 167 else if (msg[1] != 0) { 168 return fail(sock, _("Authentication failed")); 169 } 170 break; 171 case V5_AUTH_NOMETH: 172 return fail(sock, _("No acceptable authentication method")); 173 default: 174 return fail(sock, _("Unexpected authentication method chosen")); 175 } 176 177 /* Send the CONNECT command. */ 178 p = msg; 179 *p++ = V5_VERSION; 180 *p++ = V5_CMD_CONNECT; 181 *p++ = 0; /* reserved */ 182 if (addr) { 183 unsigned char raw[16]; 184 185 if (ne_iaddr_typeof(addr) == ne_iaddr_ipv4) { 186 len = 4; 187 *p++ = V5_ADDR_IPV4; 188 } 189 else { 190 len = 16; 191 *p++ = V5_ADDR_IPV6; 192 } 193 194 memcpy(p, ne_iaddr_raw(addr, raw), len); 195 p += len; 196 } 197 else { 198 len = strlen(hostname) & 0xff; 199 *p++ = V5_ADDR_FQDN; 200 *p++ = len; 201 memcpy(p, hostname, len); 202 p += len; 203 } 204 205 *p++ = (port >> 8) & 0xff; 206 *p++ = port & 0xff; 207 208 ret = ne_sock_fullwrite(sock, (char *)msg, p - msg); 209 if (ret) { 210 return sofail(sock, ret, _("Could not send connect request")); 211 } 212 213 n = ne_sock_fullread(sock, (char *)msg, 4); 214 if (n) { 215 return sofail(sock, n, _("Could not read connect reply")); 216 } 217 if (msg[0] != V5_VERSION) { 218 return fail(sock, _("Invalid version in connect reply")); 219 } 220 if (msg[1] != V5_REPLY_OK) { 221 return v5fail(sock, msg[1], _("Could not connect")); 222 } 223 224 switch (msg[3]) { 225 case V5_ADDR_IPV4: 226 len = 4; 227 break; 228 case V5_ADDR_IPV6: 229 len = 16; 230 break; 231 case V5_ADDR_FQDN: 232 n = ne_sock_read(sock, (char *)msg, 1); 233 if (n != 1) { 234 return sofail(sock, n, 235 _("Could not read FQDN length in connect reply")); 236 } 237 len = msg[0]; 238 break; 239 default: 240 return fail(sock, _("Unknown address type in connect reply")); 241 } 242 243 n = ne_sock_fullread(sock, (char *)msg, len + 2); 244 if (n) { 245 return sofail(sock, n, _("Could not read address in connect reply")); 246 } 247 248 return 0; 249} 250 251#define V4_VERSION 0x04 252#define V4_CMD_STREAM 0x01 253 254#define V4_REP_OK 0x5a /* request granted */ 255#define V4_REP_FAIL 0x5b /* request rejected or failed */ 256#define V4_REP_NOIDENT 0x5c /* request failed, could connect to identd */ 257#define V4_REP_IDFAIL 0x5d /* request failed, identd denial */ 258 259/* Fail for given SOCKSv4 error code. */ 260static int v4fail(ne_socket *sock, unsigned int code, const char *context) 261{ 262 const char *err; 263 264 switch (code) { 265 case V4_REP_FAIL: 266 err = _("request rejected or failed"); 267 break; 268 case V4_REP_NOIDENT: 269 err = _("could not establish connection to identd"); 270 break; 271 case V4_REP_IDFAIL: 272 err = _("rejected due to identd user mismatch"); 273 break; 274 default: 275 ne_sock_set_error(sock, _("%s: unrecognized failure (%u)"), 276 context, code); 277 return NE_SOCK_ERROR; 278 } 279 280 ne_sock_set_error(sock, "%s: %s", context, err); 281 return NE_SOCK_ERROR; 282} 283 284/* SOCKS v4 or v4A proxy. */ 285static int v4_proxy(ne_socket *sock, enum ne_sock_sversion vers, 286 const ne_inet_addr *addr, const char *hostname, 287 unsigned int port, const char *username) 288{ 289 unsigned char msg[1024], raw[16], *p; 290 ssize_t n; 291 int ret; 292 293 p = msg; 294 *p++ = V4_VERSION; 295 *p++ = V4_CMD_STREAM; 296 *p++ = (port >> 8) & 0xff; 297 *p++ = port & 0xff; 298 299 if (vers == NE_SOCK_SOCKSV4A) { 300 /* A bogus address is used to signify use of the hostname, 301 * 0.0.0.X where X != 0. */ 302 memcpy(p, "\x00\x00\x00\xff", 4); 303 } 304 else { 305 /* API precondition that addr is IPv4; if it's not this will 306 * just copy out the first four bytes of the v6 address; 307 * garbage in => garbage out. */ 308 memcpy(p, ne_iaddr_raw(addr, raw), 4); 309 } 310 p += 4; 311 312 if (username) { 313 unsigned int len = strlen(username) & 0xff; 314 memcpy(p, username, len); 315 p += len; 316 } 317 *p++ = '\0'; 318 319 if (vers == NE_SOCK_SOCKSV4A) { 320 unsigned int len = strlen(hostname) & 0xff; 321 memcpy(p, hostname, len); 322 p += len; 323 *p++ = '\0'; 324 } 325 326 ret = ne_sock_fullwrite(sock, (char *)msg, p - msg); 327 if (ret) { 328 return sofail(sock, ret, _("Could not send message to proxy")); 329 } 330 331 n = ne_sock_fullread(sock, (char *)msg, 8); 332 if (n) { 333 return sofail(sock, ret, _("Could not read response from proxy")); 334 } 335 336 if (msg[1] != V4_REP_OK) { 337 return v4fail(sock, ret, _("Could not connect")); 338 } 339 340 return 0; 341} 342 343int ne_sock_proxy(ne_socket *sock, enum ne_sock_sversion vers, 344 const ne_inet_addr *addr, const char *hostname, 345 unsigned int port, 346 const char *username, const char *password) 347{ 348 if (vers == NE_SOCK_SOCKSV5) { 349 return v5_proxy(sock, addr, hostname, port, username, password); 350 } 351 else { 352 return v4_proxy(sock, vers, addr, hostname, port, username); 353 } 354} 355