1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) Jeremy Harris 2015 - 2017 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* SOCKS version 5 proxy, client-mode */
13 #ifdef SUPPORT_SOCKS /* entire file */
16 # define nelem(arr) (sizeof(arr)/sizeof(*arr))
21 #define SOCKS_PORT 1080
22 #define SOCKS_TIMEOUT 5
23 #define SOCKS_WEIGHT 1
24 #define SOCKS_PRIORITY 1
27 #define AUTH_NAME 2 /* user/password per RFC 1929 */
28 #define AUTH_NAME_VER 1
37 {US
"general SOCKS server failure", EIO
},
38 {US
"connection not allowed by ruleset", EACCES
},
39 {US
"Network unreachable", ENETUNREACH
},
40 {US
"Host unreachable", EHOSTUNREACH
},
41 {US
"Connection refused", ECONNREFUSED
},
42 {US
"TTL expired", ECANCELED
},
43 {US
"Command not supported", EOPNOTSUPP
},
44 {US
"Address type not supported", EAFNOSUPPORT
}
49 const uschar
* proxy_host
;
50 uschar auth_type
; /* RFC 1928 encoding */
51 const uschar
* auth_name
;
52 const uschar
* auth_pwd
;
61 socks_option_defaults(socks_opts
* sob
)
63 sob
->proxy_host
= NULL
;
64 sob
->auth_type
= AUTH_NONE
;
65 sob
->auth_name
= US
"";
67 sob
->is_failed
= FALSE
;
68 sob
->port
= SOCKS_PORT
;
69 sob
->timeout
= SOCKS_TIMEOUT
;
70 sob
->weight
= SOCKS_WEIGHT
;
71 sob
->priority
= SOCKS_PRIORITY
;
75 socks_option(socks_opts
* sob
, const uschar
* opt
)
77 if (Ustrncmp(opt
, "auth=", 5) == 0)
80 if (Ustrcmp(opt
, "none") == 0) sob
->auth_type
= AUTH_NONE
;
81 else if (Ustrcmp(opt
, "name") == 0) sob
->auth_type
= AUTH_NAME
;
83 else if (Ustrncmp(opt
, "name=", 5) == 0)
84 sob
->auth_name
= opt
+ 5;
85 else if (Ustrncmp(opt
, "pass=", 5) == 0)
86 sob
->auth_pwd
= opt
+ 5;
87 else if (Ustrncmp(opt
, "port=", 5) == 0)
88 sob
->port
= atoi(opt
+ 5);
89 else if (Ustrncmp(opt
, "tmo=", 4) == 0)
90 sob
->timeout
= atoi(opt
+ 4);
91 else if (Ustrncmp(opt
, "pri=", 4) == 0)
92 sob
->priority
= atoi(opt
+ 4);
93 else if (Ustrncmp(opt
, "weight=", 7) == 0)
94 sob
->weight
= atoi(opt
+ 7);
99 socks_auth(int fd
, int method
, socks_opts
* sob
, time_t tmo
)
107 log_write(0, LOG_MAIN
|LOG_PANIC
,
108 "Unrecognised socks auth method %d", method
);
113 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf_indent(" socks auth NAME '%s' '%s'\n",
114 sob
->auth_name
, sob
->auth_pwd
);
115 i
= Ustrlen(sob
->auth_name
);
116 j
= Ustrlen(sob
->auth_pwd
);
117 s
= string_sprintf("%c%c%.255s%c%.255s", AUTH_NAME_VER
,
118 i
, sob
->auth_name
, j
, sob
->auth_pwd
);
120 HDEBUG(D_transport
|D_acl
|D_v
)
123 debug_printf_indent(" SOCKS>>");
124 for (i
= 0; i
<len
; i
++) debug_printf(" %02x", s
[i
]);
127 if (send(fd
, s
, len
, 0) < 0)
130 (void) setsockopt(fd
, IPPROTO_TCP
, TCP_QUICKACK
, US
&off
, sizeof(off
));
132 if (!fd_ready(fd
, tmo
-time(NULL
)) || read(fd
, s
, 2) != 2)
134 HDEBUG(D_transport
|D_acl
|D_v
)
135 debug_printf_indent(" SOCKS<< %02x %02x\n", s
[0], s
[1]);
136 if (s
[0] == AUTH_NAME_VER
&& s
[1] == 0)
138 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf_indent(" socks auth OK\n");
142 log_write(0, LOG_MAIN
|LOG_PANIC
, "socks auth failed");
150 /* Find a suitable proxy to use from the list.
151 Possible common code with spamd_get_server() ?
153 Return: index into proxy spec array, or -1
157 socks_get_proxy(socks_opts
* proxies
, unsigned nproxies
)
161 socks_opts
* lim
= &proxies
[nproxies
];
164 static BOOL srandomed
= FALSE
;
166 if (nproxies
== 1) /* shortcut, if we have only 1 server */
167 return (proxies
[0].is_failed
? -1 : 0);
173 gettimeofday(&tv
, NULL
);
174 srandom((unsigned int)(tv
.tv_usec
/1000));
178 /* scan for highest pri */
179 for (pri
= 0, sd
= proxies
; sd
< lim
; sd
++)
180 if (!sd
->is_failed
&& sd
->priority
> pri
)
183 /* get sum of weights at this pri */
184 for (weights
= 0, sd
= proxies
; sd
< lim
; sd
++)
185 if (!sd
->is_failed
&& sd
->priority
== pri
)
186 weights
+= sd
->weight
;
187 if (weights
== 0) /* all servers failed */
190 for (rnd
= random() % weights
, i
= 0; i
< nproxies
; i
++)
193 if (!sd
->is_failed
&& sd
->priority
== pri
)
194 if ((rnd
-= sd
->weight
) <= 0)
198 log_write(0, LOG_MAIN
|LOG_PANIC
,
199 "%s unknown error (memory/cpu corruption?)", __FUNCTION__
);
205 /* Make a connection via a socks proxy
208 host smtp target host
209 host_af address family
210 port remote tcp port number
211 interface local interface
213 timeout connection timeout (zero for indefinite)
216 0 on success; -1 on failure, with errno set
220 socks_sock_connect(host_item
* host
, int host_af
, int port
, uschar
* interface
,
221 transport_instance
* tb
, int timeout
)
223 smtp_transport_options_block
* ob
=
224 (smtp_transport_options_block
*)tb
->options_block
;
225 const uschar
* proxy_list
;
226 const uschar
* proxy_spec
;
230 const uschar
* state
;
232 socks_opts proxies
[32]; /* max #proxies handled */
238 if (!timeout
) timeout
= 24*60*60; /* use 1 day for "indefinite" */
239 tmo
= time(NULL
) + timeout
;
241 if (!(proxy_list
= expand_string(ob
->socks_proxy
)))
243 log_write(0, LOG_MAIN
|LOG_PANIC
, "Bad expansion for socks_proxy in %s",
248 /* Read proxy list */
251 nproxies
< nelem(proxies
)
252 && (proxy_spec
= string_nextinlist(&proxy_list
, &sep
, NULL
, 0));
256 const uschar
* option
;
258 socks_option_defaults(sob
= &proxies
[nproxies
]);
260 if (!(sob
->proxy_host
= string_nextinlist(&proxy_spec
, &subsep
, NULL
, 0)))
262 /* paniclog config error */
266 /*XXX consider global options eg. "hide socks_password = wibble" on the tpt */
267 /* extract any further per-proxy options */
268 while ((option
= string_nextinlist(&proxy_spec
, &subsep
, NULL
, 0)))
269 socks_option(sob
, option
);
272 /* Set up the socks protocol method-selection message,
273 for sending on connection */
275 state
= US
"method select";
276 buf
[0] = 5; buf
[1] = 1; buf
[2] = sob
->auth_type
;
277 early_data
.data
= buf
;
280 /* Try proxies until a connection succeeds */
288 if ((idx
= socks_get_proxy(proxies
, nproxies
)) < 0)
290 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf_indent(" no proxies left\n");
296 /* bodge up a host struct for the proxy */
297 proxy
.address
= proxy
.name
= sob
->proxy_host
;
298 proxy_af
= Ustrchr(sob
->proxy_host
, ':') ? AF_INET6
: AF_INET
;
300 if ((fd
= smtp_sock_connect(&proxy
, proxy_af
, sob
->port
,
301 interface
, tb
, sob
->timeout
, &early_data
)) >= 0)
303 proxy_local_address
= string_copy(proxy
.address
);
304 proxy_local_port
= sob
->port
;
308 log_write(0, LOG_MAIN
, "%s: %s", __FUNCTION__
, strerror(errno
));
309 sob
->is_failed
= TRUE
;
312 /* Do the socks protocol stuff */
314 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf_indent(" SOCKS>> 05 01 %02x\n", sob
->auth_type
);
316 /* expect method response */
319 (void) setsockopt(fd
, IPPROTO_TCP
, TCP_QUICKACK
, US
&off
, sizeof(off
));
322 if ( !fd_ready(fd
, tmo
-time(NULL
))
323 || read(fd
, buf
, 2) != 2
326 HDEBUG(D_transport
|D_acl
|D_v
)
327 debug_printf_indent(" SOCKS<< %02x %02x\n", buf
[0], buf
[1]);
329 || socks_auth(fd
, buf
[1], sob
, tmo
) != OK
334 union sockaddr_46 sin
;
335 (void) ip_addr(&sin
, host_af
, host
->address
, port
);
337 /* send connect (ipver, ipaddr, port) */
339 buf
[0] = 5; buf
[1] = 1; buf
[2] = 0; buf
[3] = host_af
== AF_INET6
? 4 : 1;
341 if (host_af
== AF_INET6
)
343 memcpy(buf
+4, &sin
.v6
.sin6_addr
, sizeof(sin
.v6
.sin6_addr
));
344 memcpy(buf
+4+sizeof(sin
.v6
.sin6_addr
),
345 &sin
.v6
.sin6_port
, sizeof(sin
.v6
.sin6_port
));
346 size
= 4+sizeof(sin
.v6
.sin6_addr
)+sizeof(sin
.v6
.sin6_port
);
351 memcpy(buf
+4, &sin
.v4
.sin_addr
.s_addr
, sizeof(sin
.v4
.sin_addr
.s_addr
));
352 memcpy(buf
+4+sizeof(sin
.v4
.sin_addr
.s_addr
),
353 &sin
.v4
.sin_port
, sizeof(sin
.v4
.sin_port
));
354 size
= 4+sizeof(sin
.v4
.sin_addr
.s_addr
)+sizeof(sin
.v4
.sin_port
);
359 HDEBUG(D_transport
|D_acl
|D_v
)
362 debug_printf_indent(" SOCKS>>");
363 for (i
= 0; i
<size
; i
++) debug_printf(" %02x", buf
[i
]);
366 if (send(fd
, buf
, size
, 0) < 0)
369 /* expect conn-reply (success, local(ipver, addr, port))
370 of same length as conn-request, or non-success fail code */
372 if ( !fd_ready(fd
, tmo
-time(NULL
))
373 || (size
= read(fd
, buf
, size
)) < 2
376 HDEBUG(D_transport
|D_acl
|D_v
)
379 debug_printf_indent(" SOCKS>>");
380 for (i
= 0; i
<size
; i
++) debug_printf(" %02x", buf
[i
]);
388 proxy_external_address
= string_copy(
389 host_ntoa(buf
[3] == 4 ? AF_INET6
: AF_INET
, buf
+4, NULL
, NULL
));
390 proxy_external_port
= ntohs(*((uint16_t *)(buf
+ (buf
[3] == 4 ? 20 : 8))));
391 proxy_session
= TRUE
;
393 HDEBUG(D_transport
|D_acl
|D_v
)
394 debug_printf_indent(" proxy farside: [%s]:%d\n", proxy_external_address
, proxy_external_port
);
399 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf_indent(" proxy snd_err %s: %s\n", state
, strerror(errno
));
404 struct socks_err
* se
=
405 buf
[1] > nelem(socks_errs
) ? NULL
: socks_errs
+ buf
[1];
406 HDEBUG(D_transport
|D_acl
|D_v
)
407 debug_printf_indent(" proxy %s: %s\n", state
, se
? se
->reason
: US
"unknown error code received");
408 errno
= se
? se
->errcode
: EPROTO
;
412 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf_indent(" proxy rcv_err %s: %s\n", state
, strerror(errno
));
413 if (!errno
) errno
= EPROTO
;
414 else if (errno
== ENOENT
) errno
= ECONNABORTED
;
418 #endif /* entire file */