1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Jeremy Harris 2015 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* SOCKS version 5 proxy, client-mode */
13 #ifdef EXPERIMENTAL_SOCKS /* entire file */
16 # define nelem(arr) (sizeof(arr)/sizeof(*arr))
21 #define SOCKS_PORT 1080
22 #define SOCKS_TIMEOUT 5
25 #define AUTH_NAME 2 /* user/password per RFC 1929 */
26 #define AUTH_NAME_VER 1
35 {US"general SOCKS server failure", EIO},
36 {US"connection not allowed by ruleset", EACCES},
37 {US"Network unreachable", ENETUNREACH},
38 {US"Host unreachable", EHOSTUNREACH},
39 {US"Connection refused", ECONNREFUSED},
40 {US"TTL expired", ECANCELED},
41 {US"Command not supported", EOPNOTSUPP},
42 {US"Address type not supported", EAFNOSUPPORT}
47 uschar auth_type; /* RFC 1928 encoding */
48 const uschar * auth_name;
49 const uschar * auth_pwd;
55 socks_option_defaults(socks_opts * sob)
57 sob->auth_type = AUTH_NONE;
58 sob->auth_name = US"";
60 sob->port = SOCKS_PORT;
61 sob->timeout = SOCKS_TIMEOUT;
65 socks_option(socks_opts * sob, const uschar * opt)
69 if (Ustrncmp(opt, "auth=", 5) == 0)
72 if (Ustrcmp(opt, "none") == 0) sob->auth_type = AUTH_NONE;
73 else if (Ustrcmp(opt, "name") == 0) sob->auth_type = AUTH_NAME;
75 else if (Ustrncmp(opt, "name=", 5) == 0)
76 sob->auth_name = opt + 5;
77 else if (Ustrncmp(opt, "pass=", 5) == 0)
78 sob->auth_pwd = opt + 5;
79 else if (Ustrncmp(opt, "port=", 5) == 0)
80 sob->port = atoi(opt + 5);
81 else if (Ustrncmp(opt, "tmo=", 4) == 0)
82 sob->timeout = atoi(opt + 4);
87 socks_auth(int fd, int method, socks_opts * sob, time_t tmo)
95 log_write(0, LOG_MAIN|LOG_PANIC,
96 "Unrecognised socks auth method %d", method);
101 HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth NAME '%s' '%s'\n",
102 sob->auth_name, sob->auth_pwd);
103 i = Ustrlen(sob->auth_name);
104 j = Ustrlen(sob->auth_pwd);
105 s = string_sprintf("%c%c%.255s%c%.255s", AUTH_NAME_VER,
106 i, sob->auth_name, j, sob->auth_pwd);
108 HDEBUG(D_transport|D_acl|D_v)
111 debug_printf(" SOCKS>>");
112 for (i = 0; i<len; i++) debug_printf(" %02x", s[i]);
115 if ( send(fd, s, len, 0) < 0
116 || !fd_ready(fd, tmo-time(NULL))
117 || read(fd, s, 2) != 2
120 HDEBUG(D_transport|D_acl|D_v)
121 debug_printf(" SOCKS<< %02x %02x\n", s[0], s[1]);
122 if (s[0] == AUTH_NAME_VER && s[1] == 0)
124 HDEBUG(D_transport|D_acl|D_v) debug_printf(" socks auth OK\n");
128 log_write(0, LOG_MAIN|LOG_PANIC, "socks auth failed");
136 /* Make a connection via a socks proxy
139 host smtp target host
140 host_af address family
141 port remote tcp port number
142 interface local interface
144 timeout connection timeout (zero for indefinite)
147 0 on success; -1 on failure, with errno set
151 socks_sock_connect(host_item * host, int host_af, int port, uschar * interface,
152 transport_instance * tb, int timeout)
155 smtp_transport_options_block * ob =
156 (smtp_transport_options_block *)tb->options_block;
157 const uschar * proxy_list;
158 const uschar * proxy_spec;
162 const uschar * state;
165 if (!timeout) timeout = 24*60*60; /* use 1 day for "indefinite" */
166 tmo = time(NULL) + timeout;
168 if (!(proxy_list = expand_string(ob->socks_proxy)))
170 log_write(0, LOG_MAIN|LOG_PANIC, "Bad expansion for socks_proxy in %s",
175 /* Loop over proxy list, trying in order until one works */
176 while ((proxy_spec = string_nextinlist(&proxy_list, &sep, NULL, 0)))
178 const uschar * proxy_host;
182 union sockaddr_46 sin;
185 const uschar * option;
187 if (!(proxy_host = string_nextinlist(&proxy_spec, &subsep, NULL, 0)))
189 /* paniclog config error */
193 /*XXX consider global options eg. "hide socks_password = wibble" on the tpt */
194 socks_option_defaults(&sob);
196 /* extract any further per-proxy options */
197 while ((option = string_nextinlist(&proxy_spec, &subsep, NULL, 0)))
198 socks_option(&sob, option);
200 /* bodge up a host struct for the proxy */
201 proxy.address = proxy_host;
202 proxy_af = Ustrchr(proxy_host, ':') ? AF_INET6 : AF_INET;
204 if ((fd = smtp_sock_connect(&proxy, proxy_af, sob.port,
205 interface, tb, sob.timeout)) < 0)
208 /* Do the socks protocol stuff */
209 /* Send method-selection */
210 state = US"method select";
211 HDEBUG(D_transport|D_acl|D_v) debug_printf(" SOCKS>> 05 01 %02x\n", sob.auth_type);
212 buf[0] = 5; buf[1] = 1; buf[2] = sob.auth_type;
213 if (send(fd, buf, 3, 0) < 0)
216 /* expect method response */
217 if ( !fd_ready(fd, tmo-time(NULL))
218 || read(fd, buf, 2) != 2
221 HDEBUG(D_transport|D_acl|D_v)
222 debug_printf(" SOCKS<< %02x %02x\n", buf[0], buf[1]);
224 || socks_auth(fd, buf[1], &sob, tmo) != OK
228 (void) ip_addr(&sin, host_af, host->address, port);
230 /* send connect (ipver, ipaddr, port) */
231 buf[0] = 5; buf[1] = 1; buf[2] = 0; buf[3] = host_af == AF_INET6 ? 4 : 1;
233 if (host_af == AF_INET6)
235 memcpy(buf+4, &sin.v6.sin6_addr, sizeof(sin.v6.sin6_addr));
236 memcpy(buf+4+sizeof(sin.v6.sin6_addr),
237 &sin.v6.sin6_port, sizeof(sin.v6.sin6_port));
238 size = 4+sizeof(sin.v6.sin6_addr)+sizeof(sin.v6.sin6_port);
243 memcpy(buf+4, &sin.v4.sin_addr.s_addr, sizeof(sin.v4.sin_addr.s_addr));
244 memcpy(buf+4+sizeof(sin.v4.sin_addr.s_addr),
245 &sin.v4.sin_port, sizeof(sin.v4.sin_port));
246 size = 4+sizeof(sin.v4.sin_addr.s_addr)+sizeof(sin.v4.sin_port);
250 HDEBUG(D_transport|D_acl|D_v)
253 debug_printf(" SOCKS>>");
254 for (i = 0; i<size; i++) debug_printf(" %02x", buf[i]);
257 if (send(fd, buf, size, 0) < 0)
260 /* expect conn-reply (success, local(ipver, addr, port))
261 of same length as conn-request, or non-success fail code */
262 if ( !fd_ready(fd, tmo-time(NULL))
263 || (size = read(fd, buf, size)) < 2
266 HDEBUG(D_transport|D_acl|D_v)
269 debug_printf(" SOCKS>>");
270 for (i = 0; i<size; i++) debug_printf(" %02x", buf[i]);
278 /*XXX log proxy outbound addr/port? */
279 HDEBUG(D_transport|D_acl|D_v)
280 debug_printf(" proxy farside local: [%s]:%d\n",
281 host_ntoa(buf[3] == 4 ? AF_INET6 : AF_INET, buf+4, NULL, NULL),
282 ntohs(*((uint16_t *)(buf + (buf[3] == 4 ? 20 : 8)))));
287 HDEBUG(D_transport|D_acl|D_v) debug_printf(" no proxies left\n");
291 HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy snd_err %s: %s\n", state, strerror(errno));
296 struct socks_err * se =
297 buf[1] > nelem(socks_errs) ? NULL : socks_errs + buf[1];
298 HDEBUG(D_transport|D_acl|D_v)
299 debug_printf(" proxy %s: %s\n", state, se ? se->reason : US"unknown error code received");
300 errno = se ? se->errcode : EPROTO;
304 HDEBUG(D_transport|D_acl|D_v) debug_printf(" proxy rcv_err %s: %s\n", state, strerror(errno));
305 if (!errno) errno = EPROTO;
306 else if (errno == ENOENT) errno = ECONNABORTED;
310 #endif /* entire file */