return meshlink_add_external_address(handle);
}
- /// Get the network port used by the local node.
- /** This function returns the network port that the local node is listening on.
- *
- * @return This function returns the port number, or -1 in case of an error.
- */
- int get_port() {
- return meshlink_get_port(handle);
- }
-
- /// Set the network port used by the local node.
- /** This function sets the network port that the local node is listening on.
- * It may only be called when the mesh is not running.
- * If unsure, call stop() before calling this function.
- * Also note that if your node is already part of a mesh with other nodes,
- * that the other nodes may no longer be able to initiate connections to the local node,
- * since they will try to connect to the previously configured port.
- *
- * @param port The port number to listen on. This must be between 0 and 65535.
- * If the port is set to 0, then MeshLink will listen on a port
- * that is randomly assigned by the operating system every time open() is called.
- *
- * @return This function returns true if the port was successfully changed
- * to the desired port, false otherwise. If it returns false, there
- * is no guarantee that MeshLink is listening on the old port.
- */
- bool set_port(int port) {
- return meshlink_set_port(handle, port);
- }
-
/// Set the scheduling granularity of the application
/** This should be set to the effective scheduling granularity for the application.
* This depends on the scheduling granularity of the operating system, the application's
*/
bool meshlink_add_external_address(struct meshlink_handle *mesh) __attribute__((__warn_unused_result__));
-/// Get the network port used by the local node.
-/** This function returns the network port that the local node is listening on.
- *
- * \memberof meshlink_handle
- * @param mesh A handle which represents an instance of MeshLink.
- *
- * @return This function returns the port number, or -1 in case of an error.
- */
-int meshlink_get_port(struct meshlink_handle *mesh) __attribute__((__warn_unused_result__));
-
-/// Set the network port used by the local node.
-/** This function sets the network port that the local node is listening on.
- * It may only be called when the mesh is not running.
- * If unsure, call meshlink_stop() before calling this function.
- * Also note that if your node is already part of a mesh with other nodes,
- * that the other nodes may no longer be able to initiate connections to the local node,
- * since they will try to connect to the previously configured port.
- *
- * Note that if a canonical address has been set for the local node,
- * you might need to call meshlink_set_canonical_address() again to ensure it includes the new port number.
- *
- * \memberof meshlink_handle
- * @param mesh A handle which represents an instance of MeshLink.
- * @param port The port number to listen on. This must be between 0 and 65535.
- * If the port is set to 0, then MeshLink will listen on a port
- * that is randomly assigned by the operating system every time meshlink_open() is called.
- *
- * @return This function returns true if the port was successfully changed
- * to the desired port, false otherwise. If it returns false, there
- * is no guarantee that MeshLink is listening on the old port.
- */
-
-bool meshlink_set_port(struct meshlink_handle *mesh, int port) __attribute__((__warn_unused_result__));
-
/** This function allows the local node to join an existing mesh using an invitation URL generated by another node.
* An invitation can only be used if the local node has never connected to other nodes before.
* After a successfully accepted invitation, the name of the local node may have changed.
return xstrdup(localaddr);
}
-static bool try_bind(meshlink_handle_t *mesh, int port) {
- struct addrinfo *ai = NULL;
- struct addrinfo hint = {
- .ai_flags = AI_PASSIVE,
- .ai_family = AF_UNSPEC,
- .ai_socktype = SOCK_STREAM,
- .ai_protocol = IPPROTO_TCP,
- };
-
- char portstr[16];
- snprintf(portstr, sizeof(portstr), "%d", port);
-
- if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai) {
- return false;
- }
-
- bool success = false;
-
- for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
- /* Try to bind to TCP. */
-
- 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;
- }
- }
-
- closesocket(tcp_fd);
- success = true;
- }
-
- freeaddrinfo(ai);
- return success;
-}
-
-int check_port(meshlink_handle_t *mesh) {
- for(int i = 0; i < 1000; i++) {
- int port = 0x1000 + prng(mesh, 0x8000);
-
- if(try_bind(mesh, port)) {
- free(mesh->myport);
- xasprintf(&mesh->myport, "%d", port);
- return port;
- }
- }
-
- meshlink_errno = MESHLINK_ENETWORK;
- logger(mesh, MESHLINK_DEBUG, "Could not find any available network port.\n");
- return 0;
-}
-
static bool write_main_config_files(meshlink_handle_t *mesh) {
if(!mesh->confbase) {
return true;
return false;
}
- if(check_port(mesh) == 0) {
- meshlink_errno = MESHLINK_ENETWORK;
- return false;
- }
+ mesh->myport = xstrdup("0");
/* Create a node for ourself */
return true;
}
- if(mesh->listen_socket[0].tcp.fd < 0) {
- logger(mesh, MESHLINK_ERROR, "Listening socket not open\n");
- meshlink_errno = MESHLINK_ENETWORK;
- return false;
- }
-
// Reset node connection timers
for splay_each(node_t, n, mesh->nodes) {
n->last_connect_try = 0;
}
- // TODO: open listening sockets first
//Check that a valid name is set
if(!mesh->name) {
return rval;
}
-int meshlink_get_port(meshlink_handle_t *mesh) {
- if(!mesh) {
- meshlink_errno = MESHLINK_EINVAL;
- return -1;
- }
-
- if(!mesh->myport) {
- meshlink_errno = MESHLINK_EINTERNAL;
- return -1;
- }
-
- int port;
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- port = atoi(mesh->myport);
- pthread_mutex_unlock(&mesh->mutex);
-
- return port;
-}
-
-bool meshlink_set_port(meshlink_handle_t *mesh, int port) {
- logger(mesh, MESHLINK_DEBUG, "meshlink_set_port(%d)", port);
-
- if(!mesh || port < 0 || port >= 65536 || mesh->threadstarted) {
- meshlink_errno = MESHLINK_EINVAL;
- return false;
- }
-
- if(mesh->myport && port == atoi(mesh->myport)) {
- return true;
- }
-
- if(!try_bind(mesh, port)) {
- meshlink_errno = MESHLINK_ENETWORK;
- return false;
- }
-
- devtool_trybind_probe();
-
- bool rval = false;
-
- if(pthread_mutex_lock(&mesh->mutex) != 0) {
- abort();
- }
-
- if(mesh->threadstarted) {
- meshlink_errno = MESHLINK_EINVAL;
- goto done;
- }
-
- free(mesh->myport);
- xasprintf(&mesh->myport, "%d", port);
-
- /* Close down the network. This also deletes mesh->self. */
- close_network_connections(mesh);
-
- /* Recreate mesh->self. */
- mesh->self = new_node();
- mesh->self->name = xstrdup(mesh->name);
- mesh->self->devclass = mesh->devclass;
- mesh->self->session_id = mesh->session_id;
- xasprintf(&mesh->myport, "%d", port);
-
- if(!node_read_public_key(mesh, mesh->self)) {
- logger(NULL, MESHLINK_ERROR, "Could not read our host configuration file!");
- meshlink_errno = MESHLINK_ESTORAGE;
- free_node(mesh->self);
- mesh->self = NULL;
- goto done;
- } else if(!setup_network(mesh)) {
- meshlink_errno = MESHLINK_ENETWORK;
- goto done;
- }
-
- /* Rebuild our own list of recent addresses */
- memset(mesh->self->recent, 0, sizeof(mesh->self->recent));
- add_local_addresses(mesh);
-
- /* Write meshlink.conf with the updated port number */
- write_main_config_files(mesh);
-
- rval = config_sync(mesh, "current");
-
-done:
- pthread_mutex_unlock(&mesh->mutex);
-
- return rval && meshlink_get_port(mesh) == port;
-}
-
bool meshlink_join(meshlink_handle_t *mesh, const char *invitation) {
logger(mesh, MESHLINK_DEBUG, "meshlink_join(%s)", invitation ? invitation : "(null)");
meshlink_get_node_reachability
meshlink_get_node_submesh
meshlink_get_pmtu
-meshlink_get_port
meshlink_get_self
meshlink_get_submesh
meshlink_hint_address
meshlink_set_node_duplicate_cb
meshlink_set_node_pmtu_cb
meshlink_set_node_status_cb
-meshlink_set_port
meshlink_set_receive_cb
meshlink_set_scheduling_granularity
meshlink_sign
#define MESHLINK_CONFIG_VERSION 2
#define MESHLINK_INVITATION_VERSION 2
-typedef struct listen_socket_t {
- struct io_t tcp;
- sockaddr_t sa;
- sockaddr_t broadcast_sa;
-} listen_socket_t;
-
struct meshlink_open_params {
char *confbase;
char *lock_filename;
// The most important network-related members come first
int reachable;
- int listen_sockets;
- listen_socket_t listen_socket[MAXSOCKETS];
meshlink_receive_cb_t receive_cb;
meshlink_queue_t outpacketqueue;
void finish_connecting(struct meshlink_handle *mesh, struct connection_t *);
void do_outgoing_connection(struct meshlink_handle *mesh, struct outgoing_t *);
void handle_new_meta_connection(struct event_loop_t *loop, void *, int);
-int setup_tcp_listen_socket(struct meshlink_handle *mesh, const struct addrinfo *aip) __attribute__((__warn_unused_result__));
bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len);
bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) __attribute__((__warn_unused_result__));
void send_packet(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *);
void send_mtu_probe(struct meshlink_handle *mesh, struct node_t *);
void handle_meta_connection_data(struct meshlink_handle *mesh, struct connection_t *);
void retry(struct meshlink_handle *mesh);
-int check_port(struct meshlink_handle *mesh);
void flush_meta(struct meshlink_handle *mesh, struct connection_t *);
#ifndef HAVE_MINGW
return true;
}
-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;
- }
-
-#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));
-
-#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;
-}
-
-/*
- 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_NUMERICSERV,
- };
-
- 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));
- return false;
- }
-
- bool success = false;
-
- for(struct addrinfo *aip = ai; aip; aip = aip->ai_next) {
- // Ignore duplicate addresses
- bool found = false;
-
- 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;
- }
-
- if(mesh->listen_sockets >= MAXSOCKETS) {
- logger(mesh, MESHLINK_ERROR, "Too many listening sockets");
- return false;
- }
-
- /* Try to bind to TCP */
-
- 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;
- }
- }
-
- 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);
-
- if(mesh->log_level <= MESHLINK_INFO) {
- char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
- logger(mesh, MESHLINK_INFO, "Listening on %s", hostname);
- free(hostname);
- }
-
- 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);
-
- if(aip->ai_family == AF_INET6) {
- mesh->listen_socket[mesh->listen_sockets].broadcast_sa.in6.sin6_addr.s6_addr[0x0] = 0xff;
- mesh->listen_socket[mesh->listen_sockets].broadcast_sa.in6.sin6_addr.s6_addr[0x1] = 0x02;
- mesh->listen_socket[mesh->listen_sockets].broadcast_sa.in6.sin6_addr.s6_addr[0xf] = 0x01;
- } else {
- mesh->listen_socket[mesh->listen_sockets].broadcast_sa.in.sin_addr.s_addr = 0xffffffff;
- }
-
- mesh->listen_sockets++;
- success = true;
- }
-
- freeaddrinfo(ai);
-
- if(!success) {
- for(int i = 0; i < mesh->listen_sockets; i++) {
- io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
- closesocket(mesh->listen_socket[i].tcp.fd);
- }
-
- mesh->listen_sockets = 0;
- }
-
- return success;
-}
-
/*
Configure node_t mesh->self and set up the local sockets (listen only)
*/
logger(mesh, MESHLINK_WARNING, "Could not scan all host config files");
}
- /* Open sockets */
-
- mesh->listen_sockets = 0;
-
- if(!add_listen_sockets(mesh)) {
- if(strcmp(mesh->myport, "0")) {
- logger(mesh, MESHLINK_WARNING, "Could not bind to port %s, trying to find an alternative port", mesh->myport);
-
- 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_sockets(mesh)) {
- return false;
- }
- } else {
- return false;
- }
- }
-
- if(!mesh->listen_sockets) {
- logger(mesh, MESHLINK_ERROR, "Unable to create any listening socket!");
- return false;
- }
-
/* Done. */
mesh->last_unreachable = mesh->loop.now.tv_sec;
}
}
- for(int i = 0; i < mesh->listen_sockets; i++) {
- io_del(&mesh->loop, &mesh->listen_socket[i].tcp);
- closesocket(mesh->listen_socket[i].tcp.fd);
- }
-
exit_requests(mesh);
exit_nodes(mesh);
exit_submeshes(mesh);
#define MSG_NOSIGNAL 0
#endif
-static const int max_connection_burst = 100;
-
/* Setup sockets */
static void configure_tcp(connection_t *c) {
do_outgoing_connection(mesh, outgoing);
}
-/// Delayed close of a filedescriptor.
-static void tarpit(meshlink_handle_t *mesh, int fd) {
- if(!fd) {
- return;
- }
-
- if(mesh->pits[mesh->next_pit]) {
- closesocket(mesh->pits[mesh->next_pit]);
- }
-
- mesh->pits[mesh->next_pit++] = fd;
-
- if(mesh->next_pit >= (int)(sizeof mesh->pits / sizeof mesh->pits[0])) {
- mesh->next_pit = 0;
- }
-}
-
-/*
- accept a new tcp connect and create a
- new connection
-*/
-void handle_new_meta_connection(event_loop_t *loop, void *data, int flags) {
- (void)flags;
- meshlink_handle_t *mesh = loop->data;
- listen_socket_t *l = data;
- connection_t *c;
- sockaddr_t sa;
- int fd;
- socklen_t len = sizeof(sa);
-
- memset(&sa, 0, sizeof(sa));
-
- fd = accept(l->tcp.fd, &sa.sa, &len);
-
- if(fd < 0) {
- if(sockwouldblock(errno)) {
- return;
- }
-
- if(errno == EINVAL) { // TODO: check if Windows agrees
- event_loop_stop(loop);
- return;
- }
-
- logger(mesh, MESHLINK_ERROR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
- return;
- }
-
- sockaddrunmap(&sa);
-
- /* Rate limit incoming connections to max_connection_burst/second. */
-
- if(mesh->loop.now.tv_sec != mesh->connection_burst_time) {
- mesh->connection_burst_time = mesh->loop.now.tv_sec;
- mesh->connection_burst = 0;
- }
-
- if(mesh->connection_burst >= max_connection_burst) {
- tarpit(mesh, fd);
- return;
- }
-
- mesh->connection_burst++;
-
- // Accept the new connection
-
- c = new_connection();
- c->name = xstrdup("<unknown>");
-
- c->address = sa;
- c->socket = fd;
- c->last_ping_time = mesh->loop.now.tv_sec;
-
- char *hostname = sockaddr2hostname(&sa);
- logger(mesh, MESHLINK_INFO, "Connection from %s", hostname);
- free(hostname);
-
- io_add(&mesh->loop, &c->io, handle_meta_io, c, c->socket, IO_READ);
-
- configure_tcp(c);
-
- connection_add(mesh, c);
-
- c->allow_request = ID;
- send_id(mesh, c);
-}
-
static void free_outgoing(outgoing_t *outgoing) {
meshlink_handle_t *mesh = outgoing->node->mesh;