return true;
}
-/*
- Add listening sockets.
-*/
-static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bindto) {
- char *port = mesh->myport;
+int setup_tcp_listen_socket(meshlink_handle_t *mesh, const struct addrinfo *aip) {
+ int nfd = socket(aip->ai_family, SOCK_STREAM, IPPROTO_TCP);
+
+ if(nfd == -1) {
+ return -1;
+ }
- if(address) {
- char *space = strchr(address, ' ');
+#ifdef FD_CLOEXEC
+ fcntl(nfd, F_SETFD, FD_CLOEXEC);
+#endif
- if(space) {
- *space++ = 0;
- port = space;
- }
+ int option = 1;
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
- if(!strcmp(address, "*")) {
- *address = 0;
- }
+#if defined(IPV6_V6ONLY)
+
+ if(aip->ai_family == AF_INET6) {
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
+ }
+
+#else
+#warning IPV6_V6ONLY not defined
+#endif
+
+ if(bind(nfd, aip->ai_addr, aip->ai_addrlen)) {
+ closesocket(nfd);
+ return -1;
+ }
+
+ if(listen(nfd, 3)) {
+ logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
+ closesocket(nfd);
+ return -1;
+ }
+
+ return nfd;
+}
+
+int setup_udp_listen_socket(meshlink_handle_t *mesh, const struct addrinfo *aip) {
+ int nfd = socket(aip->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+
+ if(nfd == -1) {
+ return -1;
+ }
+
+#ifdef FD_CLOEXEC
+ fcntl(nfd, F_SETFD, FD_CLOEXEC);
+#endif
+
+#ifdef O_NONBLOCK
+ int flags = fcntl(nfd, F_GETFL);
+
+ if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
+ closesocket(nfd);
+ logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "fcntl", strerror(errno));
+ return -1;
+ }
+
+#elif defined(WIN32)
+ unsigned long arg = 1;
+
+ if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
+ closesocket(nfd);
+ logger(mesh, MESHLINK_ERROR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
+ return -1;
+ }
+
+#endif
+
+ int option = 1;
+ setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
+ setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof(option));
+
+#if defined(IPV6_V6ONLY)
+
+ if(aip->ai_family == AF_INET6) {
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
+ }
+
+#endif
+
+#if defined(IP_DONTFRAG) && !defined(IP_DONTFRAGMENT)
+#define IP_DONTFRAGMENT IP_DONTFRAG
+#endif
+
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
+ option = IP_PMTUDISC_DO;
+ setsockopt(nfd, IPPROTO_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
+#elif defined(IP_DONTFRAGMENT)
+ option = 1;
+ setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, (void *)&option, sizeof(option));
+#endif
+
+ if(aip->ai_family == AF_INET6) {
+#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
+ option = IPV6_PMTUDISC_DO;
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
+#elif defined(IPV6_DONTFRAG)
+ option = 1;
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
+#endif
+ }
+
+ if(bind(nfd, aip->ai_addr, aip->ai_addrlen)) {
+ closesocket(nfd);
+ return -1;
}
+ return nfd;
+}
+
+/*
+ Add listening sockets.
+*/
+static bool add_listen_sockets(meshlink_handle_t *mesh) {
struct addrinfo *ai;
struct addrinfo hint = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
- .ai_flags = AI_PASSIVE,
+ .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
};
- int err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
-
- free(address);
+ int err = getaddrinfo(NULL, mesh->myport, &hint, &ai);
if(err || !ai) {
logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "getaddrinfo", err == EAI_SYSTEM ? strerror(err) : gai_strerror(err));
// Ignore duplicate addresses
bool found = false;
- for(int i = 0; i < mesh->listen_sockets; i++)
+ for(int i = 0; i < mesh->listen_sockets; i++) {
if(!memcmp(&mesh->listen_socket[i].sa, aip->ai_addr, aip->ai_addrlen)) {
found = true;
break;
}
+ }
if(found) {
continue;
return false;
}
- int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
+ /* Try to bind to TCP */
- if(tcp_fd < 0) {
- continue;
+ int tcp_fd = setup_tcp_listen_socket(mesh, aip);
+
+ if(tcp_fd == -1) {
+ if(errno == EADDRINUSE) {
+ /* If this port is in use for any address family, avoid it. */
+ success = false;
+ break;
+ } else {
+ continue;
+ }
}
- int udp_fd = setup_vpn_in_socket(mesh, (sockaddr_t *) aip->ai_addr);
+ /* If TCP worked, then we require that UDP works as well. */
- if(udp_fd < 0) {
- close(tcp_fd);
- continue;
+ int udp_fd = setup_udp_listen_socket(mesh, aip);
+
+ if(udp_fd == -1) {
+ closesocket(tcp_fd);
+ success = false;
+ break;
}
io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].tcp, handle_new_meta_connection, &mesh->listen_socket[mesh->listen_sockets], tcp_fd, IO_READ);
io_add(&mesh->loop, &mesh->listen_socket[mesh->listen_sockets].udp, handle_incoming_vpn_data, &mesh->listen_socket[mesh->listen_sockets], udp_fd, IO_READ);
- if(mesh->log_level >= MESHLINK_INFO) {
+ if(mesh->log_level <= MESHLINK_INFO) {
char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
logger(mesh, MESHLINK_INFO, "Listening on %s", hostname);
free(hostname);
}
- mesh->listen_socket[mesh->listen_sockets].bindto = bindto;
memcpy(&mesh->listen_socket[mesh->listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
memcpy(&mesh->listen_socket[mesh->listen_sockets].broadcast_sa, aip->ai_addr, aip->ai_addrlen);
}
freeaddrinfo(ai);
+
+ if(!success) {
+ for(int i = 0; i < mesh->listen_sockets; i++) {
+ io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
+ io_del(&mesh->loop, &mesh->listen_socket[i].udp);
+ close(mesh->listen_socket[i].tcp.fd);
+ close(mesh->listen_socket[i].udp.fd);
+ }
+
+ mesh->listen_sockets = 0;
+ }
+
return success;
}
/* Done */
mesh->self->nexthop = mesh->self;
- mesh->self->status.reachable = true;
node_add(mesh, mesh->self);
- graph(mesh);
-
if(!config_scan_all(mesh, "current", "hosts", load_node, NULL)) {
logger(mesh, MESHLINK_WARNING, "Could not scan all host config files");
}
mesh->listen_sockets = 0;
- if(!add_listen_address(mesh, NULL, NULL)) {
+ if(!add_listen_sockets(mesh)) {
if(strcmp(mesh->myport, "0")) {
- logger(mesh, MESHLINK_INFO, "Could not bind to port %s, asking OS to choose one for us", mesh->myport);
- free(mesh->myport);
- mesh->myport = strdup("0");
+ logger(mesh, MESHLINK_WARNING, "Could not bind to port %s, trying to find an alternative port", mesh->myport);
- if(!mesh->myport) {
- return false;
+ if(!check_port(mesh)) {
+ logger(mesh, MESHLINK_WARNING, "Could not bind to any port, trying to bind to port 0");
+ free(mesh->myport);
+ mesh->myport = xstrdup("0");
}
- if(!add_listen_address(mesh, NULL, NULL)) {
+ if(!add_listen_sockets(mesh)) {
return false;
}
} else {
/* Done. */
- mesh->last_config_check = mesh->loop.now.tv_sec;
+ mesh->last_unreachable = mesh->loop.now.tv_sec;
return true;
}