without requiring
.Va ConnectTo
variables.
+.Pp
+Note: it is not possible to connect to nodes using zero (system-assigned) ports in this way.
.It Va BindToAddress Li = Ar address Op Ar port
This is the same as
.Va ListenAddress ,
.Li *
for the
.Ar address .
+.Pp
+If
+.Ar port
+is set to zero, it will be randomly assigned by the system. This is useful to randomize source ports of UDP packets, which can improve UDP hole punching reliability. In this case it is recommended to set
+.Va AddressFamily
+as well, otherwise
+.Nm tinc
+will assign different ports to different address families but other nodes can only know of one.
.It Va LocalDiscovery Li = yes | no Pq yes
When enabled,
.Nm tinc
which is used if no port number is specified in an
.Va Address
statement.
+.Pp
+If this is set to zero, the port will be randomly assigned by the system. This is useful to randomize source ports of UDP packets, which can improve UDP hole punching reliability. When setting
+.Va Port
+to zero it is recommended to set
+.Va AddressFamily
+as well, otherwise
+.Nm tinc
+will assign different ports to different address families but other nodes can only know of one.
.It Va PublicKey Li = Ar key Bq obsolete
The public RSA key of this tinc daemon.
It will be used to cryptographically verify it's identity and to set up a secure connection.
typedef struct connection_status_t {
unsigned int pinged:1; /* sent ping */
- unsigned int active:1; /* 1 if active.. */
+ unsigned int unused_active:1;
unsigned int connecting:1; /* 1 if we are waiting for a non-blocking connect() to finish */
unsigned int unused_termreq:1; /* the termination of this connection was requested */
unsigned int remove_unused:1; /* Set to 1 if you want this connection removed */
unsigned int log:1; /* 1 if this is a control connection requesting log dump */
unsigned int invitation:1; /* 1 if this is an invitation */
unsigned int invitation_used:1; /* 1 if the invitation has been consumed */
- unsigned int unused:19;
+ unsigned int unused:18;
} connection_status_t;
#include "ecdsa.h"
for list_each(connection_t, other, connection_list) {
if(strcmp(other->name, name))
continue;
- terminate_connection(other, other->status.active);
+ terminate_connection(other, other->edge);
found = true;
}
extern char *device;
extern char *iface;
-extern uint64_t device_in_packets;
-extern uint64_t device_in_bytes;
-extern uint64_t device_out_packets;
-extern uint64_t device_out_bytes;
-
typedef struct devops_t {
bool (*setup)(void);
void (*close)(void);
FILE *fh = fopen(filename, "w");
if(!fh) {
fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
+ fclose(f);
return false;
}
void broadcast_meta(connection_t *from, const char *buffer, int length) {
for list_each(connection_t, c, connection_list)
- if(c != from && c->status.active)
+ if(c != from && c->edge)
send_meta(c, buffer, length);
}
char *iface = NULL;
static char *device_info = NULL;
-static uint64_t device_total_in = 0;
-static uint64_t device_total_out = 0;
-
extern char *myport;
static void device_issue_read() {
}
}
-static void device_handle_read(void *data) {
+static void device_handle_read(void *data, int flags) {
ResetEvent(device_read_overlapped.hEvent);
DWORD len;
bool found = false;
int err;
- HANDLE thread;
get_config_string(lookup_config(config_tree, "Device"), &device);
get_config_string(lookup_config(config_tree, "Interface"), &iface);
return false;
}
- device_total_out += packet->len;
-
return true;
}
void terminate_connection(connection_t *c, bool report) {
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname);
- c->status.active = false;
-
if(c->node && c->node->connection == c)
c->node->connection = NULL;
continue;
if(c->last_ping_time + pingtimeout <= now.tv_sec) {
- if(c->status.active) {
+ if(c->edge) {
if(c->status.pinged) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time);
} else if(c->last_ping_time + pinginterval <= now.tv_sec) {
else
logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
}
- terminate_connection(c, c->status.active);
+ terminate_connection(c, c->edge);
}
}
/* Count number of active connections */
int nc = 0;
for list_each(connection_t, c, connection_list) {
- if(c->status.active && !c->status.control)
+ if(c->edge)
nc++;
}
int i = 0;
for list_each(connection_t, c, connection_list) {
- if(!c->status.active || c->status.control)
+ if(!c->edge)
continue;
if(i++ != r)
logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
list_delete(outgoing_list, c->outgoing);
c->outgoing = NULL;
- terminate_connection(c, c->status.active);
+ terminate_connection(c, c->edge);
break;
}
}
void handle_meta_connection_data(connection_t *c) {
if (!receive_meta(c)) {
- terminate_connection(c, c->status.active);
+ terminate_connection(c, c->edge);
return;
}
}
struct stat s;
if(stat(fname, &s) || s.st_mtime > last_config_check) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Host config file of %s has been changed", c->name);
- terminate_connection(c, c->status.active);
+ terminate_connection(c, c->edge);
}
free(fname);
}
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
- vpn_packet_t *outpkt = pkt[0];
size_t outlen;
if(n->status.sptps) {
/* Decrypt the packet */
if(cipher_active(n->incipher)) {
- outpkt = pkt[nextpkt++];
+ vpn_packet_t *outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_decrypt(n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
length_t origlen = inpkt->len;
if(n->incompression) {
- outpkt = pkt[nextpkt++];
+ vpn_packet_t *outpkt = pkt[nextpkt++];
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
logger(DEBUG_TRAFFIC, LOG_ERR, "Error while uncompressing packet from %s (%s)",
size_t outlen;
#if defined(SOL_IP) && defined(IP_TOS)
static int priority = 0;
-#endif
int origpriority = origpkt->priority;
+#endif
if(!n->status.reachable) {
logger(DEBUG_TRAFFIC, LOG_INFO, "Trying to send UDP packet to unreachable node %s (%s)", n->name, n->hostname);
/* Otherwise, send the packet via UDP */
- const sockaddr_t *sa;
+ const sockaddr_t *sa = NULL;
int sock;
if(to->status.send_locally)
choose_local_address(to, &sa, &sock);
- else
+ if(!sa)
choose_udp_address(to, &sa, &sock);
if(sendto(listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
// usually distributes the sending of broadcast packets over all nodes.
case BMODE_MST:
for list_each(connection_t, c, connection_list)
- if(c->status.active && c->status.mst && c != from->nexthop->connection)
+ if(c->edge && c->status.mst && c != from->nexthop->connection)
send_packet(c->node, packet);
break;
logger(DEBUG_CONNECTIONS, LOG_ERR, "Could not send %d bytes of data to %s (%s): %s", c->outbuf.len - c->outbuf.offset, c->name, c->hostname, sockstrerror(sockerrno));
}
- terminate_connection(c, c->status.active);
+ terminate_connection(c, c->edge);
return;
}
if(c->outgoing && c->outgoing->timeout == -1) {
c->outgoing = NULL;
logger(DEBUG_CONNECTIONS, LOG_INFO, "No more outgoing connection to %s", c->name);
- terminate_connection(c, c->status.active);
+ terminate_connection(c, c->edge);
}
}
return true;
}
-static io_t stop_io;
+io_t stop_io;
DWORD WINAPI controlhandler(DWORD request, DWORD type, LPVOID boe, LPVOID bah) {
switch(request) {
return NO_ERROR;
}
-static void stop_handler(void *data, int flags) {
- event_exit();
-}
-
VOID WINAPI run_service(DWORD argc, LPTSTR* argv) {
extern int main2(int argc, char **argv);
- io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
- if (stop_io.event == FALSE)
- abort();
-
status.dwServiceType = SERVICE_WIN32;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = 0;
SetServiceStatus(statushandle, &status);
}
- if (WSACloseEvent(stop_io.event) == FALSE)
- abort();
- io_del(&stop_io);
return;
}
extern bool kill_other(int);
#ifdef HAVE_MINGW
+extern io_t stop_io;
extern bool init_service(void);
#endif
/* Activate this connection */
c->allow_request = ALL;
- c->status.active = true;
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection with %s (%s) activated", c->name,
c->hostname);
/* Immediately send new keys to directly connected nodes to keep UDP mappings alive */
for list_each(connection_t, c, connection_list)
- if(c->status.active && c->node && c->node->status.reachable && !c->node->status.sptps)
+ if(c->edge && c->node && c->node->status.reachable && !c->node->status.sptps)
send_ans_key(c->node);
/* Force key exchange for connections using SPTPS */
}
for list_each(connection_t, c, connection_list)
- if(c->status.active)
+ if(c->edge)
send_del_subnet(c, s);
subnet_del(myself, s);
/* And tell all other tinc daemons it's our MAC */
for list_each(connection_t, c, connection_list)
- if(c->status.active)
+ if(c->edge)
send_add_subnet(c, subnet);
timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000});
if(q) {
memcpy(ext, p, q - p);
ext[q - p] = 0;
- *q++;
+ q++;
} else {
strcpy(ext, p);
}
uint32_t seqno;
memcpy(&seqno, data, 4);
seqno = ntohl(seqno);
+ if (!sptps_check_seqno(s, seqno, false))
+ return false;
char buffer[len];
size_t outlen;
- if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen))
- return false;
-
- return sptps_check_seqno(s, seqno, false);
+ return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
}
// Receive incoming data, datagram version.
bool str2net(subnet_t *subnet, const char *subnetstr) {
char str[1024];
strncpy(str, subnetstr, sizeof(str));
+ str[sizeof str - 1] = 0;
int consumed;
int weight = DEFAULT_WEIGHT;
for (int i = 0; i < 4; i++)
if (x[i] > 255)
return false;
- sprintf(last_colon, ":%02x%02x:%02x%02x", x[0], x[1], x[2], x[3]);
+ snprintf(last_colon, sizeof str - (last_colon - str), ":%02x%02x:%02x%02x", x[0], x[1], x[2], x[3]);
}
char* double_colon = strstr(str, "::");
int nargc = 0;
char **nargv = xzalloc((optind + argc) * sizeof *nargv);
- nargv[nargc++] = c;
+ char *arg0 = c;
+#ifdef HAVE_MINGW
+ /*
+ Windows has no real concept of an "argv array". A command line is just one string.
+ The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
+ it uses quotes to handle spaces in arguments.
+ Therefore we need to quote all arguments that might contain spaces. No, execvp() won't do that for us (see MSDN).
+ If we don't do that, then execvp() will run fine but any spaces in the filename contained in arg0 will bleed
+ into the next arguments when the spawned process' CRT parses its command line, resulting in chaos.
+ */
+ xasprintf(&arg0, "\"%s\"", arg0);
+#endif
+ nargv[nargc++] = arg0;
for(int i = 1; i < optind; i++)
nargv[nargc++] = orig_argv[i];
for(int i = 1; i < argc; i++)
nargv[nargc++] = argv[i];
#ifdef HAVE_MINGW
- execvp(c, nargv);
- fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
- return 1;
+ int status = spawnvp(_P_WAIT, c, nargv);
+ if (status == -1) {
+ fprintf(stderr, "Error starting %s: %s\n", c, strerror(errno));
+ return 1;
+ }
+ return status;
#else
pid_t pid = fork();
if(pid == -1) {
#include "control.h"
#include "crypto.h"
#include "device.h"
+#include "event.h"
#include "logger.h"
#include "names.h"
#include "net.h"
#ifdef HAVE_MINGW
# define setpriority(level) !SetPriorityClass(GetCurrentProcess(), (level))
+
+static void stop_handler(void *data, int flags) {
+ event_exit();
+}
+
+static BOOL WINAPI console_ctrl_handler(DWORD type) {
+ logger(DEBUG_ALWAYS, LOG_NOTICE, "Got console shutdown request");
+ if (WSASetEvent(stop_io.event) == FALSE)
+ abort();
+ return TRUE;
+}
#else
# define NORMAL_PRIORITY_CLASS 0
# define BELOW_NORMAL_PRIORITY_CLASS 10
#endif
#ifdef HAVE_MINGW
- if(!do_detach || !init_service())
- return main2(argc, argv);
- else
- return 1;
+ io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
+ if (stop_io.event == FALSE)
+ abort();
+
+ int result;
+ if(!do_detach || !init_service()) {
+ SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
+ result = main2(argc, argv);
+ } else
+ result = 1;
+
+ if (WSACloseEvent(stop_io.event) == FALSE)
+ abort();
+ io_del(&stop_io);
+ return result;
}
int main2(int argc, char **argv) {
#ifdef HAVE_CURSES
+#undef KEY_EVENT /* There are conflicting declarations for KEY_EVENT in Windows wincon.h and curses.h. */
#include <curses.h>
#include "control_common.h"