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