X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Funix%2Fsilcunixnet.c;h=e0befdc19979ca325a83cd39b6da9015e34a28f8;hb=52e57c880aba9c5e89f59d962eb9af75670b76e0;hp=654d8bbef7294989422f0bff702b35bbb6b99c12;hpb=c27a4ecc3e616e8a5ee09b8ca888ed6ff3e501f7;p=silc.git diff --git a/lib/silcutil/unix/silcunixnet.c b/lib/silcutil/unix/silcunixnet.c index 654d8bbe..e0befdc1 100644 --- a/lib/silcutil/unix/silcunixnet.c +++ b/lib/silcutil/unix/silcunixnet.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 2007 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,9 +18,11 @@ */ /* $Id$ */ -#include "silcincludes.h" +#include "silc.h" #include "silcnet.h" +/************************** Types and definitions ***************************/ + #ifdef HAVE_IPV6 #define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \ sizeof(so.sin6) : sizeof(so.sin)) @@ -36,8 +38,10 @@ typedef union { #endif } SilcSockaddr; +/************************ Static utility functions **************************/ + static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr, - int port) + int port) { int len; @@ -81,24 +85,26 @@ static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr, return TRUE; } +/****************************** TCP Listener ********************************/ + /* Deliver new stream to upper layer */ static void silc_net_accept_stream(SilcSocketStreamStatus status, SilcStream stream, void *context) { - SilcNetServer server = context; + SilcNetListener listener = context; if (status != SILC_SOCKET_OK) return; - server->callback(SILC_NET_OK, stream, server->context); + listener->callback(SILC_NET_OK, stream, listener->context); } /* Accept incoming connection and notify upper layer */ SILC_TASK_CALLBACK(silc_net_accept) { - SilcNetServer server = context; + SilcNetListener listener = context; int sock; SILC_LOG_DEBUG(("Accepting new connection")); @@ -108,60 +114,58 @@ SILC_TASK_CALLBACK(silc_net_accept) return; /* Set socket options */ - silc_net_set_socket_nonblock(sock); silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); /* Create socket stream */ - silc_socket_stream_create(sock, TRUE, server->require_fqdn, schedule, - silc_net_accept_stream, server); + silc_socket_tcp_stream_create(sock, listener->lookup, + listener->require_fqdn, schedule, + silc_net_accept_stream, listener); } -/* Create network listener */ +/* Create TCP network listener */ -SilcNetServer -silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, - int port, SilcBool require_fqdn, SilcSchedule schedule, - SilcNetCallback callback, void *context) +SilcNetListener +silc_net_tcp_create_listener(const char **local_ip_addr, + SilcUInt32 local_ip_count, int port, + SilcBool lookup, SilcBool require_fqdn, + SilcSchedule schedule, + SilcNetCallback callback, void *context) { - SilcNetServer netserver = NULL; + SilcNetListener listener = NULL; SilcSockaddr server; int i, sock, rval; const char *ipany = "0.0.0.0"; - SILC_LOG_DEBUG(("Creating new network listener")); + SILC_LOG_DEBUG(("Creating TCP listener")); - if (port < 1 || !schedule || !callback) + if (port < 0 || !schedule || !callback) goto err; - netserver = silc_calloc(1, sizeof(*netserver)); - if (!netserver) { - callback(SILC_NET_NO_MEMORY, NULL, context); + listener = silc_calloc(1, sizeof(*listener)); + if (!listener) return NULL; - } - netserver->schedule = schedule; - netserver->callback = callback; - netserver->context = context; + listener->schedule = schedule; + listener->callback = callback; + listener->context = context; + listener->require_fqdn = require_fqdn; + listener->lookup = lookup; if (local_ip_count > 0) { - netserver->socks = silc_calloc(local_ip_count, sizeof(*netserver->socks)); - if (!netserver->socks) { - callback(SILC_NET_NO_MEMORY, NULL, context); + listener->socks = silc_calloc(local_ip_count, sizeof(*listener->socks)); + if (!listener->socks) return NULL; - } } else { - netserver->socks = silc_calloc(1, sizeof(*netserver->socks)); - if (!netserver->socks) { - callback(SILC_NET_NO_MEMORY, NULL, context); + listener->socks = silc_calloc(1, sizeof(*listener->socks)); + if (!listener->socks) return NULL; - } local_ip_count = 1; } /* Bind to local addresses */ for (i = 0; i < local_ip_count; i++) { - SILC_LOG_DEBUG(("Binding to local address %s", - local_ip_addr ? local_ip_addr[i] : ipany)); + SILC_LOG_DEBUG(("Binding to local address %s:%d", + local_ip_addr ? local_ip_addr[i] : ipany, port)); /* Set sockaddr for server */ if (!silc_net_set_sockaddr(&server, @@ -180,20 +184,23 @@ silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); if (rval < 0) { SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + close(sock); goto err; } - /* Bind the server socket */ + /* Bind the listener socket */ rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); if (rval < 0) { - SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno))); + SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno))); + close(sock); goto err; } /* Specify that we are listenning */ - rval = listen(sock, 5); + rval = listen(sock, 64); if (rval < 0) { SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno))); + close(sock); goto err; } @@ -201,41 +208,251 @@ silc_net_create_server(const char **local_ip_addr, SilcUInt32 local_ip_count, silc_net_set_socket_nonblock(sock); /* Schedule for incoming connections */ - silc_schedule_task_add_fd(schedule, sock, silc_net_accept, netserver); + silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener); - SILC_LOG_DEBUG(("Network listener created, fd=%d", sock)); - netserver->socks[i] = sock; - netserver->socks_count++; + SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock)); + listener->socks[i] = sock; + listener->socks_count++; } - return netserver; + return listener; err: - if (callback) - callback(SILC_NET_ERROR, NULL, context); - if (netserver) - silc_net_close_server(netserver); + if (listener) + silc_net_close_listener(listener); return NULL; } /* Close network listener */ -void silc_net_close_server(SilcNetServer server) +void silc_net_close_listener(SilcNetListener listener) { int i; SILC_LOG_DEBUG(("Closing network listener")); - for (i = 0; i < server->socks_count; i++) { - silc_schedule_task_del_by_fd(server->schedule, server->socks[i]); - shutdown(server->socks[i], 2); - close(server->socks[i]); + for (i = 0; i < listener->socks_count; i++) { + silc_schedule_task_del_by_fd(listener->schedule, listener->socks[i]); + shutdown(listener->socks[i], 2); + close(listener->socks[i]); + } + + silc_free(listener->socks); + silc_free(listener); +} + +/******************************* UDP Stream *********************************/ + +/* Create UDP stream */ + +SilcStream +silc_net_udp_connect(const char *local_ip_addr, int local_port, + const char *remote_ip_addr, int remote_port, + SilcSchedule schedule) +{ + SilcStream stream; + SilcSockaddr server; + int sock = -1, rval; + const char *ipany = "0.0.0.0"; + + SILC_LOG_DEBUG(("Creating UDP stream")); + + if (!schedule) + goto err; + + /* Bind to local addresses */ + SILC_LOG_DEBUG(("Binding to local address %s", + local_ip_addr ? local_ip_addr : ipany)); + + /* Set sockaddr for server */ + if (!silc_net_set_sockaddr(&server, local_ip_addr ? local_ip_addr : ipany, + local_port)) + goto err; + + /* Create the socket */ + sock = socket(server.sin.sin_family, SOCK_DGRAM, 0); + if (sock < 0) { + SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno))); + goto err; + } + + /* Set the socket options */ + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } +#ifdef SO_REUSEPORT + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEPORT, 1); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } +#endif /* SO_REUSEPORT */ + + /* Bind the listener socket */ + rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); + if (rval < 0) { + SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno))); + goto err; + } + + /* Set to connected state if remote address is provided. */ + if (remote_ip_addr && remote_port) { + if (!silc_net_set_sockaddr(&server, remote_ip_addr, remote_port)) + goto err; + + rval = connect(sock, &server.sa, SIZEOF_SOCKADDR(server)); + if (rval < 0) { + SILC_LOG_DEBUG(("Cannot connect UDP stream: %s", strerror(errno))); + goto err; + } + } + + /* Set send and receive buffer size */ +#ifdef SO_SNDBUF + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 765535); + if (rval < 0) { + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_SNDBUF, 65535); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } + } +#endif /* SO_SNDBUF */ +#ifdef SO_RCVBUF + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 765535); + if (rval < 0) { + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_RCVBUF, 65535); + if (rval < 0) { + SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno))); + goto err; + } + } +#endif /* SO_RCVBUF */ + + /* Encapsulate into socket stream */ + stream = + silc_socket_udp_stream_create(sock, local_ip_addr ? + silc_net_is_ip6(local_ip_addr) : FALSE, + remote_ip_addr ? TRUE : FALSE, schedule); + if (!stream) + goto err; + + SILC_LOG_DEBUG(("UDP stream created, fd=%d", sock)); + return stream; + + err: + if (sock != -1) + close(sock); + return NULL; +} + +/* Receive UDP packet */ + +int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr, + SilcUInt32 remote_ip_addr_size, int *remote_port, + unsigned char *ret_data, SilcUInt32 data_size) +{ + SilcSocketStream sock = stream; + SilcSockaddr s; + struct sockaddr *from; + int len, flen; + + SILC_LOG_DEBUG(("Reading data from UDP socket %d", sock->sock)); + + if (remote_ip_addr && remote_port) { + if (sock->ipv6) { + from = (struct sockaddr *)&s.sin6; + flen = sizeof(s.sin6); + } else { + from = (struct sockaddr *)&s.sin; + flen = sizeof(s.sin); + } + len = recvfrom(sock->sock, ret_data, data_size, 0, from, &flen); + } else + len = recv(sock->sock, ret_data, data_size, 0); + + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) { + SILC_LOG_DEBUG(("Could not read immediately, will do it later")); + silc_schedule_set_listen_fd(sock->schedule, sock->sock, + SILC_TASK_READ, FALSE); + return -1; + } + SILC_LOG_DEBUG(("Cannot read from UDP socket: %d:%s", + sock->sock, strerror(errno))); + silc_schedule_unset_listen_fd(sock->schedule, sock->sock); + sock->sock_error = errno; + return -2; } - silc_free(server->socks); - silc_free(server); + SILC_LOG_DEBUG(("Read %d bytes", len)); + + if (!len) + silc_schedule_unset_listen_fd(sock->schedule, sock->sock); + + /* Return remote address */ + if (remote_ip_addr && remote_port) { + if (sock->ipv6) { + *remote_port = ntohs(s.sin6.sin6_port); + inet_ntop(AF_INET6, &s.sin6.sin6_addr, remote_ip_addr, + remote_ip_addr_size); + } else { + *remote_port = ntohs(s.sin.sin_port); + inet_ntop(AF_INET, &s.sin.sin_addr, remote_ip_addr, + remote_ip_addr_size); + } + + SILC_LOG_DEBUG(("UDP packet from %s:%d", remote_ip_addr, *remote_port)); + } + + return len; +} + +/* Send UDP packet */ + +int silc_net_udp_send(SilcStream stream, + const char *remote_ip_addr, int remote_port, + const unsigned char *data, SilcUInt32 data_len) +{ + SilcSocketStream sock = stream; + SilcSockaddr remote; + int ret; + + SILC_LOG_DEBUG(("Sending data to UDP socket %d", sock->sock)); + + /* Set sockaddr */ + if (!silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) + return -2; + + /* Send */ + ret = sendto(sock->sock, data, data_len, 0, &remote.sa, + SIZEOF_SOCKADDR(remote)); + if (ret < 0) { + if (errno == EAGAIN || errno == EINTR) { + SILC_LOG_DEBUG(("Could not send immediately, will do it later")); + silc_schedule_set_listen_fd(sock->schedule, sock->sock, + SILC_TASK_READ | SILC_TASK_WRITE, FALSE); + return -1; + } + SILC_LOG_DEBUG(("Cannot send to UDP socket: %s", strerror(errno))); + silc_schedule_unset_listen_fd(sock->schedule, sock->sock); + sock->sock_error = errno; + return -2; + } + + SILC_LOG_DEBUG(("Sent data %d bytes", ret)); + if (silc_schedule_get_fd_events(sock->schedule, sock->sock) & + SILC_TASK_WRITE) + silc_schedule_set_listen_fd(sock->schedule, sock->sock, + SILC_TASK_READ, FALSE); + + return ret; } +/******************************* TCP Stream *********************************/ + /* Asynchronous TCP/IP connecting */ typedef struct { @@ -243,7 +460,7 @@ typedef struct { SilcSocketStreamStatus stream_status; SilcStream stream; SilcFSMStruct fsm; - SilcFSMSemaStruct sema; + SilcFSMEventStruct event; SilcAsyncOperation op; SilcAsyncOperation sop; char *local_ip; @@ -265,7 +482,7 @@ SILC_FSM_STATE(silc_net_connect_st_finish); SILC_TASK_CALLBACK(silc_net_connect_wait) { SilcNetConnect conn = context; - SILC_FSM_SEMA_POST(&conn->sema); + SILC_FSM_EVENT_SIGNAL(&conn->event); } SILC_FSM_STATE(silc_net_connect_st_start) @@ -333,18 +550,15 @@ SILC_FSM_STATE(silc_net_connect_st_start) rval = connect(sock, &desthost.sa, SIZEOF_SOCKADDR(desthost)); if (rval < 0) { if (errno != EINPROGRESS) { - /* retry using an IPv4 adress, if IPv6 didn't work */ - if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) { - shutdown(sock, 2); - close(sock); + shutdown(sock, 2); + close(sock); + /* Retry using an IPv4 adress, if IPv6 didn't work */ + if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) { prefer_ipv6 = FALSE; goto retry; } - shutdown(sock, 2); - close(sock); - /** Cannot connect to remote host */ SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno))); silc_fsm_next(fsm, silc_net_connect_st_finish); @@ -364,12 +578,12 @@ SILC_FSM_STATE(silc_net_connect_st_start) /** Wait for connection */ silc_fsm_next(fsm, silc_net_connect_st_connected); - silc_fsm_sema_init(&conn->sema, fsm, 0); + silc_fsm_event_init(&conn->event, fsm); silc_schedule_task_add_fd(silc_fsm_get_schedule(fsm), sock, silc_net_connect_wait, conn); silc_schedule_set_listen_fd(silc_fsm_get_schedule(fsm), sock, SILC_TASK_WRITE, FALSE); - SILC_FSM_SEMA_WAIT(&conn->sema); + SILC_FSM_EVENT_WAIT(&conn->event); return SILC_FSM_CONTINUE; } @@ -377,6 +591,7 @@ static void silc_net_connect_wait_stream(SilcSocketStreamStatus status, SilcStream stream, void *context) { SilcNetConnect conn = context; + conn->sop = NULL; conn->stream_status = status; conn->stream = stream; SILC_FSM_CALL_CONTINUE(&conn->fsm); @@ -390,6 +605,8 @@ SILC_FSM_STATE(silc_net_connect_st_connected) if (conn->aborted) { /** Aborted */ + silc_schedule_unset_listen_fd(schedule, conn->sock); + silc_schedule_task_del_by_fd(schedule, conn->sock); silc_fsm_next(fsm, silc_net_connect_st_finish); return SILC_FSM_CONTINUE; } @@ -397,8 +614,8 @@ SILC_FSM_STATE(silc_net_connect_st_connected) ret = silc_net_get_socket_opt(conn->sock, SOL_SOCKET, SO_ERROR, &opt, &optlen); - silc_schedule_task_del_by_fd(schedule, conn->sock); silc_schedule_unset_listen_fd(schedule, conn->sock); + silc_schedule_task_del_by_fd(schedule, conn->sock); if (ret != 0 || opt != 0) { if (conn->retry) { @@ -411,29 +628,30 @@ SILC_FSM_STATE(silc_net_connect_st_connected) } #if defined(ECONNREFUSED) - if (errno == ECONNREFUSED) + if (opt == ECONNREFUSED) conn->status = SILC_NET_CONNECTION_REFUSED; #endif /* ECONNREFUSED */ #if defined(ETIMEDOUT) - if (errno == ETIMEDOUT) + if (opt == ETIMEDOUT) conn->status = SILC_NET_CONNECTION_TIMEOUT; #endif /* ETIMEDOUT */ #if defined(ENETUNREACH) - if (errno == ENETUNREACH) + if (opt == ENETUNREACH) conn->status = SILC_NET_HOST_UNREACHABLE; #endif /* ENETUNREACH */ /** Connecting failed */ - SILC_LOG_DEBUG(("Connecting failed")); + SILC_LOG_DEBUG(("Connecting failed, error %s", strerror(opt))); silc_fsm_next(fsm, silc_net_connect_st_finish); return SILC_FSM_CONTINUE; } + SILC_LOG_DEBUG(("TCP connection established")); + /** Connection created */ silc_fsm_next(fsm, silc_net_connect_st_stream); - SILC_FSM_CALL((conn->sop = silc_socket_stream_create( - conn->sock, FALSE, FALSE, - schedule, + SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create( + conn->sock, TRUE, FALSE, schedule, silc_net_connect_wait_stream, conn))); } @@ -459,13 +677,8 @@ SILC_FSM_STATE(silc_net_connect_st_stream) return SILC_FSM_CONTINUE; } - /* Set stream information */ - silc_socket_stream_set_info(conn->stream, - !silc_net_is_ip(conn->remote) ? conn->remote : - conn->ip_addr, conn->ip_addr, conn->port); - /** Stream created successfully */ - SILC_LOG_DEBUG(("Connected successfully")); + SILC_LOG_DEBUG(("Connected successfully, sock %d", conn->sock)); conn->status = SILC_NET_OK; silc_fsm_next(fsm, silc_net_connect_st_finish); return SILC_FSM_CONTINUE; @@ -480,8 +693,6 @@ SILC_FSM_STATE(silc_net_connect_st_finish) conn->callback(conn->status, conn->stream, conn->context); if (conn->op) silc_async_free(conn->op); - if (conn->sop) - silc_async_free(conn->sop); } return SILC_FSM_FINISH; @@ -493,8 +704,10 @@ static void silc_net_connect_abort(SilcAsyncOperation op, void *context) conn->aborted = TRUE; /* Abort underlaying stream creation too */ - if (conn->sop) - silc_async_abort(conn->op, NULL, NULL); + if (conn->sop) { + silc_async_abort(conn->sop, NULL, NULL); + conn->sop = NULL; + } } static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context, @@ -532,6 +745,7 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr, /* Start async operation */ conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn); if (!conn->op) { + silc_free(conn); callback(SILC_NET_NO_MEMORY, NULL, context); return NULL; } @@ -540,6 +754,9 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr, conn->local_ip = strdup(local_ip_addr); conn->remote = strdup(remote_ip_addr); if (!conn->remote) { + silc_async_free(conn->op); + silc_free(conn->local_ip); + silc_free(conn); callback(SILC_NET_NO_MEMORY, NULL, context); return NULL; } @@ -559,14 +776,15 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr, void silc_net_close_connection(int sock) { + SILC_LOG_DEBUG(("Closing sock %d", sock)); close(sock); } /* Set's the socket to non-blocking mode. */ -int silc_net_set_socket_nonblock(int sock) +int silc_net_set_socket_nonblock(SilcSocket sock) { - return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); + return fcntl((int)sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); } /* Converts the IP number string from numbers-and-dots notation to