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
)
154 smtp_transport_options_block
* ob
=
155 (smtp_transport_options_block
*)tb
->options_block
;
156 const uschar
* proxy_list
;
157 const uschar
* proxy_spec
;
161 const uschar
* state
;
164 if (!timeout
) timeout
= 24*60*60; /* use 1 day for "indefinite" */
165 tmo
= time(NULL
) + timeout
;
167 if (!(proxy_list
= expand_string(ob
->socks_proxy
)))
169 log_write(0, LOG_MAIN
|LOG_PANIC
, "Bad expansion for socks_proxy in %s",
174 /* Loop over proxy list, trying in order until one works */
175 while ((proxy_spec
= string_nextinlist(&proxy_list
, &sep
, NULL
, 0)))
177 const uschar
* proxy_host
;
181 union sockaddr_46 sin
;
184 const uschar
* option
;
186 if (!(proxy_host
= string_nextinlist(&proxy_spec
, &subsep
, NULL
, 0)))
188 /* paniclog config error */
192 /*XXX consider global options eg. "hide socks_password = wibble" on the tpt */
193 socks_option_defaults(&sob
);
195 /* extract any further per-proxy options */
196 while ((option
= string_nextinlist(&proxy_spec
, &subsep
, NULL
, 0)))
197 socks_option(&sob
, option
);
199 /* bodge up a host struct for the proxy */
200 proxy
.address
= proxy_host
;
201 proxy_af
= Ustrchr(proxy_host
, ':') ? AF_INET6
: AF_INET
;
203 if ((fd
= smtp_sock_connect(&proxy
, proxy_af
, sob
.port
,
204 interface
, tb
, sob
.timeout
)) < 0)
207 /* Do the socks protocol stuff */
208 /* Send method-selection */
209 state
= US
"method select";
210 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" SOCKS>> 05 01 %02x\n", sob
.auth_type
);
211 buf
[0] = 5; buf
[1] = 1; buf
[2] = sob
.auth_type
;
212 if (send(fd
, buf
, 3, 0) < 0)
215 /* expect method response */
216 if ( !fd_ready(fd
, tmo
-time(NULL
))
217 || read(fd
, buf
, 2) != 2
220 HDEBUG(D_transport
|D_acl
|D_v
)
221 debug_printf(" SOCKS<< %02x %02x\n", buf
[0], buf
[1]);
223 || socks_auth(fd
, buf
[1], &sob
, tmo
) != OK
227 (void) ip_addr(&sin
, host_af
, host
->address
, port
);
229 /* send connect (ipver, ipaddr, port) */
230 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
);
242 memcpy(buf
+4, &sin
.v4
.sin_addr
.s_addr
, sizeof(sin
.v4
.sin_addr
.s_addr
));
243 memcpy(buf
+4+sizeof(sin
.v4
.sin_addr
.s_addr
),
244 &sin
.v4
.sin_port
, sizeof(sin
.v4
.sin_port
));
245 size
= 4+sizeof(sin
.v4
.sin_addr
.s_addr
)+sizeof(sin
.v4
.sin_port
);
249 HDEBUG(D_transport
|D_acl
|D_v
)
252 debug_printf(" SOCKS>>");
253 for (i
= 0; i
<size
; i
++) debug_printf(" %02x", buf
[i
]);
256 if (send(fd
, buf
, size
, 0) < 0)
259 /* expect conn-reply (success, local(ipver, addr, port))
260 of same length as conn-request, or non-success fail code */
261 if ( !fd_ready(fd
, tmo
-time(NULL
))
262 || (size
= read(fd
, buf
, size
)) < 2
265 HDEBUG(D_transport
|D_acl
|D_v
)
268 debug_printf(" SOCKS>>");
269 for (i
= 0; i
<size
; i
++) debug_printf(" %02x", buf
[i
]);
277 /*XXX log proxy outbound addr/port? */
278 HDEBUG(D_transport
|D_acl
|D_v
)
279 debug_printf(" proxy farside local: [%s]:%d\n",
280 host_ntoa(buf
[3] == 4 ? AF_INET6
: AF_INET
, buf
+4, NULL
, NULL
),
281 ntohs(*((uint16_t *)(buf
+ (buf
[3] == 4 ? 20 : 8)))));
286 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" no proxies left\n");
290 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" proxy snd_err %s: %s\n", state
, strerror(errno
));
295 struct socks_err
* se
=
296 buf
[1] > nelem(socks_errs
) ? NULL
: socks_errs
+ buf
[1];
297 HDEBUG(D_transport
|D_acl
|D_v
)
298 debug_printf(" proxy %s: %s\n", state
, se
? se
->reason
: US
"unknown error code received");
299 errno
= se
? se
->errcode
: EPROTO
;
303 HDEBUG(D_transport
|D_acl
|D_v
) debug_printf(" proxy rcv_err %s: %s\n", state
, strerror(errno
));
304 if (!errno
) errno
= EPROTO
;
305 else if (errno
== ENOENT
) errno
= ECONNABORTED
;
309 #endif /* entire file */