int mesh_start_listening(Socket *mesh_listen_socket, uint16_t mesh_listen_socket_port, SocketCreateAllocatedFunction create_allocated) { int phase = 0; struct addrinfo *resolved_address = NULL; const char *address = config_get_option_value("listen.address")->string; log_info("Opening mesh listen socket (P: %u)", mesh_listen_socket_port); resolved_address = socket_hostname_to_address(address, mesh_listen_socket_port); // Currently no IPv6 support for mesh. if(resolved_address->ai_family == AF_INET6) { log_error("Mesh gateway does not support IPv6"); goto CLEANUP; } if (resolved_address == NULL) { log_error("Could not resolve mesh listen address '%s' (P: %u): %s (%d)", address, mesh_listen_socket_port, get_errno_name(errno), errno); goto CLEANUP; } phase = 1; // Create socket. if (socket_create(mesh_listen_socket) < 0) { log_error("Could not create mesh listen socket: %s (%d)", get_errno_name(errno), errno); goto CLEANUP; } phase = 2; if (socket_open(mesh_listen_socket, resolved_address->ai_family, resolved_address->ai_socktype, resolved_address->ai_protocol) < 0) { log_error("Could not open %s mesh listen socket: %s (%d)", network_get_address_family_name(resolved_address->ai_family, false), get_errno_name(errno), errno); goto CLEANUP; } #ifndef _WIN32 /* * On Unix the SO_REUSEADDR socket option allows to rebind sockets in * CLOSE-WAIT state. this is a desired effect. On Windows SO_REUSEADDR * allows to rebind sockets in any state. This is dangerous. Therefore, * don't set SO_REUSEADDR on Windows. Sockets can be rebound in CLOSE-WAIT * state on Windows by default. */ if (socket_set_address_reuse(mesh_listen_socket, true) < 0) { log_error("Could not enable address-reuse mode for mesh listen socket: %s (%d)", get_errno_name(errno), errno); goto CLEANUP; } #endif // Bind socket and start to listen. if (socket_bind(mesh_listen_socket, resolved_address->ai_addr, resolved_address->ai_addrlen) < 0) { log_error("Could not bind %s mesh listen socket to (A: %s, P: %u): %s (%d)", network_get_address_family_name(resolved_address->ai_family, true), address, mesh_listen_socket_port, get_errno_name(errno), errno); goto CLEANUP; } if (socket_listen(mesh_listen_socket, 10, create_allocated) < 0) { log_error("Could not listen to %s mesh socket (A: %s, P: %u): %s (%d)", network_get_address_family_name(resolved_address->ai_family, true), address, mesh_listen_socket_port, get_errno_name(errno), errno); goto CLEANUP; } log_info("Mesh gateway started listening on (A: %s, P: %u, F: %s)", address, mesh_listen_socket_port, network_get_address_family_name(resolved_address->ai_family, true)); if(event_add_source(mesh_listen_socket->base.handle, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, mesh_handle_accept, mesh_listen_socket) < 0) { goto CLEANUP; } phase = 3; freeaddrinfo(resolved_address); CLEANUP: switch(phase) { // No breaks, all cases fall through intentionally. case 2: socket_destroy(mesh_listen_socket); case 1: freeaddrinfo(resolved_address); default: break; } return phase == 3 ? 0 : -1; }
static int network_open_server_socket(Socket *server_socket, uint16_t port, SocketCreateAllocatedFunction create_allocated) { int phase = 0; const char *address = config_get_option_value("listen.address")->string; struct addrinfo *resolved_address = NULL; bool dual_stack; log_debug("Opening server socket on port %u", port); // resolve listen address // FIXME: bind to all returned addresses, instead of just the first one. // requires special handling if IPv4 and IPv6 addresses are returned // and dual-stack mode is enabled resolved_address = socket_hostname_to_address(address, port); if (resolved_address == NULL) { log_error("Could not resolve listen address '%s' (port: %u): %s (%d)", address, port, get_errno_name(errno), errno); goto cleanup; } phase = 1; // create socket if (socket_create(server_socket) < 0) { log_error("Could not create socket: %s (%d)", get_errno_name(errno), errno); goto cleanup; } phase = 2; if (socket_open(server_socket, resolved_address->ai_family, resolved_address->ai_socktype, resolved_address->ai_protocol) < 0) { log_error("Could not open %s server socket: %s (%d)", network_get_address_family_name(resolved_address->ai_family, false), get_errno_name(errno), errno); goto cleanup; } if (resolved_address->ai_family == AF_INET6) { dual_stack = config_get_option_value("listen.dual_stack")->boolean; if (socket_set_dual_stack(server_socket, dual_stack) < 0) { log_error("Could not %s dual-stack mode for IPv6 server socket: %s (%d)", dual_stack ? "enable" : "disable", get_errno_name(errno), errno); goto cleanup; } } #ifndef _WIN32 // on Unix the SO_REUSEADDR socket option allows to rebind sockets in // CLOSE-WAIT state. this is a desired effect. on Windows SO_REUSEADDR // allows to rebind sockets in any state. this is dangerous. therefore, // don't set SO_REUSEADDR on Windows. sockets can be rebound in CLOSE-WAIT // state on Windows by default. if (socket_set_address_reuse(server_socket, true) < 0) { log_error("Could not enable address-reuse mode for server socket: %s (%d)", get_errno_name(errno), errno); goto cleanup; } #endif // bind socket and start to listen if (socket_bind(server_socket, resolved_address->ai_addr, resolved_address->ai_addrlen) < 0) { log_error("Could not bind %s server socket to '%s' on port %u: %s (%d)", network_get_address_family_name(resolved_address->ai_family, true), address, port, get_errno_name(errno), errno); goto cleanup; } if (socket_listen(server_socket, 10, create_allocated) < 0) { log_error("Could not listen to %s server socket bound to '%s' on port %u: %s (%d)", network_get_address_family_name(resolved_address->ai_family, true), address, port, get_errno_name(errno), errno); goto cleanup; } log_debug("Started listening to '%s' (%s) on port %u", address, network_get_address_family_name(resolved_address->ai_family, true), port); if (event_add_source(server_socket->base.handle, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, network_handle_accept, server_socket) < 0) { goto cleanup; } phase = 3; freeaddrinfo(resolved_address); cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 2: socket_destroy(server_socket); case 1: freeaddrinfo(resolved_address); default: break; } return phase == 3 ? 0 : -1; }