X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fwin32%2Fsilcwin32net.c;h=0501ab82d12b62873471491773133528edfc6164;hb=e7b6c157b80152bf9fb9266e6bdd93f9fb0db776;hp=87fc12f84e025fcf56d533ca55016c38b0ec2bf8;hpb=d0bd38935f38aac315930baaa64993e00056ddb2;p=silc.git diff --git a/lib/silcutil/win32/silcwin32net.c b/lib/silcutil/win32/silcwin32net.c index 87fc12f8..0501ab82 100644 --- a/lib/silcutil/win32/silcwin32net.c +++ b/lib/silcutil/win32/silcwin32net.c @@ -51,22 +51,27 @@ static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr, if (ip_addr) { if (!silc_net_is_ip(ip_addr)) { SILC_LOG_ERROR(("%s is not IP address", ip_addr)); + silc_set_errno_reason(SILC_ERR_BAD_IP, "%s is not an IP address", + ip_addr); return FALSE; } if (silc_net_is_ip4(ip_addr)) { /* IPv4 address */ len = sizeof(addr->sin.sin_addr); - silc_net_addr2bin(ip_addr, - (unsigned char *)&addr->sin.sin_addr.s_addr, len); + if (!silc_net_addr2bin(ip_addr, + (unsigned char *)&addr->sin.sin_addr.s_addr, + len)) + return FALSE; addr->sin.sin_family = AF_INET; addr->sin.sin_port = port ? htons(port) : 0; } else { #ifdef HAVE_IPV6 /* IPv6 address */ len = sizeof(addr->sin6.sin6_addr); - silc_net_addr2bin(ip_addr, - (unsigned char *)&addr->sin6.sin6_addr, len); + if (!silc_net_addr2bin(ip_addr, + (unsigned char *)&addr->sin6.sin6_addr, len)) + return FALSE; addr->sin6.sin6_family = AF_INET6; addr->sin6.sin6_port = port ? htons(port) : 0; #else @@ -90,15 +95,15 @@ static SilcBool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr, /* Deliver new stream to upper layer */ -static void silc_net_accept_stream(SilcSocketStreamStatus status, +static void silc_net_accept_stream(SilcResult status, SilcStream stream, void *context) { SilcNetListener listener = context; - if (status != SILC_SOCKET_OK) + if (status != SILC_OK) return; - listener->callback(SILC_NET_OK, stream, listener->context); + listener->callback(SILC_OK, stream, listener->context); } /* Accept incoming connection and notify upper layer */ @@ -115,7 +120,6 @@ 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 */ @@ -141,8 +145,18 @@ silc_net_tcp_create_listener(const char **local_ip_addr, SILC_LOG_DEBUG(("Creating TCP listener")); - if (port < 0 || !schedule || !callback) + if (!schedule) { + schedule = silc_schedule_get_global(); + if (!schedule) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + goto err; + } + } + + if (port < 0 || !callback) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); goto err; + } listener = silc_calloc(1, sizeof(*listener)); if (!listener) @@ -179,14 +193,17 @@ silc_net_tcp_create_listener(const char **local_ip_addr, /* Create the socket */ sock = socket(server.sin.sin_family, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { - SILC_LOG_ERROR(("Cannot create socket")); + silc_set_errno_posix(WSAGetLastError()); + SILC_LOG_ERROR(("Cannot create socket, error %s", + silc_errno_string(silc_errno))); goto err; } /* Set the socket options */ rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); if (rval == SOCKET_ERROR) { - SILC_LOG_ERROR(("Cannot set socket options")); + SILC_LOG_ERROR(("Cannot set socket options, error %s", + silc_errno_string(silc_errno))); closesocket(sock); goto err; } @@ -194,7 +211,9 @@ silc_net_tcp_create_listener(const char **local_ip_addr, /* Bind the listener socket */ rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); if (rval == SOCKET_ERROR) { - SILC_LOG_ERROR(("Cannot bind socket")); + silc_set_errno_posix(WSAGetLastError()); + SILC_LOG_ERROR(("Cannot bind socket, error %s", + silc_errno_string(silc_errno))); closesocket(sock); goto err; } @@ -202,13 +221,141 @@ silc_net_tcp_create_listener(const char **local_ip_addr, /* Specify that we are listenning */ rval = listen(sock, SOMAXCONN); if (rval == SOCKET_ERROR) { - SILC_LOG_ERROR(("Cannot set socket listenning")); + silc_set_errno_posix(WSAGetLastError()); + SILC_LOG_ERROR(("Cannot set socket listenning, error %s", + silc_errno_string(silc_errno))); + closesocket(sock); + goto err; + } + + /* Schedule for incoming connections */ + silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener); + + SILC_LOG_DEBUG(("TCP listener created, fd=%d", sock)); + listener->socks[i] = sock; + listener->socks_count++; + } + + return listener; + + err: + if (listener) + silc_net_close_listener(listener); + return NULL; +} + +/* Create TCP network, multiple ports */ + +SilcNetListener +silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports, + SilcUInt32 port_count, + SilcBool ignore_port_error, + SilcBool lookup, SilcBool require_fqdn, + SilcSchedule schedule, + SilcNetCallback callback, void *context) +{ + SilcNetListener listener = NULL; + SOCKET sock; + SilcSockaddr server; + int i, rval; + const char *ipany = "0.0.0.0"; + + SILC_LOG_DEBUG(("Creating TCP listener")); + + if (!schedule) { + schedule = silc_schedule_get_global(); + if (!schedule) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + goto err; + } + } + + if (!callback) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + goto err; + } + + listener = silc_calloc(1, sizeof(*listener)); + if (!listener) + return NULL; + listener->schedule = schedule; + listener->callback = callback; + listener->context = context; + listener->require_fqdn = require_fqdn; + listener->lookup = lookup; + + if (port_count > 0) { + listener->socks = silc_calloc(port_count, sizeof(*listener->socks)); + if (!listener->socks) + return NULL; + } else { + listener->socks = silc_calloc(1, sizeof(*listener->socks)); + if (!listener->socks) + return NULL; + + port_count = 1; + } + + /* Bind to local addresses */ + for (i = 0; i < local_ip_count; i++) { + SILC_LOG_DEBUG(("Binding to local address %s:%d", + local_ip_addr ? local_ip_addr : ipany, + ports ? ports[i] : 0)); + + /* Set sockaddr for server */ + if (!silc_net_set_sockaddr(&server, + local_ip_addr ? local_ip_addr : ipany, + ports ? ports[i] : 0)) { + if (ignore_port_error) + continue; + goto err; + } + + /* Create the socket */ + sock = socket(server.sin.sin_family, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + if (ignore_port_error) + continue; + silc_set_errno_posix(WSAGetLastError()); + SILC_LOG_ERROR(("Cannot create socket, error %s", + silc_errno_string(silc_errno))); + goto err; + } + + /* Set the socket options */ + rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); + if (rval == SOCKET_ERROR) { closesocket(sock); + if (ignore_port_error) + continue; + SILC_LOG_ERROR(("Cannot set socket options, error %s", + silc_errno_string(silc_errno))); goto err; } - /* Set the server socket to non-blocking mode. Dunno if this works. */ - silc_net_set_socket_nonblock(sock); + /* Bind the listener socket */ + rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); + if (rval == SOCKET_ERROR) { + closesocket(sock); + if (ignore_port_error) + continue; + silc_set_errno_posix(WSAGetLastError()); + SILC_LOG_ERROR(("Cannot bind socket, error %s", + silc_errno_string(silc_errno))); + goto err; + } + + /* Specify that we are listenning */ + rval = listen(sock, SOMAXCONN); + if (rval == SOCKET_ERROR) { + closesocket(sock); + if (ignore_port_error) + continue; + silc_set_errno_posix(WSAGetLastError()); + SILC_LOG_ERROR(("Cannot set socket listenning, error %s", + silc_errno_string(silc_errno))); + goto err; + } /* Schedule for incoming connections */ silc_schedule_task_add_fd(schedule, sock, silc_net_accept, listener); @@ -218,6 +365,9 @@ silc_net_tcp_create_listener(const char **local_ip_addr, listener->socks_count++; } + if (ignore_port_error && !listener->socks_count) + goto err; + return listener; err: @@ -234,6 +384,9 @@ void silc_net_close_listener(SilcNetListener listener) SILC_LOG_DEBUG(("Closing network listener")); + if (!listener) + return; + for (i = 0; i < listener->socks_count; i++) { silc_schedule_task_del_by_fd(listener->schedule, listener->socks[i]); shutdown(listener->socks[i], 2); @@ -261,8 +414,13 @@ silc_net_udp_connect(const char *local_ip_addr, int local_port, SILC_LOG_DEBUG(("Creating UDP stream")); - if (!schedule) - goto err; + if (!schedule) { + schedule = silc_schedule_get_global(); + if (!schedule) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + goto err; + } + } /* Bind to local addresses */ SILC_LOG_DEBUG(("Binding to local address %s", @@ -277,6 +435,7 @@ silc_net_udp_connect(const char *local_ip_addr, int local_port, sock = socket(server.sin.sin_family, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) { SILC_LOG_ERROR(("Cannot create socket")); + silc_set_errno_posix(WSAGetLastError()); goto err; } @@ -298,12 +457,10 @@ silc_net_udp_connect(const char *local_ip_addr, int local_port, rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server)); if (rval == SOCKET_ERROR) { SILC_LOG_DEBUG(("Cannot bind socket")); + silc_set_errno_posix(WSAGetLastError()); goto err; } - /* Set socket to non-blocking mode */ - silc_net_set_socket_nonblock(sock); - /* 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)) @@ -312,6 +469,7 @@ silc_net_udp_connect(const char *local_ip_addr, int local_port, rval = connect(sock, &server.sa, SIZEOF_SOCKADDR(server)); if (rval == SOCKET_ERROR) { SILC_LOG_DEBUG(("Cannot connect UDP stream")); + silc_set_errno_posix(WSAGetLastError()); goto err; } } @@ -362,15 +520,16 @@ int silc_net_udp_receive(SilcStream stream, char *remote_ip_addr, if (len == SOCKET_ERROR) { err = WSAGetLastError(); + silc_set_errno_posix(err); if (err == WSAEWOULDBLOCK) { 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", sock->sock)); + SILC_LOG_DEBUG(("Cannot read from UDP socket: %d: %s", sock->sock, + silc_errno_string(silc_errno))); silc_schedule_unset_listen_fd(sock->schedule, sock->sock); - sock->sock_error = err; return -2; } @@ -421,15 +580,16 @@ int silc_net_udp_send(SilcStream stream, SIZEOF_SOCKADDR(remote)); if (ret == SOCKET_ERROR) { err = WSAGetLastError(); + silc_set_errno_posix(err); if (err == WSAEWOULDBLOCK) { 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_LOG_DEBUG(("Cannot send to UDP socket: %s", + silc_errno_string(silc_errno))); silc_schedule_unset_listen_fd(sock->schedule, sock->sock); - sock->sock_error = err; return -2; } @@ -446,8 +606,7 @@ int silc_net_udp_send(SilcStream stream, /******************************* TCP Stream *********************************/ typedef struct { - SilcNetStatus status; - SilcSocketStreamStatus stream_status; + SilcResult status; SilcStream stream; SilcFSMStruct fsm; SilcFSMThreadStruct thread; @@ -468,13 +627,14 @@ SILC_FSM_STATE(silc_net_connect_st_start); SILC_FSM_STATE(silc_net_connect_st_stream); SILC_FSM_STATE(silc_net_connect_st_finish); -static void silc_net_connect_wait_stream(SilcSocketStreamStatus status, +static void silc_net_connect_wait_stream(SilcResult status, SilcStream stream, void *context) { SilcNetConnect conn = context; - conn->stream_status = status; + conn->sop = NULL; + conn->status = status; conn->stream = stream; - SILC_FSM_CALL_CONTINUE(&conn->fsm); + SILC_FSM_CALL_CONTINUE(&conn->thread); } /* Start connecting. Create a real thread where we connect. */ @@ -483,7 +643,7 @@ SILC_FSM_STATE(silc_net_connect_st_thread) { SilcNetConnect conn = fsm_context; - /* Connect in real thread as as to not block the application. */ + /* Connect in real thread so as to not block the application. */ silc_fsm_thread_init(&conn->thread, fsm, conn, NULL, NULL, TRUE); silc_fsm_start(&conn->thread, silc_net_connect_st_start); @@ -510,10 +670,10 @@ SILC_FSM_STATE(silc_net_connect_st_start) if (!silc_net_gethostbyname(conn->remote, prefer_ipv6, conn->ip_addr, sizeof(conn->ip_addr))) { SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the " - "host", conn->remote)); + "host, error %d", conn->remote, WSAGetLastError())); /** Network unreachable */ - conn->status = SILC_NET_HOST_UNREACHABLE; + conn->status = SILC_ERR_UNREACHABLE; return SILC_FSM_FINISH; } @@ -532,7 +692,9 @@ SILC_FSM_STATE(silc_net_connect_st_start) } /** Cannot create socket */ - SILC_LOG_ERROR(("Cannot create socket")); + silc_set_errno_posix(err); + SILC_LOG_ERROR(("Cannot create socket, error %d", + silc_errno_string(silc_errno))); return SILC_FSM_FINISH; } @@ -553,27 +715,18 @@ SILC_FSM_STATE(silc_net_connect_st_start) shutdown(sock, 2); closesocket(sock); - /* Retry using an IPv4 adress, if IPv6 didn't work */ + /* Retry using an IPv4 address, if IPv6 didn't work */ if (prefer_ipv6 && silc_net_is_ip6(conn->ip_addr)) { prefer_ipv6 = FALSE; goto retry; } - switch (err) { - case WSAETIMEDOUT: - conn->status = SILC_NET_CONNECTION_TIMEOUT; - break; - case WSAECONNREFUSED: - conn->status = SILC_NET_CONNECTION_REFUSED; - break; - case WSAEHOSTUNREACH: - conn->status = SILC_NET_HOST_UNREACHABLE; - break; - default: - break; - } + /* Set error */ + silc_set_errno_posix(err); + conn->status = silc_errno; - SILC_LOG_ERROR(("Cannot connect to remote host")); + SILC_LOG_ERROR(("Cannot connect to remote host: %s", + silc_errno_string(silc_errno))); return SILC_FSM_FINISH; } } @@ -594,8 +747,8 @@ SILC_FSM_STATE(silc_net_connect_st_start) /** Connection created */ silc_fsm_next(fsm, silc_net_connect_st_stream); SILC_FSM_CALL((conn->sop = silc_socket_tcp_stream_create( - conn->sock, FALSE, FALSE, - silc_fsm_get_schedule(fsm), + conn->sock, TRUE, FALSE, + silc_fsm_get_schedule(&conn->fsm), silc_net_connect_wait_stream, conn))); } @@ -608,26 +761,14 @@ SILC_FSM_STATE(silc_net_connect_st_stream) if (conn->aborted) return SILC_FSM_FINISH; - if (conn->stream_status != SILC_SOCKET_OK) { + if (conn->status != SILC_OK) { /** Stream creation failed */ - if (conn->stream_status == SILC_SOCKET_UNKNOWN_IP) - conn->status = SILC_NET_UNKNOWN_IP; - else if (conn->stream_status == SILC_SOCKET_UNKNOWN_HOST) - conn->status = SILC_NET_UNKNOWN_HOST; - else - conn->status = SILC_NET_ERROR; - return SILC_FSM_FINISH; } - /* 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, sock %d", conn->sock)); - conn->status = SILC_NET_OK; + conn->status = SILC_OK; return SILC_FSM_FINISH; } @@ -640,8 +781,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; @@ -653,8 +792,10 @@ static void silc_net_connect_abort(SilcAsyncOperation op, void *context) conn->aborted = TRUE; /* Abort underlaying stream creation too */ - if (conn->sop) + if (conn->sop) { silc_async_abort(conn->sop, NULL, NULL); + conn->sop = NULL; + } } static void silc_net_connect_destructor(SilcFSM fsm, void *fsm_context, @@ -677,15 +818,25 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr, { SilcNetConnect conn; - if (!remote_ip_addr || remote_port < 1 || !schedule || !callback) + if (!schedule) { + schedule = silc_schedule_get_global(); + if (!schedule) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); + return NULL; + } + } + + if (!remote_ip_addr || remote_port < 1 || !callback) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); return NULL; + } SILC_LOG_DEBUG(("Creating connection to host %s port %d", remote_ip_addr, remote_port)); conn = silc_calloc(1, sizeof(*conn)); if (!conn) { - callback(SILC_NET_NO_MEMORY, NULL, context); + callback(SILC_ERR_OUT_OF_MEMORY, NULL, context); return NULL; } @@ -693,25 +844,25 @@ SilcAsyncOperation silc_net_tcp_connect(const char *local_ip_addr, conn->op = silc_async_alloc(silc_net_connect_abort, NULL, conn); if (!conn->op) { silc_free(conn); - callback(SILC_NET_NO_MEMORY, NULL, context); + callback(SILC_ERR_OUT_OF_MEMORY, NULL, context); return NULL; } if (local_ip_addr) - conn->local_ip = strdup(local_ip_addr); - conn->remote = strdup(remote_ip_addr); + conn->local_ip = silc_strdup(local_ip_addr); + conn->remote = silc_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); + callback(SILC_ERR_OUT_OF_MEMORY, NULL, context); return NULL; } conn->port = remote_port; conn->callback = callback; conn->context = context; conn->retry = 1; - conn->status = SILC_NET_ERROR; + conn->status = SILC_ERR; silc_fsm_init(&conn->fsm, conn, silc_net_connect_destructor, NULL, schedule); silc_fsm_start(&conn->fsm, silc_net_connect_st_thread); @@ -732,25 +883,52 @@ void silc_net_close_connection(int sock) SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len) { - unsigned long ret; - if (silc_net_is_ip4(addr)) { /* IPv4 address */ - ret = inet_addr(addr); + int i = 0, c = 0, d = 0, len = strlen(addr); + unsigned char ret[4]; + + memset(ret, 0, sizeof(ret)); + while (len-- > 0) { + if (addr[i++] == '.') { + ret[c++] = d; + d = 0; + if (c > 3) + goto err; + continue; + } + + if (!isdigit((int)addr[i - 1])) + goto err; + + d = 10 * d + addr[i - 1] - '0'; + if (d > 255) + goto err; + } + if (c != 3) + goto err; + ret[c] = d; - if (bin_len < 4) + if (bin_len < sizeof(ret)) { + silc_set_errno(SILC_ERR_OVERFLOW); return FALSE; + } + + memcpy(bin, ret, sizeof(ret)); + return TRUE; - memcpy(bin, (unsigned char *)&ret, 4); - return ret != INADDR_NONE; + err: + return FALSE; } else { #ifdef HAVE_IPV6 struct addrinfo hints, *ai; SilcSockaddr *s; /* IPv6 address */ - if (bin_len < 16) + if (bin_len < 16) { + silc_set_errno(SILC_ERR_INVALID_ARGUMENT); return FALSE; + } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; @@ -777,32 +955,3 @@ int silc_net_set_socket_nonblock(SilcSocket sock) unsigned long on = 1; return ioctlsocket(sock, FIONBIO, &on); } - -/* Init Winsock2. */ - -SilcBool silc_net_win32_init(void) -{ - int ret, sopt = SO_SYNCHRONOUS_NONALERT; - WSADATA wdata; - WORD ver = MAKEWORD(1, 1); - - ret = WSAStartup(ver, &wdata); - if (ret) - return FALSE; - - /* Allow using the SOCKET's as file descriptors so that we can poll - them with SILC Scheduler. */ - ret = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sopt, - sizeof(sopt)); - if (ret) - return FALSE; - - return TRUE; -} - -/* Uninit Winsock2 */ - -void silc_net_win32_uninit(void) -{ - WSACleanup(); -}