#include "utils.h"
#include "xalloc.h"
#include "random.h"
+#include "pidfile.h"
char controlcookie[65];
randomize(controlcookie, sizeof(controlcookie) / 2);
bin2hex(controlcookie, controlcookie, sizeof(controlcookie) / 2);
- mode_t mask = umask(0);
- umask(mask | 077);
- FILE *f = fopen(pidfilename, "w");
- umask(mask);
-
- if(!f) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Cannot write control socket cookie file %s: %s", pidfilename, strerror(errno));
- return false;
- }
-
// Get the address and port of the first listening socket
char *localhost = NULL;
// Make sure we have a valid address, and map 0.0.0.0 and :: to 127.0.0.1 and ::1.
if(getsockname(listen_socket[0].tcp.fd, &sa.sa, &len)) {
- xasprintf(&localhost, "127.0.0.1 port %s", myport);
+ xasprintf(&localhost, "127.0.0.1 port %s", myport.tcp);
} else {
if(sa.sa.sa_family == AF_INET) {
if(sa.in.sin_addr.s_addr == 0) {
localhost = sockaddr2hostname(&sa);
}
- fprintf(f, "%d %s %s\n", (int)getpid(), controlcookie, localhost);
-
+ bool wrote = write_pidfile(controlcookie, localhost);
free(localhost);
- fclose(f);
+
+ if(!wrote) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Cannot write control socket cookie file %s: %s", pidfilename, strerror(errno));
+ return false;
+ }
#ifndef HAVE_WINDOWS
int unix_fd = socket(AF_UNIX, SOCK_STREAM, 0);
unlink(unixsocketname);
+ mode_t mask = umask(0);
umask(mask | 077);
int result = bind(unix_fd, (struct sockaddr *)&sa_un, sizeof(sa_un));
umask(mask);
#include "utils.h"
#include "xalloc.h"
#include "random.h"
+#include "pidfile.h"
#include "ed25519/sha512.h"
p[strcspn(p, "\t ")] = 0;
if(!*port && !strcasecmp(line, "Port")) {
+ free(*port);
*port = xstrdup(q);
} else if(!*hostname && !strcasecmp(line, "Address")) {
+ free(*hostname);
*hostname = xstrdup(q);
if(*p) {
fclose(f);
}
-static char *get_my_hostname(void) {
+static bool get_my_hostname(char **out_address, char **out_port) {
char *hostname = NULL;
char *port = NULL;
char *hostport = NULL;
free(name);
name = NULL;
+ if(!port || (is_decimal(port) && atoi(port) == 0)) {
+ pidfile_t *pidfile = read_pidfile();
+
+ if(pidfile) {
+ free(port);
+ port = xstrdup(pidfile->port);
+ free(pidfile);
+ } else {
+ fprintf(stderr, "tincd is using a dynamic port and is not running. Please start tincd or set the Port option to a non-zero value.\n");
+ goto exit;
+ }
+ }
+
if(hostname) {
goto done;
}
if(!hostname) {
fprintf(stderr, "Could not determine the external address or hostname. Please set Address manually.\n");
free(port);
- return NULL;
+ return false;
}
goto save;
fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
free(hostname);
free(port);
- return NULL;
+ return false;
}
if(!rstrip(line)) {
}
}
+exit:
free(hostname);
+
+ if(hostport && port) {
+ *out_address = hostport;
+ *out_port = port;
+ return true;
+ }
+
+ free(hostport);
free(port);
- return hostport;
+ return false;
}
-static bool fcopy(FILE *out, const char *filename) {
+// Copy host configuration file, replacing Port with the value passed here. Host
+// configs may contain this clause: `Port = 0`, which means 'ask the operating
+// system to allocate any available port'. This obviously won't do for invitation
+// files, so replace it with an actual port we've obtained previously.
+static bool copy_config_replacing_port(FILE *out, const char *filename, const char *port) {
FILE *in = fopen(filename, "r");
if(!in) {
return false;
}
- char buf[1024];
- size_t len;
+ char line[1024];
+
+ while(fgets(line, sizeof(line), in)) {
+ const char *var_beg = line + strspn(line, "\t ");
+ const char *var_end = var_beg + strcspn(var_beg, "\t ");
- while((len = fread(buf, 1, sizeof(buf), in))) {
- fwrite(buf, len, 1, out);
+ // Check the name of the variable we've read. If it's Port, replace it with
+ // a port we'll use in invitation URL. Otherwise, just copy the line.
+ if(var_end > var_beg && !strncasecmp(var_beg, "Port", var_end - var_beg)) {
+ fprintf(out, "Port = %s\n", port);
+ } else {
+ fprintf(out, "%s", line);
+ }
}
fclose(in);
return true;
}
+static bool append_host_config(FILE *f, const char *nodename, const char *port) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s" SLASH "hosts" SLASH "%s", confbase, nodename);
+ bool success = copy_config_replacing_port(f, path, port);
+ fclose(f);
+ return success;
+}
+
int cmd_invite(int argc, char *argv[]) {
if(argc < 2) {
fprintf(stderr, "Not enough arguments!\n");
}
// Get the local address
- char *address = get_my_hostname();
+ char *address = NULL;
+ char *port = NULL;
+
+ if(!get_my_hostname(&address, &port)) {
+ return 1;
+ }
// Fill in the details.
fprintf(f, "Name = %s\n", argv[1]);
fprintf(f, "#---------------------------------------------------------------#\n");
fprintf(f, "Name = %s\n", myname);
- char filename2[PATH_MAX];
- snprintf(filename2, sizeof(filename2), "%s" SLASH "hosts" SLASH "%s", confbase, myname);
- fcopy(f, filename2);
- fclose(f);
+ bool appended = append_host_config(f, myname, port);
+ free(port);
+
+ if(!appended) {
+ fprintf(stderr, "Could not append my config to invitation file: %s.\n", strerror(errno));
+ free(address);
+ return 1;
+ }
// Create an URL from the local address, key hash and cookie
char *url;
}
if(!netname) {
- netname = xstrdup(grep(data, "NetName"));
+ const char *net = grep(data, "NetName");
- if(netname && !check_netname(netname, true)) {
- fprintf(stderr, "Unsafe NetName found in invitation!\n");
- return false;
+ if(net) {
+ netname = xstrdup(net);
+
+ if(!check_netname(netname, true)) {
+ fprintf(stderr, "Unsafe NetName found in invitation!\n");
+ return false;
+ }
}
}
'logger.c',
'names.c',
'netutl.c',
+ 'pidfile.c',
'script.c',
'splay_tree.c',
'sptps.c',
timeout_t ev;
} outgoing_t;
+typedef struct ports_t {
+ char *tcp;
+ char *udp;
+} ports_t;
+
extern list_t outgoing_list;
extern int maxoutbufsize;
extern int max_connection_burst;
extern int fwmark;
extern bool do_prune;
-extern char *myport;
+extern ports_t myport;
extern bool device_standby;
extern bool autoconnect;
extern bool disablebuggypeers;
#include "upnp.h"
#endif
-char *myport;
+ports_t myport;
static io_t device_io;
devops_t devops;
bool device_standby = false;
return true;
}
+// Get the port that `from_fd` is listening on, and assign it to
+// `sa` if `sa` has a dynamically allocated (zero) port.
+static bool assign_static_port(sockaddr_t *sa, int from_fd) {
+ // We cannot get a port from a bad FD. Bail out.
+ if(from_fd <= 0) {
+ return false;
+ }
+
+ int port = get_bound_port(from_fd);
+
+ if(!port) {
+ return false;
+ }
+
+ // If the port is non-zero, don't reassign it as it's already static.
+ switch(sa->sa.sa_family) {
+ case AF_INET:
+ if(!sa->in.sin_port) {
+ sa->in.sin_port = htons(port);
+ }
+
+ return true;
+
+ case AF_INET6:
+ if(!sa->in6.sin6_port) {
+ sa->in6.sin6_port = htons(port);
+ }
+
+ return true;
+
+ default:
+ logger(DEBUG_ALWAYS, LOG_ERR, "Unknown address family 0x%x", sa->sa.sa_family);
+ return false;
+ }
+}
+
+typedef int (*bind_fn_t)(const sockaddr_t *);
+
+static int bind_reusing_port(const sockaddr_t *sa, int from_fd, bind_fn_t setup) {
+ sockaddr_t reuse_sa;
+ memcpy(&reuse_sa, sa, SALEN(sa->sa));
+
+ int fd = -1;
+
+ // Check if the address we've been passed here is using port 0.
+ // If it is, try to get an actual port from an already bound socket, and reuse it here.
+ if(assign_static_port(&reuse_sa, from_fd)) {
+ fd = setup(&reuse_sa);
+ }
+
+ // If we're binding to a hardcoded non-zero port, or no socket is listening yet,
+ // or binding failed, try the original address.
+ if(fd < 0) {
+ fd = setup(sa);
+ }
+
+ return fd;
+}
+
/*
Add listening sockets.
*/
static bool add_listen_address(char *address, bool bindto) {
- char *port = myport;
+ char *port = myport.tcp;
if(address) {
char *space = strchr(address, ' ');
return false;
}
- int tcp_fd = setup_listen_socket((sockaddr_t *) aip->ai_addr);
+ const sockaddr_t *sa = (sockaddr_t *) aip->ai_addr;
+ int from_fd = listen_socket[0].tcp.fd;
+
+ // If we're binding to a dynamically allocated (zero) port, try to get the actual
+ // port of the first TCP socket, and use it for this one. If that succeeds, our
+ // tincd instance will use the same port for all addresses it listens on.
+ int tcp_fd = bind_reusing_port(sa, from_fd, setup_listen_socket);
if(tcp_fd < 0) {
continue;
}
- int udp_fd = setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
+ // If we just successfully bound the first socket, use it for the UDP procedure below.
+ // Otherwise, keep using the socket we've obtained from listen_socket[0].
+ if(!from_fd) {
+ from_fd = tcp_fd;
+ }
+
+ int udp_fd = bind_reusing_port(sa, from_fd, setup_vpn_in_socket);
if(udp_fd < 0) {
closesocket(tcp_fd);
continue;
}
- io_add(&listen_socket[listen_sockets].tcp, handle_new_meta_connection, &listen_socket[listen_sockets], tcp_fd, IO_READ);
- io_add(&listen_socket[listen_sockets].udp, handle_incoming_vpn_data, &listen_socket[listen_sockets], udp_fd, IO_READ);
+ listen_socket_t *sock = &listen_socket[listen_sockets];
+ io_add(&sock->tcp, handle_new_meta_connection, sock, tcp_fd, IO_READ);
+ io_add(&sock->udp, handle_incoming_vpn_data, sock, udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
- char *hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
- logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
+ int tcp_port = get_bound_port(tcp_fd);
+ char *hostname = NULL;
+ sockaddr2str(sa, &hostname, NULL);
+ logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s port %d", hostname, tcp_port);
free(hostname);
}
- listen_socket[listen_sockets].bindto = bindto;
- memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
+ sock->bindto = bindto;
+ memcpy(&sock->sa, aip->ai_addr, aip->ai_addrlen);
listen_sockets++;
}
Configure node_t myself and set up the local sockets (listen only)
*/
static bool setup_myself(void) {
- char *name, *hostname, *type;
+ char *name, *type;
char *address = NULL;
bool port_specified = false;
myself->connection->name = xstrdup(name);
read_host_config(&config_tree, name, true);
- if(!get_config_string(lookup_config(&config_tree, "Port"), &myport)) {
- myport = xstrdup("655");
+ if(!get_config_string(lookup_config(&config_tree, "Port"), &myport.tcp)) {
+ myport.tcp = xstrdup("655");
} else {
port_specified = true;
}
+ myport.udp = xstrdup(myport.tcp);
+
myself->connection->options = 0;
myself->connection->protocol_major = PROT_MAJOR;
myself->connection->protocol_minor = PROT_MINOR;
#endif
/* Ensure myport is numeric */
+ if(!is_decimal(myport.tcp)) {
+ uint16_t port = service_to_port(myport.tcp);
- if(!atoi(myport)) {
- struct addrinfo *ai = str2addrinfo("localhost", myport, SOCK_DGRAM);
- sockaddr_t sa;
-
- if(!ai || !ai->ai_addr) {
+ if(!port) {
return false;
}
- free(myport);
- memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
- freeaddrinfo(ai);
- sockaddr2str(&sa, NULL, &myport);
+ free(myport.tcp);
+ myport.tcp = int_to_str(port);
+
+ free(myport.udp);
+ myport.udp = xstrdup(myport.tcp);
}
/* Read in all the subnets specified in the host configuration file */
}
for(int i = 0; i < listen_sockets; i++) {
+ const int tcp_fd = i + 3;
salen = sizeof(sa);
- if(getsockname(i + 3, &sa.sa, &salen) < 0) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(sockerrno));
+ if(getsockname(tcp_fd, &sa.sa, &salen) < 0) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not get address of listen fd %d: %s", tcp_fd, sockstrerror(sockerrno));
return false;
}
#ifdef FD_CLOEXEC
- fcntl(i + 3, F_SETFD, FD_CLOEXEC);
+ fcntl(tcp_fd, F_SETFD, FD_CLOEXEC);
#endif
int udp_fd = setup_vpn_in_socket(&sa);
return false;
}
- io_add(&listen_socket[i].tcp, (io_cb_t)handle_new_meta_connection, &listen_socket[i], i + 3, IO_READ);
+ io_add(&listen_socket[i].tcp, (io_cb_t)handle_new_meta_connection, &listen_socket[i], tcp_fd, IO_READ);
io_add(&listen_socket[i].udp, (io_cb_t)handle_incoming_vpn_data, &listen_socket[i], udp_fd, IO_READ);
if(debug_level >= DEBUG_CONNECTIONS) {
- hostname = sockaddr2hostname(&sa);
+ char *hostname = sockaddr2hostname(&sa);
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Listening on %s", hostname);
free(hostname);
}
/* If no Port option was specified, set myport to the port used by the first listening socket. */
- if(!port_specified || atoi(myport) == 0) {
- sockaddr_t sa;
- socklen_t salen = sizeof(sa);
+ if(!port_specified || atoi(myport.tcp) == 0) {
+ listen_socket_t *sock = &listen_socket[0];
- if(!getsockname(listen_socket[0].udp.fd, &sa.sa, &salen)) {
- free(myport);
- sockaddr2str(&sa, NULL, &myport);
+ uint16_t tcp = get_bound_port(sock->tcp.fd);
+ free(myport.tcp);
+ myport.tcp = int_to_str(tcp);
- if(!myport) {
- myport = xstrdup("655");
- }
- }
+ uint16_t udp = get_bound_port(sock->udp.fd);
+ free(myport.udp);
+ myport.udp = int_to_str(udp);
}
- xasprintf(&myself->hostname, "MYSELF port %s", myport);
+ xasprintf(&myself->hostname, "MYSELF port %s", myport.tcp);
myself->connection->hostname = xstrdup(myself->hostname);
char *upnp = NULL;
device_disable();
}
- free(myport);
+ free(myport.tcp);
+ free(myport.udp);
if(device_fd >= 0) {
io_del(&device_io);
return true;
}
+static bool try_bind(int nfd, const sockaddr_t *sa, const char *type) {
+ if(!bind(nfd, &sa->sa, SALEN(sa->sa))) {
+ return true;
+ }
+
+ closesocket(nfd);
+ char *addrstr = sockaddr2hostname(sa);
+ logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/%s: %s", addrstr, type, sockstrerror(sockerrno));
+ free(addrstr);
+ return false;
+}
+
int setup_listen_socket(const sockaddr_t *sa) {
int nfd;
- char *addrstr;
int option;
char *iface;
#endif
}
- if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
- closesocket(nfd);
- addrstr = sockaddr2hostname(sa);
- logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
- free(addrstr);
+ if(!try_bind(nfd, sa, "tcp")) {
return -1;
}
}
}
-
int setup_vpn_in_socket(const sockaddr_t *sa) {
int nfd;
- char *addrstr;
int option;
nfd = socket(sa->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
return -1;
}
- if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
- closesocket(nfd);
- addrstr = sockaddr2hostname(sa);
- logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s/udp: %s", addrstr, sockstrerror(sockerrno));
- free(addrstr);
+ if(!try_bind(nfd, sa, "udp")) {
return -1;
}
bool hostnames = false;
+uint16_t service_to_port(const char *service) {
+ struct addrinfo *ai = str2addrinfo("localhost", service, SOCK_STREAM);
+
+ if(!ai || !ai->ai_addr) {
+ return 0;
+ }
+
+ sockaddr_t sa;
+ memcpy(&sa, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+
+ switch(sa.sa.sa_family) {
+ case AF_INET:
+ return ntohs(sa.in.sin_port);
+
+ case AF_INET6:
+ return ntohs(sa.in6.sin6_port);
+
+ default:
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Unknown address family %d for service %s.", sa.sa.sa_family, service);
+ return 0;
+ }
+}
+
/*
Turn a string into a struct addrinfo.
Return NULL on failure.
return;
}
}
+
+uint16_t get_bound_port(int sockfd) {
+ sockaddr_t sa;
+ socklen_t salen = sizeof(sa);
+
+ if(getsockname(sockfd, (struct sockaddr *) &sa, &salen)) {
+ return 0;
+ }
+
+ if(sa.sa.sa_family == AF_INET) {
+ return ntohs(sa.in.sin_port);
+ } else if(sa.sa.sa_family == AF_INET6) {
+ return ntohs(sa.in6.sin6_port);
+ } else {
+ return 0;
+ }
+}
extern bool hostnames;
+// Converts service name (as listed in /etc/services) to port number. Returns 0 on error.
+extern uint16_t service_to_port(const char *service);
extern struct addrinfo *str2addrinfo(const char *address, const char *service, int socktype) ATTR_MALLOC;
extern sockaddr_t str2sockaddr(const char *address, const char *port);
extern void sockaddr2str(const sockaddr_t *sa, char **address, char **port);
extern void sockaddrfree(sockaddr_t *sa);
extern void sockaddrcpy(sockaddr_t *dest, const sockaddr_t *src);
extern void sockaddr_setport(sockaddr_t *sa, const char *port);
+extern uint16_t get_bound_port(int sockfd);
#endif
--- /dev/null
+#include "system.h"
+
+#include "pidfile.h"
+#include "names.h"
+
+pidfile_t *read_pidfile(void) {
+ FILE *f = fopen(pidfilename, "r");
+
+ if(!f) {
+ return NULL;
+ }
+
+ pidfile_t *pf = malloc(sizeof(pidfile_t));
+ int read = fscanf(f, "%20d %64s %128s port %128s", &pf->pid, pf->cookie, pf->host, pf->port);
+ fclose(f);
+
+ if(read != 4) {
+ free(pf);
+ pf = NULL;
+ }
+
+ return pf;
+}
+
+
+bool write_pidfile(const char *controlcookie, const char *address) {
+ const mode_t mask = umask(0);
+ umask(mask | 077);
+ FILE *f = fopen(pidfilename, "w");
+
+ if(!f) {
+ return false;
+ }
+
+ umask(mask);
+ fprintf(f, "%d %s %s\n", (int)getpid(), controlcookie, address);
+ return !fclose(f);
+}
--- /dev/null
+#ifndef TINC_PIDFILE_H
+#define TINC_PIDFILE_H
+
+#include "system.h"
+
+typedef struct pidfile_t {
+ int pid;
+ char host[129];
+ char port[129];
+ char cookie[65];
+} pidfile_t;
+
+extern pidfile_t *read_pidfile(void) ATTR_MALLOC;
+extern bool write_pidfile(const char *controlcookie, const char *address);
+
+#endif // TINC_PIDFILE_H
get_config_int(lookup_config(&config_tree, "Weight"), &c->estimated_weight);
}
- return send_request(c, "%d %s %d %x", ACK, myport, c->estimated_weight, (c->options & 0xffffff) | (experimental ? (PROT_MINOR << 24) : 0));
+ return send_request(c, "%d %s %d %x", ACK, myport.udp, c->estimated_weight, (c->options & 0xffffff) | (experimental ? (PROT_MINOR << 24) : 0));
}
static void send_everything(connection_t *c) {
if(getsockname(c->socket, &local_sa.sa, &local_salen) < 0) {
logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get local socket address for connection with %s", c->name);
} else {
- sockaddr_setport(&local_sa, myport);
+ sockaddr_setport(&local_sa, myport.udp);
c->edge->local_address = local_sa;
}
#include "subnet.h"
#include "keys.h"
#include "random.h"
+#include "pidfile.h"
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
}
}
- FILE *f = fopen(pidfilename, "r");
+ pidfile_t *pidfile = read_pidfile();
- if(!f) {
+ if(!pidfile) {
if(verbose) {
fprintf(stderr, "Could not open pid file %s: %s\n", pidfilename, strerror(errno));
}
return false;
}
- char host[129];
- char port[129];
-
- if(fscanf(f, "%20d %1024s %128s port %128s", &pid, controlcookie, host, port) != 4) {
- if(verbose) {
- fprintf(stderr, "Could not parse pid file %s\n", pidfilename);
- }
-
- fclose(f);
- return false;
- }
-
- fclose(f);
+ pid = pidfile->pid;
+ strcpy(controlcookie, pidfile->cookie);
#ifndef HAVE_WINDOWS
+ free(pidfile);
if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
struct addrinfo *res = NULL;
- if(getaddrinfo(host, port, &hints, &res) || !res) {
+ if(getaddrinfo(pidfile->host, pidfile->port, &hints, &res) || !res) {
if(verbose) {
- fprintf(stderr, "Cannot resolve %s port %s: %s\n", host, port, sockstrerror(sockerrno));
+ fprintf(stderr, "Cannot resolve %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
}
+ free(pidfile);
return false;
}
fprintf(stderr, "Cannot create TCP socket: %s\n", sockstrerror(sockerrno));
}
+ free(pidfile);
return false;
}
if(connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
if(verbose) {
- fprintf(stderr, "Cannot connect to %s port %s: %s\n", host, port, sockstrerror(sockerrno));
+ fprintf(stderr, "Cannot connect to %s port %s: %s\n", pidfile->host, pidfile->port, sockstrerror(sockerrno));
}
+ free(pidfile);
closesocket(fd);
fd = -1;
return false;
}
+ free(pidfile);
freeaddrinfo(res);
#endif
{NULL, 0}
};
+// Request actual port from tincd
+static bool read_actual_port(void) {
+ pidfile_t *pidfile = read_pidfile();
+
+ if(pidfile) {
+ printf("%s\n", pidfile->port);
+ free(pidfile);
+ return true;
+ } else {
+ fprintf(stderr, "Could not get port from the pidfile.\n");
+ return false;
+ }
+}
+
static int cmd_config(int argc, char *argv[]) {
if(argc < 2) {
fprintf(stderr, "Invalid number of arguments.\n");
action = SET;
}
+ // If port is requested, try reading it from the pidfile and fall back to configs if that fails
+ if(action == GET && !strcasecmp(variable, "Port") && read_actual_port()) {
+ return 0;
+ }
+
/* Some simple checks. */
bool found = false;
bool warnonremove = false;
}
}
+bool is_decimal(const char *str) {
+ if(!str) {
+ return false;
+ }
+
+ errno = 0;
+ char *badchar = NULL;
+ strtol(str, &badchar, 10);
+ return !errno && badchar != str && !*badchar;
+}
+
+// itoa() conflicts with a similarly named function under MinGW.
+char *int_to_str(int num) {
+ char *str = NULL;
+ xasprintf(&str, "%d", num);
+ return str;
+}
+
static size_t b64encode_tinc_internal(const void *src, char *dst, size_t length, const char *alphabet) {
uint32_t triplet;
const unsigned char *usrc = (unsigned char *)src;
extern size_t hex2bin(const char *src, void *dst, size_t length);
extern size_t bin2hex(const void *src, char *dst, size_t length);
+// Returns true if string represents a base-10 integer.
+extern bool is_decimal(const char *str);
+
+// The reverse of atoi().
+extern char *int_to_str(int num);
+
extern size_t b64encode_tinc(const void *src, char *dst, size_t length);
extern size_t b64encode_tinc_urlsafe(const void *src, char *dst, size_t length);
extern size_t b64decode_tinc(const char *src, void *dst, size_t length);
from testlib.test import Test
+def run_port0_test(ctx: Test) -> None:
+ """Checks that tinc invite fails if called with Port 0 and tincd stopped."""
+ foo = ctx.node()
+ stdin = f"""
+ init {foo}
+ set Port 0
+ set Address localhost
+ set DeviceType dummy
+ """
+ foo.cmd(stdin=stdin)
+ _, err = foo.cmd("invite", "bar", code=1)
+ check.is_in("Please start tincd", err)
+
+
def run_invite_test(ctx: Test, start_before_invite: bool) -> None:
"""Run tests. If start_before_invite is True,
tincd is started *before* creating invitation, and vice versa.
"""
foo, bar = ctx.node(), ctx.node()
-
stdin = f"""
init {foo}
- set Port 0
+ set Port 12345
set Address localhost
set DeviceType dummy
set Mode switch
foo.cmd(stdin=stdin)
if start_before_invite:
+ foo.cmd("set", "Port", "0")
port = foo.start()
log.info("create invitation")
foo_invite = foo_invite.strip()
if not start_before_invite:
+ foo.cmd("set", "Port", "0")
port = foo.start()
- foo_invite = foo_invite.replace(":0/", f":{port}/")
+ foo_invite = foo_invite.replace(":12345/", f":{port}/")
log.info("join second node with %s", foo_invite)
bar.cmd("join", foo_invite)
check.nodes(bar, 2)
+with Test("fail with Port 0 and tincd not running") as context:
+ run_port0_test(context)
+
with Test("offline mode") as context:
run_invite_test(context, start_before_invite=False)
if code is not None:
check.equals(code, res)
- # Check that port was not used by something else
- check.not_in("Can't bind to ", err)
-
return out if out else "", err if err else ""
def tinc(self, *args: str) -> subp.Popen:
'code': 'test_random_noinit.c',
'fail': true,
},
+ 'netutl': {
+ 'code': 'test_netutl.c',
+ },
'net': {
'code': 'test_net.c',
'mock': ['execute_script', 'environment_init', 'environment_exit'],
'protocol': {
'code': 'test_protocol.c',
},
+ 'utils': {
+ 'code': 'test_utils.c',
+ },
'splay_tree': {
'code': 'test_splay_tree.c',
'link': link_tinc,
--- /dev/null
+#include "unittest.h"
+#include "../../src/netutl.h"
+
+static void test_service_to_port_invalid(void **state) {
+ (void)state;
+
+ assert_int_equal(0, service_to_port(NULL));
+ assert_int_equal(0, service_to_port(""));
+ assert_int_equal(0, service_to_port("-1"));
+ assert_int_equal(0, service_to_port("foobar"));
+}
+
+static void test_service_to_port_valid(void **state) {
+ (void)state;
+
+ assert_int_equal(22, service_to_port("ssh"));
+ assert_int_equal(80, service_to_port("http"));
+ assert_int_equal(443, service_to_port("https"));
+ assert_int_equal(1234, service_to_port("1234"));
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_service_to_port_invalid),
+ cmocka_unit_test(test_service_to_port_valid),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
--- /dev/null
+#include "unittest.h"
+#include "../../src/utils.h"
+
+static void test_int_to_str(const char *ref, int num) {
+ char *result = int_to_str(num);
+ assert_string_equal(ref, result);
+ free(result);
+}
+
+static void test_int_to_str_return_expected(void **state) {
+ (void)state;
+
+ test_int_to_str("0", 0);
+ test_int_to_str("-1337", -1337);
+ test_int_to_str("65535", 65535);
+}
+
+static void test_is_decimal_fail_empty(void **state) {
+ (void)state;
+
+ assert_false(is_decimal(NULL));
+ assert_false(is_decimal(""));
+}
+
+static void test_is_decimal_fail_hex(void **state) {
+ (void)state;
+
+ assert_false(is_decimal("DEADBEEF"));
+ assert_false(is_decimal("0xCAFE"));
+}
+
+static void test_is_decimal_fail_junk_suffix(void **state) {
+ (void)state;
+
+ assert_false(is_decimal("123foobar"));
+ assert_false(is_decimal("777 "));
+}
+
+static void test_is_decimal_pass_simple(void **state) {
+ (void)state;
+
+ assert_true(is_decimal("0"));
+ assert_true(is_decimal("123"));
+}
+
+static void test_is_decimal_pass_signs(void **state) {
+ (void)state;
+
+ assert_true(is_decimal("-123"));
+ assert_true(is_decimal("+123"));
+}
+
+static void test_is_decimal_pass_whitespace_prefix(void **state) {
+ (void)state;
+
+ assert_true(is_decimal(" \r\n\t 777"));
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_int_to_str_return_expected),
+ cmocka_unit_test(test_is_decimal_fail_empty),
+ cmocka_unit_test(test_is_decimal_fail_hex),
+ cmocka_unit_test(test_is_decimal_fail_junk_suffix),
+ cmocka_unit_test(test_is_decimal_pass_simple),
+ cmocka_unit_test(test_is_decimal_pass_signs),
+ cmocka_unit_test(test_is_decimal_pass_whitespace_prefix),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}