Avoid recursion when processing residual inbuf data.
[shim.git] / conn.c
blob64729d153caf2354f08514a353dd33cd0b1ea090
1 #include "netheaders.h"
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include <event2/event.h>
8 #include <event2/bufferevent.h>
9 #include <event2/buffer.h>
10 #include <event2/util.h>
11 #include <event2/dns.h>
13 #include "config.h"
14 #include "conn.h"
15 #include "util.h"
16 #include "log.h"
18 struct conninfo {
19 enum socks_ver socks;
20 struct bufferevent *bev;
21 void *cbarg;
22 int connecting;
23 conn_connectcb on_connect;
24 /* for socks... */
25 char *host;
26 int port;
27 struct sockaddr_storage addr;
28 int addr_len;
31 static enum socks_ver use_socks = SOCKS_NONE;
32 static struct sockaddr_storage socks_addr;
33 static int socks_addr_len = sizeof(socks_addr);
34 static char *conn_error_string = NULL;
36 static void
37 finish_connection(struct conninfo *info, int ok, const char *reason)
39 mem_free(conn_error_string);
40 conn_error_string = NULL;
41 if (!ok)
42 conn_error_string = mem_strdup(reason);
43 bufferevent_disable(info->bev, EV_READ);
44 bufferevent_setcb(info->bev, NULL, NULL, NULL, NULL);
45 info->on_connect(info->bev, ok, info->cbarg);
46 mem_free(info->host);
47 mem_free(info);
50 static void
51 write_socks_request(struct conninfo *info)
53 if (info->socks == SOCKS_4) {
54 struct sockaddr_in *sin = (struct sockaddr_in*)&info->addr;
56 if (info->addr.ss_family != AF_INET) {
57 finish_connection(info, 0,
58 "SOCKS 4 can't handle ipv6!");
61 /* connection request */
62 bufferevent_write(info->bev, "\x04\x01", 2);
63 bufferevent_write(info->bev, &sin->sin_port,
64 sizeof(sin->sin_port));
65 bufferevent_write(info->bev, &sin->sin_addr.s_addr,
66 sizeof(sin->sin_addr.s_addr));
67 bufferevent_write(info->bev, "xx", 3);
68 } else {
69 ev_uint16_t port = htons(info->port);
71 assert(info->host != NULL);
72 bufferevent_write(info->bev, "\x04\x01", 2);
73 bufferevent_write(info->bev, &port, sizeof(port));
74 bufferevent_write(info->bev, "\x00\x00\x00\xff", 4);
75 bufferevent_write(info->bev, "xx", 3);
76 bufferevent_write(info->bev, info->host, strlen(info->host)+1);
79 bufferevent_enable(info->bev, EV_READ);
82 static void
83 conn_errorcb(struct bufferevent *bev, short what, void *arg)
85 struct conninfo *info = arg;
87 if (info->connecting) {
88 info->connecting = 0;
89 if (what & BEV_EVENT_CONNECTED) {
90 if (info->socks != SOCKS_NONE)
91 write_socks_request(info);
92 else
93 finish_connection(info, 1, NULL);
94 } else {
95 // XXX need better err msg
96 const char *msg = "Connection failed";
97 if (info->socks != SOCKS_NONE)
98 msg = "Connection to proxy server failed";
99 finish_connection(info, 0, msg);
101 } else {
102 finish_connection(info, 0, "SOCKS I/O error");
106 static const char *
107 socks4_error_to_string(unsigned char err)
109 switch (err) {
110 case 0x5b:
111 return "SOCKS 4: request failed";
112 case 0x5c:
113 return "SOCKS 4: client is not running identd";
114 case 0x5d:
115 return "SOCKS 4: invalid user ID";
118 return "SOCKS 4: unknown error";
121 static void
122 conn_readcb(struct bufferevent *bev, void *arg)
124 struct conninfo *info = arg;
125 struct evbuffer *inbuf = bufferevent_get_input(bev);
126 unsigned char *data;
127 unsigned char code;
129 /* socks4 and socks4a both have an 8 byte response */
130 if (evbuffer_get_length(inbuf) < 8) {
131 log_debug("conn: waiting for full socks response");
132 return;
135 data = evbuffer_pullup(inbuf, 8);
136 code = data[1];
137 evbuffer_drain(inbuf, 8);
139 if (code != 0x5a) {
140 finish_connection(info, 0, socks4_error_to_string(code));
141 } else {
142 finish_connection(info, 1, NULL);
146 void socks_resolvecb(int result, struct evutil_addrinfo *ai, void *arg)
148 struct conninfo *info = arg;
150 if (result) {
151 char buf[256];
152 evutil_snprintf(buf, sizeof(buf), "DNS Failure: %s",
153 evutil_gai_strerror(result));
154 finish_connection(info, 0, buf);
155 } else {
156 log_debug("conn: socks resolve %s",
157 format_addr(ai->ai_addr));
158 assert(ai->ai_addrlen <= sizeof(info->addr));
159 memcpy(&info->addr, ai->ai_addr, ai->ai_addrlen);
160 info->addr_len = ai->ai_addrlen;
161 bufferevent_socket_connect(info->bev,
162 (struct sockaddr*)&socks_addr,
163 socks_addr_len);
166 if (ai)
167 evutil_freeaddrinfo(ai);
171 conn_connect_bufferevent(struct bufferevent *bev, struct evdns_base *dns,
172 int family, const char *name, int port,
173 conn_connectcb conncb, void *arg)
175 struct conninfo *info;
176 int rv = -1;
179 info = mem_calloc(1, sizeof(*info));
180 info->bev = bev;
181 info->on_connect = conncb;
182 info->cbarg = arg;
183 info->connecting = 1;
184 info->socks = use_socks;
186 bufferevent_setcb(bev, conn_readcb, NULL, conn_errorcb, info);
187 if (use_socks != SOCKS_NONE) {
188 info->host = mem_strdup(name);
189 info->port = port;
190 if (use_socks == SOCKS_4a) {
191 rv = bufferevent_socket_connect(bev,
192 (struct sockaddr*)&socks_addr,
193 socks_addr_len);
194 return rv;
196 #ifndef DISABLE_DIRECT_CONNECTIONS
197 else {
198 struct evutil_addrinfo hint;
199 char portstr[NI_MAXSERV];
201 evutil_snprintf(portstr, sizeof(portstr), "%d", port);
202 memset(&hint, 0, sizeof(hint));
203 hint.ai_family = AF_INET;
204 hint.ai_protocol = IPPROTO_TCP;
205 hint.ai_socktype = SOCK_STREAM;
207 evdns_getaddrinfo(dns, name, portstr, &hint,
208 socks_resolvecb, info);
209 return 0;
211 #endif
213 #ifdef DISABLE_DIRECT_CONNECTIONS
215 const char *msg;
216 msg = "Direct connections disabled, but I have no SOCKS 4a "
217 "proxy to connect to!";
218 log_error("conn: %s", msg);
219 finish_connection(info, 0, msg);
221 #else
222 rv = bufferevent_socket_connect_hostname(bev, dns, family, name, port);
223 #endif
225 return rv;
229 conn_set_socks_server(const char *name, int port, enum socks_ver ver)
231 int ret;
232 int rv = -1;
233 struct evutil_addrinfo *ai = NULL;
234 struct evutil_addrinfo hint;
235 char portstr[NI_MAXSERV];
237 assert(ver != SOCKS_NONE);
239 evutil_snprintf(portstr, sizeof(portstr), "%d", port);
240 memset(&hint, 0, sizeof(hint));
241 hint.ai_family = AF_UNSPEC;
242 hint.ai_protocol = IPPROTO_TCP;
243 hint.ai_socktype = SOCK_STREAM;
244 hint.ai_flags = EVUTIL_AI_ADDRCONFIG;
246 ret = evutil_getaddrinfo(name, portstr, &hint, &ai);
247 if (!ret) {
248 rv = 0;
249 memset(&socks_addr, 0, sizeof(socks_addr));
250 memcpy(&socks_addr, ai->ai_addr, ai->ai_addrlen);
251 socks_addr_len = ai->ai_addrlen;
252 use_socks = ver;
253 log_notice("conn: socks server set to %s",
254 format_addr((struct sockaddr*)&socks_addr));
255 } else {
256 log_error("conn: can't resolve socks server %s: %s",
257 name, evutil_gai_strerror(ret));
260 if (ai)
261 evutil_freeaddrinfo(ai);
263 return rv;
266 const char *
267 conn_get_connect_error(void)
269 return conn_error_string;
272 #ifdef TEST_CONN
273 void do_connect(struct bufferevent *bev, int ok, void *arg)
275 if (!ok) {
276 log_notice("conn: failed: %s", conn_get_connect_error());
277 } else {
278 log_notice("conn: conn OK!");
280 bufferevent_free(bev);
283 int main(int argc, char **argv)
285 struct evdns_base *dns;
286 struct event_base *base;
287 struct bufferevent *bev;
288 struct url *socks, *host;
289 int s4;
291 base = event_base_new();
292 dns = evdns_base_new(base, 1);
294 log_set_file(NULL);
295 log_set_min_level(LOG_DEBUG);
296 if (argc >= 3) {
297 socks = url_tokenize(argv[2]);
298 s4 = !evutil_ascii_strcasecmp("socks4", socks->scheme);
299 if (conn_set_socks_server(socks->host, socks->port, s4?
300 SOCKS_4 : SOCKS_4a) < 0)
301 return 0;
304 host = url_connect_tokenize(argv[1]);
306 bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
308 conn_connect_bufferevent(bev, dns, AF_INET, host->host, host->port,
309 do_connect, NULL);
311 event_base_dispatch(base);
313 return 0;
315 #endif