/*
net_socket.c -- Handle various kinds of sockets.
Copyright (C) 1998-2005 Ivo Timmermans,
- 2000-2014 Guus Sliepen <guus@tinc-vpn.org>
+ 2000-2017 Guus Sliepen <guus@tinc-vpn.org>
2006 Scott Lamb <slamb@slamb.org>
2009 Florian Forster <octo@verplant.org>
#include "net.h"
#include "netutl.h"
#include "protocol.h"
+#include "proxy.h"
#include "utils.h"
#include "xalloc.h"
if(fcntl(c->socket, F_SETFL, flags | O_NONBLOCK) < 0) {
logger(LOG_ERR, "fcntl for %s: %s", c->hostname, strerror(errno));
}
+
#elif defined(WIN32)
unsigned long arg = 1;
if(ioctlsocket(c->socket, FIONBIO, &arg) != 0) {
logger(LOG_ERR, "ioctlsocket for %s: %s", c->hostname, sockstrerror(sockerrno));
}
+
#endif
#if defined(SOL_TCP) && defined(TCP_NODELAY)
setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option));
#endif
-#if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
+#if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
- setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof(option));
+ setsockopt(c->socket, IPPROTO_IP, IP_TOS, (void *)&option, sizeof(option));
#endif
-#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) && defined(IPTOS_LOWDELAY)
+#if defined(IPV6_TCLASS) && defined(IPTOS_LOWDELAY)
option = IPTOS_LOWDELAY;
setsockopt(c->socket, IPPROTO_IPV6, IPV6_TCLASS, (void *)&option, sizeof(option));
#endif
int status;
#endif /* defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) */
- if(!get_config_string(lookup_config (config_tree, "BindToInterface"), &iface))
+ if(!get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
return true;
+ }
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
free(iface);
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
+
if(status) {
logger(LOG_ERR, "Can't bind to interface %s: %s", ifr.ifr_ifrn.ifrn_name, strerror(errno));
return false;
option = 1;
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
-#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
- if(sa->sa.sa_family == AF_INET6)
- setsockopt(nfd, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
+#if defined(IPV6_V6ONLY)
+
+ if(sa->sa.sa_family == AF_INET6) {
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
+ }
+
+#else
+#warning IPV6_V6ONLY not defined
#endif
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
closesocket(nfd);
logger(LOG_ERR, "System call `%s' failed: %s", "fcntl",
- strerror(errno));
+ strerror(errno));
return -1;
}
}
#elif defined(WIN32)
{
unsigned long arg = 1;
+
if(ioctlsocket(nfd, FIONBIO, &arg) != 0) {
closesocket(nfd);
logger(LOG_ERR, "Call to `%s' failed: %s", "ioctlsocket", sockstrerror(sockerrno));
setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
setsockopt(nfd, SOL_SOCKET, SO_BROADCAST, (void *)&option, sizeof(option));
- if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
+ if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf))) {
logger(LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
+ }
- if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
+ if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf))) {
logger(LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
+ }
+
+#if defined(IPV6_V6ONLY)
+
+ if(sa->sa.sa_family == AF_INET6) {
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
+ }
-#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
- if(sa->sa.sa_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(SOL_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
+
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = IP_PMTUDISC_DO;
- setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
+ setsockopt(nfd, IPPROTO_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
}
-#elif defined(IPPROTO_IP) && defined(IP_DONTFRAGMENT)
+
+#elif defined(IP_DONTFRAGMENT)
+
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = 1;
setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, (void *)&option, sizeof(option));
}
+
#endif
-#if defined(SOL_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
+#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
+
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = IPV6_PMTUDISC_DO;
- setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
+ setsockopt(nfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
}
-#elif defined(IPPROTO_IPV6) && defined(IPV6_DONTFRAG)
+
+#elif defined(IPV6_DONTFRAG)
+
if(myself->options & OPTION_PMTU_DISCOVERY) {
option = 1;
setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
}
+
#endif
- if (!bind_to_interface(nfd)) {
+ if(!bind_to_interface(nfd)) {
closesocket(nfd);
return -1;
}
void retry_outgoing(outgoing_t *outgoing) {
outgoing->timeout += 5;
- if(outgoing->timeout < mintimeout)
+ if(outgoing->timeout < mintimeout) {
outgoing->timeout = mintimeout;
+ }
- if(outgoing->timeout > maxtimeout)
+ if(outgoing->timeout > maxtimeout) {
outgoing->timeout = maxtimeout;
+ }
- if(outgoing->event)
+ if(outgoing->event) {
event_del(outgoing->event);
+ }
+
outgoing->event = new_event();
outgoing->event->handler = (event_handler_t) setup_outgoing_connection;
outgoing->event->time = now + outgoing->timeout;
event_add(outgoing->event);
ifdebug(CONNECTIONS) logger(LOG_NOTICE,
- "Trying to re-establish outgoing connection in %d seconds",
- outgoing->timeout);
+ "Trying to re-establish outgoing connection in %d seconds",
+ outgoing->timeout);
}
void finish_connecting(connection_t *c) {
setenv("REMOTEPORT", port, true);
setenv("NODE", c->name, true);
setenv("NAME", myself->name, true);
- if(netname)
+
+ if(netname) {
setenv("NETNAME", netname, true);
+ }
int result = system(command);
- if(result < 0)
+
+ if(result < 0) {
logger(LOG_ERR, "Could not execute %s: %s\n", command, strerror(errno));
- else if(result)
+ } else if(result) {
logger(LOG_ERR, "%s exited with non-zero status %d", command, result);
+ }
+
exit(result);
#else
logger(LOG_ERR, "Proxy type exec not supported on this platform!");
#endif
}
+static bool is_valid_host_port(const char *host, const char *port) {
+ for(const char *p = host; *p; p++)
+ if(!isalnum(*p) && *p != '-' && *p != '.') {
+ return false;
+ }
+
+ for(const char *p = port; *p; p++)
+ if(!isalnum(*p)) {
+ return false;
+ }
+
+ return true;
+}
+
void do_outgoing_connection(connection_t *c) {
- char *address, *port, *space;
struct addrinfo *proxyai = NULL;
int result;
}
begin:
+
if(!c->outgoing->ai) {
if(!c->outgoing->cfg) {
ifdebug(CONNECTIONS) logger(LOG_ERR, "Could not set up a meta connection to %s",
- c->name);
+ c->name);
c->status.remove = true;
retry_outgoing(c->outgoing);
c->outgoing = NULL;
return;
}
+ char *address, *port, *space;
+
get_config_string(c->outgoing->cfg, &address);
space = strchr(address, ' ');
+
if(space) {
port = xstrdup(space + 1);
*space = 0;
} else {
- if(!get_config_string(lookup_config(c->config_tree, "Port"), &port))
+ if(!get_config_string(lookup_config(c->config_tree, "Port"), &port)) {
port = xstrdup("655");
+ }
}
c->outgoing->ai = str2addrinfo(address, port, SOCK_STREAM);
- free(address);
- free(port);
+
+ // If we cannot resolve the address, maybe we are using a proxy that can?
+ if(!c->outgoing->ai && proxytype != PROXY_NONE && is_valid_host_port(address, port)) {
+ memset(&c->address, 0, sizeof(c->address));
+ c->address.sa.sa_family = AF_UNKNOWN;
+ c->address.unknown.address = address;
+ c->address.unknown.port = port;
+ } else {
+ free(address);
+ free(port);
+ }
c->outgoing->aip = c->outgoing->ai;
c->outgoing->cfg = lookup_config_next(c->config_tree, c->outgoing->cfg);
+
+ if(!c->outgoing->ai && proxytype != PROXY_NONE) {
+ goto connect;
+ }
}
if(!c->outgoing->aip) {
- if(c->outgoing->ai)
+ if(c->outgoing->ai) {
freeaddrinfo(c->outgoing->ai);
+ }
+
c->outgoing->ai = NULL;
goto begin;
}
memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen);
c->outgoing->aip = c->outgoing->aip->ai_next;
- if(c->hostname)
+connect:
+
+ if(c->hostname) {
free(c->hostname);
+ }
c->hostname = sockaddr2hostname(&c->address);
ifdebug(CONNECTIONS) logger(LOG_INFO, "Trying to connect to %s (%s)", c->name,
- c->hostname);
+ c->hostname);
if(!proxytype) {
c->socket = socket(c->address.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
} else if(proxytype == PROXY_EXEC) {
+ c->status.proxy_passed = true;
do_outgoing_pipe(c, proxyhost);
} else {
proxyai = str2addrinfo(proxyhost, proxyport, SOCK_STREAM);
- if(!proxyai)
+
+ if(!proxyai) {
goto begin;
+ }
+
ifdebug(CONNECTIONS) logger(LOG_INFO, "Using proxy at %s port %s", proxyhost, proxyport);
c->socket = socket(proxyai->ai_family, SOCK_STREAM, IPPROTO_TCP);
}
goto begin;
}
- if(proxytype != PROXY_EXEC)
+ if(proxytype != PROXY_EXEC) {
configure_tcp(c);
+ }
#ifdef FD_CLOEXEC
fcntl(c->socket, F_SETFD, FD_CLOEXEC);
#endif
if(proxytype != PROXY_EXEC) {
-#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
+#if defined(IPV6_V6ONLY)
int option = 1;
- if(c->address.sa.sa_family == AF_INET6)
- setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
+
+ if(c->address.sa.sa_family == AF_INET6) {
+ setsockopt(c->socket, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof(option));
+ }
+
#endif
bind_to_interface(c->socket);
+
+ int b = -1;
+
+ for(int i = 0; i < listen_sockets; i++) {
+ if(listen_socket[i].sa.sa.sa_family == c->address.sa.sa_family) {
+ if(b == -1) {
+ b = i;
+ } else {
+ b = -1;
+ break;
+ }
+ }
+ }
+
+ if(b != -1) {
+ sockaddr_t sa = listen_socket[b].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))) {
+ char *addrstr = sockaddr2hostname(&sa);
+ logger(LOG_ERR, "Can't bind to %s/tcp: %s", addrstr, sockstrerror(sockerrno));
+ free(addrstr);
+ }
+ }
}
/* Connect */
freeaddrinfo(proxyai);
}
+ now = time(NULL);
+
if(result == -1) {
if(sockinprogress(sockerrno)) {
+ c->last_ping_time = now;
c->status.connecting = true;
return;
}
c->outcompression = myself->connection->outcompression;
init_configuration(&c->config_tree);
- read_connection_config(c);
+
+ if(!read_connection_config(c)) {
+ free_connection(c);
+ outgoing->timeout = maxtimeout;
+ retry_outgoing(outgoing);
+ return;
+ }
outgoing->cfg = lookup_config(c->config_tree, "Address");
if(!outgoing->cfg) {
logger(LOG_ERR, "No address specified for %s", c->name);
free_connection(c);
+ outgoing->timeout = maxtimeout;
+ retry_outgoing(outgoing);
return;
}
new connection
*/
bool handle_new_meta_connection(int sock) {
+ static const int max_accept_burst = 10;
+ static int last_accept_burst;
+ static int last_accept_time;
connection_t *c;
sockaddr_t sa;
int fd;
return false;
}
+ if(last_accept_time == now) {
+ last_accept_burst++;
+
+ if(last_accept_burst >= max_accept_burst) {
+ if(last_accept_burst == max_accept_burst) {
+ ifdebug(CONNECTIONS) logger(LOG_WARNING, "Throttling incoming connections");
+ }
+
+ tarpit(fd);
+ return false;
+ }
+ } else {
+ last_accept_burst = 0;
+ last_accept_time = now;
+ }
+
sockaddrunmap(&sa);
c = new_connection();
connection_add(c);
c->allow_request = ID;
- send_id(c);
return true;
}
static void free_outgoing(outgoing_t *outgoing) {
- if(outgoing->ai)
+ if(outgoing->ai) {
freeaddrinfo(outgoing->ai);
+ }
- if(outgoing->name)
+ if(outgoing->name) {
free(outgoing->name);
+ }
free(outgoing);
}
static config_t *cfg = NULL;
char *name;
outgoing_t *outgoing;
-
+
outgoing_list = list_alloc((list_action_t)free_outgoing);
-
+
for(cfg = lookup_config(config_tree, "ConnectTo"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
get_config_string(cfg, &name);
if(!check_id(name)) {
logger(LOG_ERR,
- "Invalid name for outgoing connection in %s line %d",
- cfg->file, cfg->line);
+ "Invalid name for outgoing connection in %s line %d",
+ cfg->file, cfg->line);
free(name);
continue;
}