return success;
}
-static int check_port(meshlink_handle_t *mesh) {
+int check_port(meshlink_handle_t *mesh) {
for(int i = 0; i < 1000; i++) {
int port = 0x1000 + prng(mesh, 0x8000);
struct io_t udp;
sockaddr_t sa;
sockaddr_t broadcast_sa;
- bool bindto;
} listen_socket_t;
typedef enum proxytype_t {
extern void finish_connecting(struct meshlink_handle *mesh, struct connection_t *);
extern void do_outgoing_connection(struct meshlink_handle *mesh, struct outgoing_t *);
extern void handle_new_meta_connection(struct event_loop_t *loop, void *, int);
-extern int setup_listen_socket(const sockaddr_t *) __attribute__((__warn_unused_result__));
-extern int setup_vpn_in_socket(struct meshlink_handle *mesh, const sockaddr_t *) __attribute__((__warn_unused_result__));
+extern bool setup_listen_socket(struct meshlink_handle *mesh, int fd, int sa_family) __attribute__((__warn_unused_result__));
+extern bool setup_vpn_in_socket(struct meshlink_handle *mesh, int fd, int sa_family) __attribute__((__warn_unused_result__));
extern bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len);
extern bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) __attribute__((__warn_unused_result__));
extern void send_packet(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *);
extern void send_mtu_probe(struct meshlink_handle *mesh, struct node_t *);
extern void handle_meta_connection_data(struct meshlink_handle *mesh, struct connection_t *);
extern void retry(struct meshlink_handle *mesh);
+extern int check_port(struct meshlink_handle *mesh);
#ifndef HAVE_MINGW
#define closesocket(s) close(s)
/*
Add listening sockets.
*/
-static bool add_listen_address(meshlink_handle_t *mesh, char *address, bool bindto) {
- char *port = mesh->myport;
-
- if(address) {
- char *space = strchr(address, ' ');
-
- if(space) {
- *space++ = 0;
- port = space;
- }
-
- if(!strcmp(address, "*")) {
- *address = 0;
- }
- }
-
+static bool add_listen_sockets(meshlink_handle_t *mesh) {
struct addrinfo *ai;
struct addrinfo hint = {
.ai_flags = AI_PASSIVE,
};
- 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 */
+
+ int tcp_fd = socket(aip->ai_family, SOCK_STREAM, IPPROTO_TCP);
- if(tcp_fd < 0) {
+ if(tcp_fd == -1) {
continue;
}
- int udp_fd = setup_vpn_in_socket(mesh, (sockaddr_t *) aip->ai_addr);
+ if(bind(tcp_fd, aip->ai_addr, aip->ai_addrlen)) {
+ closesocket(tcp_fd);
- if(udp_fd < 0) {
- close(tcp_fd);
- continue;
+ if(errno == EADDRINUSE) {
+ /* If this port is in use for any address family, avoid it. */
+ success = false;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if(!setup_listen_socket(mesh, tcp_fd, aip->ai_family)) {
+ closesocket(tcp_fd);
+ success = false;
+ break;
+ }
+
+ /* If TCP worked, then we require that UDP works as well. */
+
+ int udp_fd = socket(aip->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+
+ if(udp_fd == -1) {
+ closesocket(tcp_fd);
+ success = false;
+ break;
+ }
+
+ if(bind(udp_fd, aip->ai_addr, aip->ai_addrlen) || !setup_vpn_in_socket(mesh, udp_fd, aip->ai_family)) {
+ closesocket(tcp_fd);
+ closesocket(udp_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);
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;
}
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 {
#endif
}
-static bool bind_to_address(meshlink_handle_t *mesh, connection_t *c) {
- int s = -1;
-
- for(int i = 0; i < mesh->listen_sockets && mesh->listen_socket[i].bindto; i++) {
- if(mesh->listen_socket[i].sa.sa.sa_family != c->address.sa.sa_family) {
- continue;
- }
-
- if(s >= 0) {
- return false;
- }
-
- s = i;
- }
-
- if(s < 0) {
- return false;
- }
-
- sockaddr_t sa = mesh->listen_socket[s].sa;
-
- if(sa.sa.sa_family == AF_INET) {
- sa.in.sin_port = 0;
- } else if(sa.sa.sa_family == AF_INET6) {
- sa.in6.sin6_port = 0;
- }
-
- if(bind(c->socket, &sa.sa, SALEN(sa.sa))) {
- logger(mesh, MESHLINK_WARNING, "Can't bind outgoing socket: %s", strerror(errno));
- return false;
- }
-
- return true;
-}
-
-int setup_listen_socket(const sockaddr_t *sa) {
- int nfd;
- char *addrstr;
- int option;
-
- nfd = socket(sa->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
-
- if(nfd < 0) {
- logger(NULL, MESHLINK_ERROR, "Creating metasocket failed: %s", sockstrerror(sockerrno));
- return -1;
- }
-
+bool setup_listen_socket(meshlink_handle_t *mesh, int nfd, int sa_family) {
#ifdef FD_CLOEXEC
fcntl(nfd, F_SETFD, FD_CLOEXEC);
#endif
- /* Optimize TCP settings */
-
- option = 1;
+ int option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
#if defined(IPV6_V6ONLY)
- if(sa->sa.sa_family == AF_INET6) {
+ if(sa_family == AF_INET6) {
setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
}
#warning IPV6_V6ONLY not defined
#endif
- if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
- closesocket(nfd);
- addrstr = sockaddr2hostname(sa);
- logger(NULL, MESHLINK_ERROR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
- free(addrstr);
- return -1;
- }
-
if(listen(nfd, 3)) {
- closesocket(nfd);
- logger(NULL, MESHLINK_ERROR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
- return -1;
+ logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "listen", sockstrerror(sockerrno));
+ return false;
}
- return nfd;
+ return true;
}
-int setup_vpn_in_socket(meshlink_handle_t *mesh, const sockaddr_t *sa) {
- int nfd;
- char *addrstr;
- int option;
-
- nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
-
- if(nfd < 0) {
- logger(mesh, MESHLINK_ERROR, "Creating UDP socket failed: %s", sockstrerror(sockerrno));
- return -1;
- }
-
+bool setup_vpn_in_socket(meshlink_handle_t *mesh, int nfd, int sa_family) {
#ifdef FD_CLOEXEC
fcntl(nfd, F_SETFD, FD_CLOEXEC);
#endif
closesocket(nfd);
logger(mesh, MESHLINK_ERROR, "System call `%s' failed: %s", "fcntl",
strerror(errno));
- return -1;
+ return false;
}
}
#elif defined(WIN32)
if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
closesocket(nfd);
logger(mesh, MESHLINK_ERROR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
- return -1;
+ return false;
}
}
#endif
- option = 1;
+ 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(sa->sa.sa_family == AF_INET6) {
+ if(sa_family == AF_INET6) {
setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
}
setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
#endif
- if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
- closesocket(nfd);
- addrstr = sockaddr2hostname(sa);
- logger(mesh, MESHLINK_ERROR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
- free(addrstr);
- return -1;
- }
-
- return nfd;
+ return true;
} /* int setup_vpn_in_socket */
static void retry_outgoing_handler(event_loop_t *loop, void *data) {
#endif
- bind_to_address(mesh, c);
-
/* Connect */
if(!mesh->proxytype) {