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;
232 if (host_af
== AF_INET6
)
234 memcpy(buf
+4, &sin
.v6
.sin6_addr
, sizeof(sin
.v6
.sin6_addr
));
235 memcpy(buf
+4+sizeof(sin
.v6
.sin6_addr
),
236 &sin
.v6
.sin6_port
, sizeof(sin
.v6
.sin6_port
));
237 size
= 4+sizeof(sin
.v6
.sin6_addr
)+sizeof(sin
.v6
.sin6_port
);
241 memcpy(buf
+4, &sin
.v4
.sin_addr
.s_addr
, sizeof(sin
.v4
.sin_addr
.s_addr
));
242 memcpy(buf
+4+sizeof(sin
.v4
.sin_addr
.s_addr
),
243 &sin
.v4
.sin_port
, sizeof(sin
.v4
.sin_port
));
244 size
= 4+sizeof(sin
.v4
.sin_addr
.s_addr
)+sizeof(sin
.v4
.sin_port
);
248 HDEBUG(D_transport
|D_acl
|D_v
)
251 debug_printf(" SOCKS>>");
252 for (i
= 0; i
<size
; i
++) debug_printf(" %02x", buf
[i
]);
255 if (send(fd
, buf
, size
, 0) < 0)
258 /* expect conn-reply (success, local(ipver, addr, port))
259 of same length as conn-request, or non-success fail code */
260 if ( !fd_ready(fd
, tmo
-time(NULL
))
261 || (size
= read(fd
, buf
, size
)) < 2
264 HDEBUG(D_transport
|D_acl
|D_v
)
267 debug_printf(" SOCKS>>");
268 for (i
= 0; i
<size
; i
++) debug_printf(" %02x", buf
[i
]);
276 /*XXX log proxy outbound addr/port? */
277 HDEBUG(D_transport
|D_acl
|D_v
)
278 debug_printf(" proxy farside local: [%s]:%d\n",
279 host_ntoa(buf
[3] == 4 ? AF_INET6
: AF_INET
, buf
+4, NULL
, NULL
),
280 ntohs(*((uint16_t *)(buf
+ (buf
[3] == 4 ? 20 : 8)))));
285 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" no proxies left\n");
289 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" proxy snd_err %s: %s\n", state
, strerror(errno
));
294 struct socks_err
* se
=
295 buf
[1] > nelem(socks_errs
) ? NULL
: socks_errs
+ buf
[1];
296 HDEBUG(D_transport
|D_acl
|D_v
)
297 debug_printf(" proxy %s: %s\n", state
, se
? se
->reason
: US
"unknown error code received");
298 errno
= se
? se
->errcode
: EPROTO
;
302 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" proxy rcv_err %s: %s\n", state
, strerror(errno
));
303 if (!errno
) errno
= EPROTO
;
304 else if (errno
== ENOENT
) errno
= ECONNABORTED
;
308 #endif /* entire file */