5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2005 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcincludes.h"
25 #define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
26 sizeof(so.sin6) : sizeof(so.sin))
28 #define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
33 struct sockaddr_in sin;
35 struct sockaddr_in6 sin6;
39 static bool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
44 memset(addr, 0, sizeof(*addr));
46 /* Check for IPv4 and IPv6 addresses */
48 if (!silc_net_is_ip(ip_addr)) {
49 SILC_LOG_ERROR(("%s is not IP address", ip_addr));
53 if (silc_net_is_ip4(ip_addr)) {
55 len = sizeof(addr->sin.sin_addr);
56 silc_net_addr2bin(ip_addr,
57 (unsigned char *)&addr->sin.sin_addr.s_addr, len);
58 addr->sin.sin_family = AF_INET;
59 addr->sin.sin_port = port ? htons(port) : 0;
63 len = sizeof(addr->sin6.sin6_addr);
64 silc_net_addr2bin(ip_addr,
65 (unsigned char *)&addr->sin6.sin6_addr, len);
66 addr->sin6.sin6_family = AF_INET6;
67 addr->sin6.sin6_port = port ? htons(port) : 0;
69 SILC_LOG_ERROR(("IPv6 support is not compiled in"));
75 addr->sin.sin_family = AF_INET;
76 addr->sin.sin_addr.s_addr = INADDR_ANY;
78 addr->sin.sin_port = htons(port);
84 /* Deliver new stream to upper layer */
86 static void silc_net_accept_stream(SilcSocketStreamStatus status,
87 SilcStream stream, void *context)
89 SilcNetServer server = context;
91 if (status != SILC_SOCKET_OK)
94 server->callback(SILC_NET_OK, stream, server->context);
97 /* Accept incoming connection and notify upper layer */
99 SILC_TASK_CALLBACK(silc_net_accept)
101 SilcNetServer server = context;
104 SILC_LOG_DEBUG(("Accepting new connection"));
106 sock = silc_net_accept_connection(fd);
110 /* Set socket options */
111 silc_net_set_socket_nonblock(sock);
112 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
114 /* Create socket stream */
115 silc_socket_stream_create(sock, TRUE, server->require_fqdn, schedule,
116 silc_net_accept_stream, server);
119 /* Create network listener */
122 silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count,
123 int port, bool require_fqdn, SilcSchedule schedule,
124 SilcNetCallback callback, void *context)
126 SilcNetServer netserver = NULL;
129 const char *ipany = "0.0.0.0";
131 SILC_LOG_DEBUG(("Creating new network listener"));
133 if (port < 1 || !schedule || !callback)
136 netserver = silc_calloc(1, sizeof(*netserver));
138 callback(SILC_NET_NO_MEMORY, NULL, context);
141 netserver->schedule = schedule;
142 netserver->callback = callback;
143 netserver->context = context;
145 if (local_ip_count > 0) {
146 netserver->socks = silc_calloc(local_ip_count, sizeof(*netserver->socks));
147 if (!netserver->socks) {
148 callback(SILC_NET_NO_MEMORY, NULL, context);
152 netserver->socks = silc_calloc(1, sizeof(*netserver->socks));
153 if (!netserver->socks) {
154 callback(SILC_NET_NO_MEMORY, NULL, context);
161 /* Bind to local addresses */
162 for (i = 0; i < local_ip_count; i++) {
163 SILC_LOG_DEBUG(("Binding to local address %s",
164 local_ip_addr ? local_ip_addr[i] : ipany));
166 /* Set sockaddr for server */
167 if (!silc_net_set_sockaddr(&server,
168 local_ip_addr ? local_ip_addr[i] : ipany,
172 /* Create the socket */
173 sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
175 SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
179 /* Set the socket options */
180 rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
182 SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
186 /* Bind the server socket */
187 rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
189 SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
193 /* Specify that we are listenning */
194 rval = listen(sock, 5);
196 SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
200 /* Set the server socket to non-blocking mode */
201 silc_net_set_socket_nonblock(sock);
203 /* Schedule for incoming connections */
204 silc_schedule_task_add_fd(schedule, sock, silc_net_accept, netserver);
206 SILC_LOG_DEBUG(("Network listener created, fd=%d", sock));
207 netserver->socks[i] = sock;
208 netserver->socks_count++;
215 callback(SILC_NET_ERROR, NULL, context);
217 silc_net_close_server(netserver);
221 /* Close network listener */
223 void silc_net_close_server(SilcNetServer server)
227 SILC_LOG_DEBUG(("Closing network listener"));
229 for (i = 0; i < server->socks_count; i++) {
230 silc_schedule_task_del_by_fd(server->schedule, server->socks[i]);
231 shutdown(server->socks[i], 2);
232 close(server->socks[i]);
235 silc_free(server->socks);
239 /* Asynchronous TCP/IP connecting */
242 SilcNetStatus status;
243 SilcSocketStreamStatus stream_status;
246 SilcFSMSemaStruct sema;
247 SilcAsyncOperation op;
248 SilcAsyncOperation sop;
253 SilcNetCallback callback;
255 unsigned int port : 24;
256 unsigned int retry : 7;
257 unsigned int aborted : 1;
260 SILC_FSM_STATE(silc_net_connect_st_start);
261 SILC_FSM_STATE(silc_net_connect_st_connected);
262 SILC_FSM_STATE(silc_net_connect_st_stream);
263 SILC_FSM_STATE(silc_net_connect_st_finish);
265 SILC_TASK_CALLBACK(silc_net_connect_wait)
267 SilcNetConnect conn = context;
268 SILC_FSM_SEMA_POST(&conn->sema);
271 SILC_FSM_STATE(silc_net_connect_st_start)
273 SilcNetConnect conn = fsm_context;
275 SilcSockaddr desthost;
276 bool prefer_ipv6 = TRUE;
280 silc_fsm_next(fsm, silc_net_connect_st_finish);
281 return SILC_FSM_CONTINUE;
286 if (!silc_net_gethostbyname(conn->remote, prefer_ipv6,
287 conn->ip_addr, sizeof(conn->ip_addr))) {
288 SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
289 "host", conn->remote));
291 /** Network unreachable */
292 conn->status = SILC_NET_HOST_UNREACHABLE;
293 silc_fsm_next(fsm, silc_net_connect_st_finish);
294 return SILC_FSM_CONTINUE;
297 /* Set sockaddr for this connection */
298 if (!silc_net_set_sockaddr(&desthost, conn->ip_addr, conn->port)) {
299 /** Sockaddr failed */
300 silc_fsm_next(fsm, silc_net_connect_st_finish);
301 return SILC_FSM_CONTINUE;
304 /* Create the connection socket */
305 sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
307 /* If address is IPv6, then fallback to IPv4 and see whether we can do
308 better with that on socket creation. */
309 if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
314 /** Cannot create socket */
315 SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
316 silc_fsm_next(fsm, silc_net_connect_st_finish);
317 return SILC_FSM_CONTINUE;
320 /* Bind to the local address if provided */
321 if (conn->local_ip) {
324 /* Set sockaddr for local listener, and try to bind it. */
325 if (silc_net_set_sockaddr(&local, conn->local_ip, 0))
326 bind(sock, &local.sa, SIZEOF_SOCKADDR(local));
329 /* Set the socket to non-blocking mode */
330 silc_net_set_socket_nonblock(sock);
332 /* Connect to the host */
333 rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost));
335 if (errno != EINPROGRESS) {
336 /* retry using an IPv4 adress, if IPv6 didn't work */
337 if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) {
348 /** Cannot connect to remote host */
349 SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
350 silc_fsm_next(fsm, silc_net_connect_st_finish);
351 return SILC_FSM_CONTINUE;
355 /* Set appropriate options */
356 #if defined(TCP_NODELAY)
357 silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
359 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
361 SILC_LOG_DEBUG(("Connection operation in progress"));
365 /** Wait for connection */
366 silc_fsm_next(fsm, silc_net_connect_st_connected);
367 silc_fsm_sema_init(&conn->sema, fsm, 0);
368 silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock,
369 silc_net_connect_wait, conn);
370 silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock,
371 SILC_TASK_WRITE, FALSE);
372 SILC_FSM_SEMA_WAIT(&conn->sema);
373 return SILC_FSM_CONTINUE;
376 static void silc_net_connect_wait_stream(SilcSocketStreamStatus status,
377 SilcStream stream, void *context)
379 SilcNetConnect conn = context;
380 conn->stream_status = status;
381 conn->stream = stream;
382 SILC_FSM_CALL_CONTINUE(&conn->fsm);
385 SILC_FSM_STATE(silc_net_connect_st_connected)
387 SilcNetConnect conn = fsm_context;
388 SilcSchedule schedule = silc_fsm_get_schedule(fsm);
389 int opt = EINVAL, optlen = sizeof(opt), ret;
393 silc_fsm_next(fsm, silc_net_connect_st_finish);
394 return SILC_FSM_CONTINUE;
397 ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR,
400 silc_schedule_task_del_by_fd(schedule, conn->sock);
401 silc_schedule_unset_listen_fd(schedule, conn->sock);
403 if (ret != 0 || opt != 0) {
405 /** Retry connecting */
406 SILC_LOG_DEBUG(("Retry connecting"));
408 silc_net_close_connection(conn->sock);
409 silc_fsm_next(fsm, silc_net_connect_st_start);
410 return SILC_FSM_CONTINUE;
413 #if defined(ECONNREFUSED)
414 if (errno == ECONNREFUSED)
415 conn->status = SILC_NET_CONNECTION_REFUSED;
416 #endif /* ECONNREFUSED */
417 #if defined(ETIMEDOUT)
418 if (errno == ETIMEDOUT)
419 conn->status = SILC_NET_CONNECTION_TIMEOUT;
420 #endif /* ETIMEDOUT */
421 #if defined(ENETUNREACH)
422 if (errno == ENETUNREACH)
423 conn->status = SILC_NET_HOST_UNREACHABLE;
424 #endif /* ENETUNREACH */
426 /** Connecting failed */
427 SILC_LOG_DEBUG(("Connecting failed"));
428 silc_fsm_next(fsm, silc_net_connect_st_finish);
429 return SILC_FSM_CONTINUE;
432 /** Connection created */
433 silc_fsm_next(fsm, silc_net_connect_st_stream);
434 SILC_FSM_CALL((conn->sop = silc_socket_stream_create(
435 conn->sock, FALSE, FALSE,
437 silc_net_connect_wait_stream, conn)));
440 SILC_FSM_STATE(silc_net_connect_st_stream)
442 SilcNetConnect conn = fsm_context;
446 silc_fsm_next(fsm, silc_net_connect_st_finish);
447 return SILC_FSM_CONTINUE;
450 if (conn->stream_status != SILC_SOCKET_OK) {
451 /** Stream creation failed */
452 if (conn->stream_status == SILC_SOCKET_UNKNOWN_IP)
453 conn->status = SILC_NET_UNKNOWN_IP;
454 else if (conn->stream_status == SILC_SOCKET_UNKNOWN_HOST)
455 conn->status = SILC_NET_UNKNOWN_HOST;
457 conn->status = SILC_NET_ERROR;
458 silc_fsm_next(fsm, silc_net_connect_st_finish);
459 return SILC_FSM_CONTINUE;
462 /* Set stream information */
463 silc_socket_stream_set_info(conn->stream,
464 !silc_net_is_ip(conn->remote) ? conn->remote :
465 conn->ip_addr, conn->ip_addr, conn->port);
467 /** Stream created successfully */
468 SILC_LOG_DEBUG(("Connected successfully"));
469 conn->status = SILC_NET_OK;
470 silc_fsm_next(fsm, silc_net_connect_st_finish);
471 return SILC_FSM_CONTINUE;
474 SILC_FSM_STATE(silc_net_connect_st_finish)
476 SilcNetConnect conn = fsm_context;
478 /* Deliver error or new stream */
479 if (!conn->aborted) {
480 conn->callback(conn->status, conn->stream, conn->context);
482 silc_async_free(conn->op);
484 silc_async_free(conn->sop);
487 return SILC_FSM_FINISH;
490 static void silc_net_connect_abort(SilcAsyncOperation op, void *context)
492 SilcNetConnect conn = context;
493 conn->aborted = TRUE;
495 /* Abort underlaying stream creation too */
497 silc_async_abort(conn->op, NULL, NULL);
500 static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context,
501 void *destructor_context)
503 SilcNetConnect conn = fsm_context;
504 silc_free(conn->local_ip);
505 silc_free(conn->remote);
509 /* Create asynchronous TCP/IP connection. */
511 SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr,
512 const char *remote_ip_addr,
514 SilcSchedule schedule,
515 SilcNetCallback callback,
520 if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
523 SILC_LOG_DEBUG(("Creating connection to host %s port %d",
524 remote_ip_addr, remote_port));
526 conn = silc_calloc(1, sizeof(*conn));
528 callback(SILC_NET_NO_MEMORY, NULL, context);
532 /* Start async operation */
533 conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn);
535 callback(SILC_NET_NO_MEMORY, NULL, context);
540 conn->local_ip = strdup(local_ip_addr);
541 conn->remote = strdup(remote_ip_addr);
543 callback(SILC_NET_NO_MEMORY, NULL, context);
546 conn->port = remote_port;
547 conn->callback = callback;
548 conn->context = context;
550 conn->status = SILC_NET_ERROR;
552 silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule);
553 silc_fsm_start(&conn->fsm, silc_net_connect_st_start);
558 /* Closes the connection by closing the socket connection. */
560 void silc_net_close_connection(int sock)
565 /* Set's the socket to non-blocking mode. */
567 int silc_net_set_socket_nonblock(int sock)
569 return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
572 /* Converts the IP number string from numbers-and-dots notation to
575 bool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
579 if (silc_net_is_ip4(addr)) {
582 ret = inet_aton(addr, &tmp);
586 memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
589 struct addrinfo hints, *ai;
596 memset(&hints, 0, sizeof(hints));
597 hints.ai_family = AF_INET6;
598 if (getaddrinfo(addr, NULL, &hints, &ai))
602 s = (SilcSockaddr *)ai->ai_addr;
603 memcpy(bin, &s->sin6.sin6_addr, sizeof(s->sin6.sin6_addr));
608 #endif /* HAVE_IPV6 */