1 /*
2 * uhub - A tiny ADC p2p connection hub
3 * Copyright (C) 2007-2014, Jan Vidar Krey
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include "uhub.h"
21 #include "network/common.h"
22 #include "network/backend.h"
23
is_blocked_or_interrupted()24 static int is_blocked_or_interrupted()
25 {
26 int err = net_error();
27 return
28 #ifdef WINSOCK
29 err == WSAEWOULDBLOCK
30 #else
31 err == EWOULDBLOCK
32 #endif
33 || err == EINTR;
34 }
35
net_con_send(struct net_connection * con,const void * buf,size_t len)36 ssize_t net_con_send(struct net_connection* con, const void* buf, size_t len)
37 {
38 int ret;
39 #ifdef SSL_SUPPORT
40 if (!con->ssl)
41 {
42 #endif
43 ret = net_send(con->sd, buf, len, UHUB_SEND_SIGNAL);
44 if (ret == -1)
45 {
46 if (is_blocked_or_interrupted())
47 return 0;
48 return -1;
49 }
50 #ifdef SSL_SUPPORT
51 }
52 else
53 {
54 ret = net_ssl_send(con, buf, len);
55 }
56 #endif /* SSL_SUPPORT */
57 return ret;
58 }
59
net_con_recv(struct net_connection * con,void * buf,size_t len)60 ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len)
61 {
62 int ret;
63 #ifdef SSL_SUPPORT
64 if (!con->ssl)
65 {
66 #endif
67 ret = net_recv(con->sd, buf, len, 0);
68 if (ret == -1)
69 {
70 if (is_blocked_or_interrupted())
71 return 0;
72 return -net_error();
73 }
74 else if (ret == 0)
75 {
76 return -1;
77 }
78 #ifdef SSL_SUPPORT
79 }
80 else
81 {
82 ret = net_ssl_recv(con, buf, len);
83 }
84 #endif /* SSL_SUPPORT */
85 return ret;
86 }
87
net_con_peek(struct net_connection * con,void * buf,size_t len)88 ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len)
89 {
90 int ret = net_recv(con->sd, buf, len, MSG_PEEK);
91 if (ret == -1)
92 {
93 if (is_blocked_or_interrupted())
94 return 0;
95 return -net_error();
96 }
97 else if (ret == 0)
98 return -1;
99 return ret;
100 }
101
102 #ifdef SSL_SUPPORT
103
net_con_is_ssl(struct net_connection * con)104 int net_con_is_ssl(struct net_connection* con)
105 {
106 return !!con->ssl;
107 }
108 #endif /* SSL_SUPPORT */
109
net_con_get_sd(struct net_connection * con)110 int net_con_get_sd(struct net_connection* con)
111 {
112 return con->sd;
113 }
114
net_con_get_ptr(struct net_connection * con)115 void* net_con_get_ptr(struct net_connection* con)
116 {
117 return con->ptr;
118 }
119
net_con_update(struct net_connection * con,int events)120 void net_con_update(struct net_connection* con, int events)
121 {
122 #ifdef SSL_SUPPORT
123 if (con->ssl)
124 net_ssl_update(con, events);
125 else
126 #endif
127 net_backend_update(con, events);
128 }
129
net_con_reinitialize(struct net_connection * con,net_connection_cb callback,const void * ptr,int events)130 void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events)
131 {
132 con->callback = callback;
133 con->ptr = (void*) ptr;
134 net_con_update(con, events);
135 }
136
net_con_destroy(struct net_connection * con)137 void net_con_destroy(struct net_connection* con)
138 {
139 #ifdef SSL_SUPPORT
140 if (con && con->ssl)
141 net_ssl_destroy(con);
142 #endif
143 hub_free(con);
144 }
145
net_con_callback(struct net_connection * con,int events)146 void net_con_callback(struct net_connection* con, int events)
147 {
148 if (con->flags & NET_CLEANUP)
149 return;
150
151 if (events == NET_EVENT_TIMEOUT)
152 {
153 LOG_TRACE("net_con_callback(%p, TIMEOUT)", con);
154 con->callback(con, events, con->ptr);
155 return;
156 }
157
158 #ifdef SSL_SUPPORT
159 if (con->ssl)
160 net_ssl_callback(con, events);
161 else
162 #endif
163 con->callback(con, events, con->ptr);
164 }
165
166 struct net_connect_job
167 {
168 struct net_connection* con;
169 struct net_connect_handle* handle;
170 struct sockaddr_storage addr;
171 struct net_connect_job* next;
172 };
173
174 struct net_connect_handle
175 {
176 const char* address;
177 uint16_t port;
178 void* ptr;
179 net_connect_cb callback;
180 struct net_dns_job* dns;
181 const struct net_dns_result* result;
182 struct net_connect_job* job4;
183 struct net_connect_job* job6;
184 };
185
186 static void net_connect_callback(struct net_connect_handle* handle, enum net_connect_status status, struct net_connection* con);
187 static void net_connect_job_internal_cb(struct net_connection* con, int event, void* ptr);
188
189 /**
190 * Check if a connection job is completed.
191 * @return -1 on completed with an error, 0 on not yet completed, or 1 if completed successfully (connected).
192 */
net_connect_job_check(struct net_connect_job * job)193 static int net_connect_job_check(struct net_connect_job* job)
194 {
195 struct net_connection* con = job->con;
196 int af = job->addr.ss_family;
197 enum net_connect_status status;
198
199 int ret = net_connect(net_con_get_sd(con), (struct sockaddr*) &job->addr, af == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
200 if (ret == 0 || (ret == -1 && net_error() == EISCONN))
201 {
202 LOG_TRACE("net_connect_job_check(): Socket connected!");
203 job->con = NULL;
204 net_con_clear_timeout(con);
205 net_connect_callback(job->handle, net_connect_status_ok, con);
206 return 1;
207 }
208 else if (ret == -1 && (net_error() == EALREADY || net_error() == EINPROGRESS || net_error() == EWOULDBLOCK || net_error() == EINTR))
209 {
210 return 0;
211 }
212 LOG_TRACE("net_connect_job_check(): Socket error!");
213
214 switch (net_error())
215 {
216 case ECONNREFUSED:
217 status = net_connect_status_refused;
218 break;
219 case ENETUNREACH:
220 status = net_connect_status_unreachable;
221 break;
222
223 default:
224 status = net_connect_status_socket_error;
225 }
226
227 net_connect_callback(job->handle, status, NULL);
228 return -1;
229 }
230
net_connect_job_free(struct net_connect_job * job)231 static void net_connect_job_free(struct net_connect_job* job)
232 {
233 if (job->con)
234 net_con_close(job->con);
235 job->handle = NULL;
236 job->next = NULL;
237 hub_free(job);
238 }
239
net_connect_job_stop(struct net_connect_job * job)240 static void net_connect_job_stop(struct net_connect_job* job)
241 {
242 if (job->addr.ss_family == AF_INET6)
243 {
244 job->handle->job6 = job->next;
245 }
246 else
247 {
248 job->handle->job4 = job->next;
249 }
250
251 net_connect_job_free(job);
252 }
253
net_connect_depleted(struct net_connect_handle * handle)254 static int net_connect_depleted(struct net_connect_handle* handle)
255 {
256 return (!handle->job6 && !handle->job4);
257 }
258
net_connect_job_process(struct net_connect_job * job)259 static int net_connect_job_process(struct net_connect_job* job)
260 {
261 int sd;
262 if (!job->con)
263 {
264 sd = net_socket_create(job->addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
265 if (sd == -1)
266 {
267 LOG_DEBUG("net_connect_job_process: Unable to create socket!");
268 net_connect_callback(job->handle, net_connect_status_socket_error, NULL);
269 return -1; // FIXME
270 }
271
272 job->con = net_con_create();
273 net_con_initialize(job->con, sd, net_connect_job_internal_cb, job, NET_EVENT_WRITE);
274 net_con_set_timeout(job->con, TIMEOUT_CONNECTED); // FIXME: Use a proper timeout value!
275 }
276
277 return net_connect_job_check(job);
278 }
279
280
281 /*
282 * Internal callback used to establish an outbound connection.
283 */
net_connect_job_internal_cb(struct net_connection * con,int event,void * ptr)284 static void net_connect_job_internal_cb(struct net_connection* con, int event, void* ptr)
285 {
286 struct net_connect_job* job = net_con_get_ptr(con);
287 struct net_connect_job* next_job = job->next;
288 struct net_connect_handle* handle = job->handle;
289
290 if (event == NET_EVENT_TIMEOUT)
291 {
292 // FIXME: Try next address, or if no more addresses left declare failure to connect.
293 if (job->addr.ss_family == AF_INET6)
294 {
295 net_connect_job_stop(job);
296
297 if (!next_job)
298 {
299 LOG_TRACE("No more IPv6 addresses to try!");
300 }
301
302 }
303 else
304 {
305 net_connect_job_stop(job);
306
307 if (!next_job)
308 {
309 LOG_TRACE("No more IPv4 addresses to try!");
310 }
311 }
312
313 if (net_connect_depleted(handle))
314 {
315 LOG_TRACE("No more addresses left. Unable to connect!");
316 net_connect_callback(handle, net_connect_status_timeout, NULL);
317 }
318 return;
319 }
320
321 if (event == NET_EVENT_WRITE)
322 {
323 net_connect_job_process(job);
324 }
325 }
326
327
net_connect_cancel(struct net_connect_handle * handle)328 static void net_connect_cancel(struct net_connect_handle* handle)
329 {
330 struct net_connect_job* job;
331
332 job = handle->job6;
333 while (job)
334 {
335 job = job->next;
336 net_connect_job_free(handle->job6);
337 handle->job6 = job;
338 }
339
340 job = handle->job4;
341 while (job)
342 {
343 job = job->next;
344 net_connect_job_free(handle->job4);
345 handle->job4 = job;
346 }
347 }
348
349
net_connect_process_queue(struct net_connect_handle * handle,struct net_connect_job * job)350 static int net_connect_process_queue(struct net_connect_handle* handle, struct net_connect_job* job)
351 {
352 int ret;
353 while (job)
354 {
355 ret = net_connect_job_process(job);
356 if (ret < 0)
357 {
358 net_connect_job_stop(job);
359 continue;
360 }
361 else if (ret == 0)
362 {
363 // Need to process again
364 return 0;
365 }
366 else
367 {
368 // FIXME: Success!
369 return 1;
370 }
371 }
372 return -1;
373 }
374
net_connect_process(struct net_connect_handle * handle)375 static int net_connect_process(struct net_connect_handle* handle)
376 {
377 int ret4, ret6;
378
379 ret6 = net_connect_process_queue(handle, handle->job6);
380 if (ret6 == 1)
381 return 1; // Connected - cool!
382
383 net_connect_process_queue(handle, handle->job4);
384 return 0;
385 }
386
387
net_connect_job_schedule(struct net_connect_handle * handle,struct ip_addr_encap * addr)388 static int net_connect_job_schedule(struct net_connect_handle* handle, struct ip_addr_encap* addr)
389 {
390 struct net_connect_job* job;
391 struct sockaddr_in* addr4;
392 struct sockaddr_in6* addr6;
393
394 if (addr->af == AF_INET6 && !net_is_ipv6_supported())
395 {
396 LOG_TRACE("net_connect_job_schedule(): Skipping IPv6 support since IPv6 is not supported.");
397 return 0;
398 }
399 else
400 {
401 job = hub_malloc_zero(sizeof(struct net_connect_job));
402 job->handle = handle;
403 if (addr->af == AF_INET6)
404 {
405 addr6 = (struct sockaddr_in6*) &job->addr;
406 LOG_TRACE("net_connect_job_schedule(): Scheduling IPv6 connect job.");
407 addr6->sin6_family = AF_INET6;
408 addr6->sin6_port = htons(handle->port);
409 memcpy(&addr6->sin6_addr, &addr->internal_ip_data.in6, sizeof(struct in6_addr));
410
411 // prepend
412 job->next = handle->job6;
413 handle->job6 = job;
414 }
415 else
416 {
417 addr4 = (struct sockaddr_in*) &job->addr;
418 LOG_TRACE("net_connect_job_schedule(): Scheduling IPv4 connect job.");
419 addr4->sin_family = AF_INET;
420 addr4->sin_port = htons(handle->port);
421 memcpy(&addr4->sin_addr, &addr->internal_ip_data.in, sizeof(struct in_addr));
422
423 // prepend
424 job->next = handle->job4;
425 handle->job4 = job;
426 }
427 }
428 return 1;
429 }
430
431
432 /*
433 * Callback when the DNS results are ready.
434 * Create a list of IPv6 and IPv4 addresses, then
435 * start connecting to them one by one until one succeeds.
436 */
net_con_connect_dns_callback(struct net_dns_job * job,const struct net_dns_result * result)437 static int net_con_connect_dns_callback(struct net_dns_job* job, const struct net_dns_result* result)
438 {
439 struct ip_addr_encap* addr;
440 struct net_connect_handle* handle = (struct net_connect_handle*) net_dns_job_get_ptr(job);
441 handle->dns = NULL;
442 size_t usable = 0;
443
444 LOG_TRACE("net_con_connect(): async - Got DNS results");
445 if (!result)
446 {
447 LOG_DEBUG("net_con_connect() - Unable to lookup host!");
448 net_connect_callback(handle, net_connect_status_dns_error, NULL);
449 return 1;
450 }
451
452 if (!net_dns_result_size(result))
453 {
454 LOG_DEBUG("net_con_connect() - Host not found!");
455 net_connect_callback(handle, net_connect_status_host_not_found, NULL);
456 return 1;
457 }
458
459 handle->result = result;
460
461 // Extract results into a separate list of IPv4 and IPv6 addresses.
462 addr = net_dns_result_first(result);
463 while (addr)
464 {
465 if (net_connect_job_schedule(handle, addr))
466 usable++;
467 addr = net_dns_result_next(result);
468 }
469
470 net_connect_process(handle);
471
472 return 0;
473 }
474
475 // typedef void (*net_connect_cb)(struct net_connect_handle*, enum net_connect_handle_code, struct net_connection* con);
476
net_con_connect(const char * address,uint16_t port,net_connect_cb callback,void * ptr)477 struct net_connect_handle* net_con_connect(const char* address, uint16_t port, net_connect_cb callback, void* ptr)
478 {
479 struct net_connect_handle* handle = hub_malloc_zero(sizeof(struct net_connect_handle));
480
481 handle->address = hub_strdup(address);
482 handle->port = port;
483 handle->ptr = ptr;
484 handle->callback = callback;
485
486 // FIXME: Check if DNS resolving is necessary ?
487 handle->dns = net_dns_gethostbyname(address, AF_UNSPEC, net_con_connect_dns_callback, handle);
488 if (!handle->dns)
489 {
490 LOG_TRACE("net_con_connect(): Unable to create DNS lookup job.");
491 hub_free((char*) handle->address);
492 hub_free(handle);
493 return NULL;
494 }
495
496 return handle;
497 }
498
net_connect_destroy(struct net_connect_handle * handle)499 void net_connect_destroy(struct net_connect_handle* handle)
500 {
501 hub_free((char*) handle->address);
502
503 // cancel DNS job if pending
504 if (handle->dns)
505 net_dns_job_cancel(handle->dns);
506
507 // Stop any connect jobs.
508 net_connect_cancel(handle);
509
510 // free any DNS results
511 net_dns_result_free(handle->result);
512
513 hub_free(handle);
514 }
515
net_connect_callback(struct net_connect_handle * handle,enum net_connect_status status,struct net_connection * con)516 static void net_connect_callback(struct net_connect_handle* handle, enum net_connect_status status, struct net_connection* con)
517 {
518 uhub_assert(handle->callback != NULL);
519
520 // Call the callback
521 handle->callback(handle, status, con, handle->ptr);
522 handle->callback = NULL;
523
524 // Cleanup
525 net_connect_destroy(handle);
526 }
527
timeout_callback(struct timeout_evt * evt)528 static void timeout_callback(struct timeout_evt* evt)
529 {
530 net_con_callback((struct net_connection*) evt->ptr, NET_EVENT_TIMEOUT);
531 }
532
533
net_con_set_timeout(struct net_connection * con,int seconds)534 void net_con_set_timeout(struct net_connection* con, int seconds)
535 {
536 if (!con->timeout)
537 {
538 con->timeout = hub_malloc_zero(sizeof(struct timeout_evt));
539 timeout_evt_initialize(con->timeout, timeout_callback, con);
540 timeout_queue_insert(net_backend_get_timeout_queue(), con->timeout, seconds);
541 }
542 else
543 {
544 timeout_queue_reschedule(net_backend_get_timeout_queue(), con->timeout, seconds);
545 }
546 }
547
net_con_clear_timeout(struct net_connection * con)548 void net_con_clear_timeout(struct net_connection* con)
549 {
550 if (con->timeout && timeout_evt_is_scheduled(con->timeout))
551 {
552 timeout_queue_remove(net_backend_get_timeout_queue(), con->timeout);
553 hub_free(con->timeout);
554 con->timeout = 0;
555 }
556 }
557
558