6fbf66fa |
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#include "syshead.h"
#include "socket.h"
#include "fdmisc.h"
#include "misc.h"
#include "gremlin.h"
#include "plugin.h" |
6add6b2f |
#include "ps.h" |
bf97c00f |
#include "run_command.h" |
e482a632 |
#include "manage.h" |
8e9666d5 |
#include "misc.h" |
a55b3cdb |
#include "manage.h" |
e719a053 |
#include "openvpn.h" |
f2134b7b |
#include "forward.h" |
6fbf66fa |
#include "memdbg.h"
|
1406db55 |
const int proto_overhead[] = { /* indexed by PROTO_x */ |
81d882d5 |
0,
IPv4_UDP_HEADER_SIZE, /* IPv4 */
IPv4_TCP_HEADER_SIZE,
IPv4_TCP_HEADER_SIZE,
IPv6_UDP_HEADER_SIZE, /* IPv6 */
IPv6_TCP_HEADER_SIZE,
IPv6_TCP_HEADER_SIZE,
IPv6_TCP_HEADER_SIZE, |
1406db55 |
};
|
6fbf66fa |
/* |
8e9666d5 |
* Convert sockflags/getaddr_flags into getaddr_flags
*/
static unsigned int |
86093c1c |
sf2gaf(const unsigned int getaddr_flags, |
8e9666d5 |
const unsigned int sockflags)
{ |
81d882d5 |
if (sockflags & SF_HOST_RANDOMIZE)
{
return getaddr_flags | GETADDR_RANDOMIZE;
}
else
{
return getaddr_flags;
} |
8e9666d5 |
}
/* |
6fbf66fa |
* Functions related to the translation of DNS names to IP addresses.
*/
/*
* Translate IP addr or hostname to in_addr_t.
* If resolve error, try again for
* resolve_retry_seconds seconds.
*/
in_addr_t |
81d882d5 |
getaddr(unsigned int flags,
const char *hostname,
int resolve_retry_seconds,
bool *succeeded,
volatile int *signal_received)
{
struct addrinfo *ai;
int status;
status = openvpn_getaddrinfo(flags & ~GETADDR_HOST_ORDER, hostname, NULL,
resolve_retry_seconds, signal_received, AF_INET, &ai);
if (status==0)
{
struct in_addr ia;
if (succeeded)
{
*succeeded = true;
}
ia = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
freeaddrinfo(ai);
return (flags & GETADDR_HOST_ORDER) ? ntohl(ia.s_addr) : ia.s_addr;
}
else
{
if (succeeded)
{
*succeeded = false;
}
return 0;
} |
6fbf66fa |
}
|
e719a053 |
static inline bool |
81d882d5 |
streqnull(const char *a, const char *b) |
e719a053 |
{ |
81d882d5 |
if (a == NULL && b == NULL)
{
return true;
}
else if (a == NULL || b == NULL)
{
return false;
}
else
{
return streq(a, b);
} |
e719a053 |
}
/* |
81d882d5 |
* get_cached_dns_entry return 0 on success and -1
* otherwise. (like getaddrinfo) |
e719a053 |
*/
static int |
81d882d5 |
get_cached_dns_entry(struct cached_dns_entry *dns_cache,
const char *hostname,
const char *servname,
int ai_family,
int resolve_flags,
struct addrinfo **ai) |
e719a053 |
{ |
81d882d5 |
struct cached_dns_entry *ph;
int flags; |
e719a053 |
|
81d882d5 |
/* Only use flags that are relevant for the structure */
flags = resolve_flags & GETADDR_CACHE_MASK; |
e719a053 |
|
81d882d5 |
for (ph = dns_cache; ph; ph = ph->next) |
e719a053 |
{ |
81d882d5 |
if (streqnull(ph->hostname, hostname)
&& streqnull(ph->servname, servname)
&& ph->ai_family == ai_family
&& ph->flags == flags)
{
*ai = ph->ai;
return 0;
} |
e719a053 |
} |
81d882d5 |
return -1; |
e719a053 |
}
static int |
81d882d5 |
do_preresolve_host(struct context *c,
const char *hostname,
const char *servname,
const int af,
const int flags) |
e719a053 |
{ |
81d882d5 |
struct addrinfo *ai;
int status; |
e719a053 |
|
81d882d5 |
if (get_cached_dns_entry(c->c1.dns_cache,
hostname,
servname,
af,
flags,
&ai) == 0) |
e719a053 |
{ |
81d882d5 |
/* entry already cached, return success */
return 0; |
e719a053 |
}
|
81d882d5 |
status = openvpn_getaddrinfo(flags, hostname, servname,
c->options.resolve_retry_seconds, NULL,
af, &ai);
if (status == 0) |
e719a053 |
{ |
81d882d5 |
struct cached_dns_entry *ph; |
e719a053 |
|
81d882d5 |
ALLOC_OBJ_CLEAR_GC(ph, struct cached_dns_entry, &c->gc);
ph->ai = ai;
ph->hostname = hostname;
ph->servname = servname;
ph->flags = flags & GETADDR_CACHE_MASK; |
e719a053 |
|
81d882d5 |
if (!c->c1.dns_cache)
{
c->c1.dns_cache = ph;
}
else
{
struct cached_dns_entry *prev = c->c1.dns_cache;
while (prev->next) |
4cd4899e |
{ |
81d882d5 |
prev = prev->next; |
4cd4899e |
} |
81d882d5 |
prev->next = ph;
} |
e719a053 |
|
81d882d5 |
gc_addspecial(ai, &gc_freeaddrinfo_callback, &c->gc); |
e719a053 |
} |
81d882d5 |
return status; |
e719a053 |
}
void |
81d882d5 |
do_preresolve(struct context *c)
{
int i;
struct connection_list *l = c->options.connection_list;
const unsigned int preresolve_flags = GETADDR_RESOLVE
|GETADDR_UPDATE_MANAGEMENT_STATE
|GETADDR_MENTION_RESOLVE_RETRY
|GETADDR_FATAL;
for (i = 0; i < l->len; ++i)
{
int status;
const char *remote;
int flags = preresolve_flags;
struct connection_entry *ce = c->options.connection_list->array[i];
if (proto_is_dgram(ce->proto))
{
flags |= GETADDR_DATAGRAM;
}
if (c->options.sockflags & SF_HOST_RANDOMIZE)
{
flags |= GETADDR_RANDOMIZE;
}
if (c->options.ip_remote_hint)
{
remote = c->options.ip_remote_hint;
}
else
{
remote = ce->remote;
}
/* HTTP remote hostname does not need to be resolved */
if (!ce->http_proxy_options)
{
status = do_preresolve_host(c, remote, ce->remote_port, ce->af, flags);
if (status != 0)
{
goto err;
}
}
/* Preresolve proxy */
if (ce->http_proxy_options)
{
status = do_preresolve_host(c,
ce->http_proxy_options->server,
ce->http_proxy_options->port,
ce->af,
preresolve_flags);
if (status != 0)
{
goto err;
}
}
if (ce->socks_proxy_server)
{
status = do_preresolve_host(c,
ce->socks_proxy_server,
ce->socks_proxy_port,
ce->af,
flags);
if (status != 0)
{
goto err;
}
}
if (ce->bind_local)
{
flags |= GETADDR_PASSIVE;
flags &= ~GETADDR_RANDOMIZE;
status = do_preresolve_host(c, ce->local, ce->local_port, ce->af, flags);
if (status != 0)
{
goto err;
}
} |
e719a053 |
}
return;
|
81d882d5 |
err:
throw_signal_soft(SIGHUP, "Preresolving failed"); |
e719a053 |
} |
f2d6f3bc |
|
ea93a078 |
/* |
f2d6f3bc |
* Translate IPv4/IPv6 addr or hostname into struct addrinfo
* If resolve error, try again for resolve_retry_seconds seconds. |
ea93a078 |
*/ |
f2d6f3bc |
int |
81d882d5 |
openvpn_getaddrinfo(unsigned int flags,
const char *hostname,
const char *servname,
int resolve_retry_seconds,
volatile int *signal_received,
int ai_family,
struct addrinfo **res) |
ea93a078 |
{ |
81d882d5 |
struct addrinfo hints;
int status;
int sigrec = 0;
int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
struct gc_arena gc = gc_new();
const char *print_hostname;
const char *print_servname; |
ea93a078 |
|
81d882d5 |
ASSERT(res); |
f2d6f3bc |
|
81d882d5 |
ASSERT(hostname || servname);
ASSERT(!(flags & GETADDR_HOST_ORDER)); |
076fd3e4 |
|
81d882d5 |
if (servname)
{
print_servname = servname;
}
else
{
print_servname = "";
} |
076fd3e4 |
|
81d882d5 |
if (flags & GETADDR_MSG_VIRT_OUT)
{
msglevel |= M_MSG_VIRT_OUT;
} |
ea93a078 |
|
81d882d5 |
if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
&& !signal_received)
{
signal_received = &sigrec;
} |
ea93a078 |
|
81d882d5 |
/* try numeric ipv6 addr first */
CLEAR(hints);
hints.ai_family = ai_family;
hints.ai_flags = AI_NUMERICHOST; |
97ba084b |
|
81d882d5 |
if (flags & GETADDR_PASSIVE)
{
hints.ai_flags |= AI_PASSIVE;
} |
076fd3e4 |
|
81d882d5 |
if (flags & GETADDR_DATAGRAM)
{
hints.ai_socktype = SOCK_DGRAM;
}
else
{
hints.ai_socktype = SOCK_STREAM;
} |
23d61c56 |
|
81d882d5 |
status = getaddrinfo(hostname, servname, &hints, res); |
ea93a078 |
|
81d882d5 |
if (status != 0) /* parse as numeric address failed? */ |
ea93a078 |
{ |
81d882d5 |
const int fail_wait_interval = 5; /* seconds */
/* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 */
int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 :
((resolve_retry_seconds + 4)/ fail_wait_interval);
const char *fmt;
int level = 0; |
ea93a078 |
|
3c748aeb |
if (hostname && (flags & GETADDR_RANDOMIZE))
{
hostname = hostname_randomize(hostname, &gc);
}
if (hostname)
{
print_hostname = hostname;
}
else
{
print_hostname = "undefined";
}
|
81d882d5 |
fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s)";
if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
&& !resolve_retry_seconds)
{
fmt = "RESOLVE: Cannot resolve host address: %s:%s (%s) (I would have retried this name query if you had specified the --resolv-retry option.)";
} |
ea93a078 |
|
81d882d5 |
if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL) |
f2d6f3bc |
{ |
81d882d5 |
msg(msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)",
print_hostname,print_servname, gai_strerror(status));
goto done; |
f2d6f3bc |
} |
ea93a078 |
#ifdef ENABLE_MANAGEMENT |
81d882d5 |
if (flags & GETADDR_UPDATE_MANAGEMENT_STATE) |
f2d6f3bc |
{ |
81d882d5 |
if (management)
{
management_set_state(management,
OPENVPN_STATE_RESOLVE,
NULL,
NULL,
NULL,
NULL,
NULL);
} |
f2d6f3bc |
} |
ea93a078 |
#endif
|
81d882d5 |
/*
* Resolve hostname
*/
while (true) |
f2d6f3bc |
{ |
445b192a |
#ifndef _WIN32 |
81d882d5 |
res_init(); |
288a819a |
#endif |
81d882d5 |
/* try hostname lookup */
hints.ai_flags &= ~AI_NUMERICHOST;
dmsg(D_SOCKET_DEBUG, "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d",
flags, hints.ai_family, hints.ai_socktype);
status = getaddrinfo(hostname, servname, &hints, res); |
6d1b80bd |
|
81d882d5 |
if (signal_received) |
f2d6f3bc |
{ |
81d882d5 |
get_signal(signal_received);
if (*signal_received) /* were we interrupted by a signal? */ |
f2d6f3bc |
{ |
81d882d5 |
if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */ |
f2d6f3bc |
{ |
81d882d5 |
msg(level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
*signal_received = 0; |
f2d6f3bc |
} |
81d882d5 |
else |
282788a8 |
{ |
81d882d5 |
/* turn success into failure (interrupted syscall) */
if (0 == status)
{
ASSERT(res);
freeaddrinfo(*res);
*res = NULL;
status = EAI_AGAIN; /* = temporary failure */
errno = EINTR;
}
goto done; |
282788a8 |
} |
f2d6f3bc |
}
} |
ea93a078 |
|
81d882d5 |
/* success? */
if (0 == status)
{
break;
} |
ea93a078 |
|
81d882d5 |
/* resolve lookup failed, should we
* continue or fail? */
level = msglevel;
if (resolve_retries > 0)
{
level = D_RESOLVE_ERRORS;
} |
ea93a078 |
|
81d882d5 |
msg(level,
fmt,
print_hostname,
print_servname,
gai_strerror(status)); |
f2d6f3bc |
|
81d882d5 |
if (--resolve_retries <= 0)
{
goto done;
} |
ea93a078 |
|
45b2af9c |
management_sleep(fail_wait_interval); |
ea93a078 |
}
|
81d882d5 |
ASSERT(res); |
ea93a078 |
|
81d882d5 |
/* hostname resolve succeeded */ |
f2d6f3bc |
|
81d882d5 |
/*
* Do not choose an IP Addresse by random or change the order *
* of IP addresses, doing so will break RFC 3484 address selection *
*/ |
ea93a078 |
} |
81d882d5 |
else |
ea93a078 |
{ |
81d882d5 |
/* IP address parse succeeded */ |
3c748aeb |
if (flags & GETADDR_RANDOMIZE)
{
msg(M_WARN, "WARNING: ignoring --remote-random-hostname because the hostname is an IP address");
} |
ea93a078 |
}
|
81d882d5 |
done:
if (signal_received && *signal_received) |
ea93a078 |
{ |
81d882d5 |
int level = 0;
if (flags & GETADDR_FATAL_ON_SIGNAL)
{
level = M_FATAL;
}
else if (flags & GETADDR_WARN_ON_SIGNAL)
{
level = M_WARN;
}
msg(level, "RESOLVE: signal received during DNS resolution attempt"); |
ea93a078 |
}
|
81d882d5 |
gc_free(&gc);
return status; |
ea93a078 |
}
|
6fbf66fa |
/*
* We do our own inet_aton because the glibc function
* isn't very good about error checking.
*/
int |
81d882d5 |
openvpn_inet_aton(const char *dotted_quad, struct in_addr *addr) |
6fbf66fa |
{ |
81d882d5 |
unsigned int a, b, c, d; |
6fbf66fa |
|
81d882d5 |
CLEAR(*addr);
if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) == 4)
{
if (a < 256 && b < 256 && c < 256 && d < 256)
{
addr->s_addr = htonl(a<<24 | b<<16 | c<<8 | d);
return OIA_IP; /* good dotted quad */
}
}
if (string_class(dotted_quad, CC_DIGIT|CC_DOT, 0))
{
return OIA_ERROR; /* probably a badly formatted dotted quad */
}
else |
6fbf66fa |
{ |
81d882d5 |
return OIA_HOSTNAME; /* probably a hostname */ |
6fbf66fa |
}
}
|
b4073a76 |
bool |
81d882d5 |
ip_addr_dotted_quad_safe(const char *dotted_quad) |
b4073a76 |
{ |
81d882d5 |
/* verify non-NULL */
if (!dotted_quad)
{
return false;
} |
b4073a76 |
|
81d882d5 |
/* verify length is within limits */
if (strlen(dotted_quad) > 15)
{
return false;
}
/* verify that all chars are either numeric or '.' and that no numeric
* substring is greater than 3 chars */
{
int nnum = 0;
const char *p = dotted_quad;
int c; |
b4073a76 |
|
81d882d5 |
while ((c = *p++))
{
if (c >= '0' && c <= '9')
{
++nnum;
if (nnum > 3)
{
return false;
}
}
else if (c == '.')
{
nnum = 0;
}
else
{
return false;
}
}
}
/* verify that string will convert to IP address */
{
struct in_addr a;
return openvpn_inet_aton(dotted_quad, &a) == OIA_IP;
} |
b4073a76 |
}
|
512cda46 |
bool |
81d882d5 |
ipv6_addr_safe(const char *ipv6_text_addr) |
512cda46 |
{ |
81d882d5 |
/* verify non-NULL */
if (!ipv6_text_addr)
{
return false;
} |
512cda46 |
|
81d882d5 |
/* verify length is within limits */
if (strlen(ipv6_text_addr) > INET6_ADDRSTRLEN)
{
return false;
} |
512cda46 |
|
81d882d5 |
/* verify that string will convert to IPv6 address */
{
struct in6_addr a6;
return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
} |
512cda46 |
}
|
b4b5c311 |
static bool |
81d882d5 |
dns_addr_safe(const char *addr) |
b4b5c311 |
{ |
81d882d5 |
if (addr) |
b4b5c311 |
{ |
81d882d5 |
const size_t len = strlen(addr);
return len > 0 && len <= 255 && string_class(addr, CC_ALNUM|CC_DASH|CC_DOT, 0);
}
else
{
return false; |
b4b5c311 |
}
}
|
0a838de8 |
bool |
81d882d5 |
ip_or_dns_addr_safe(const char *addr, const bool allow_fqdn) |
0a838de8 |
{ |
81d882d5 |
if (ip_addr_dotted_quad_safe(addr))
{
return true;
}
else if (allow_fqdn)
{
return dns_addr_safe(addr);
}
else
{
return false;
} |
0a838de8 |
}
|
6e2c457d |
bool |
81d882d5 |
mac_addr_safe(const char *mac_addr) |
6e2c457d |
{ |
81d882d5 |
/* verify non-NULL */
if (!mac_addr)
{
return false;
} |
6e2c457d |
|
81d882d5 |
/* verify length is within limits */
if (strlen(mac_addr) > 17)
{
return false;
}
/* verify that all chars are either alphanumeric or ':' and that no
* alphanumeric substring is greater than 2 chars */
{
int nnum = 0;
const char *p = mac_addr;
int c;
while ((c = *p++))
{
if ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') )
{
++nnum;
if (nnum > 2)
{
return false;
}
}
else if (c == ':')
{
nnum = 0;
}
else
{
return false;
}
}
} |
6e2c457d |
|
81d882d5 |
/* error-checking is left to script invoked in lladdr.c */
return true; |
6e2c457d |
}
|
6fbf66fa |
static int |
81d882d5 |
socket_get_sndbuf(int sd) |
6fbf66fa |
{
#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF) |
81d882d5 |
int val;
socklen_t len; |
6fbf66fa |
|
81d882d5 |
len = sizeof(val);
if (getsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &val, &len) == 0
&& len == sizeof(val))
{
return val;
} |
6fbf66fa |
#endif |
81d882d5 |
return 0; |
6fbf66fa |
}
static void |
81d882d5 |
socket_set_sndbuf(int sd, int size) |
6fbf66fa |
{
#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF) |
81d882d5 |
if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof(size)) != 0) |
6fbf66fa |
{ |
81d882d5 |
msg(M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size); |
6fbf66fa |
}
#endif
}
static int |
81d882d5 |
socket_get_rcvbuf(int sd) |
6fbf66fa |
{
#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF) |
81d882d5 |
int val;
socklen_t len; |
6fbf66fa |
|
81d882d5 |
len = sizeof(val);
if (getsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *) &val, &len) == 0
&& len == sizeof(val))
{
return val;
} |
6fbf66fa |
#endif |
81d882d5 |
return 0; |
6fbf66fa |
}
static bool |
81d882d5 |
socket_set_rcvbuf(int sd, int size) |
6fbf66fa |
{
#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF) |
81d882d5 |
if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof(size)) != 0) |
6fbf66fa |
{ |
81d882d5 |
msg(M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size);
return false; |
6fbf66fa |
} |
81d882d5 |
return true; |
6fbf66fa |
#endif
}
static void |
81d882d5 |
socket_set_buffers(int fd, const struct socket_buffer_size *sbs) |
6fbf66fa |
{ |
81d882d5 |
if (sbs) |
6fbf66fa |
{ |
81d882d5 |
const int sndbuf_old = socket_get_sndbuf(fd);
const int rcvbuf_old = socket_get_rcvbuf(fd);
if (sbs->sndbuf)
{
socket_set_sndbuf(fd, sbs->sndbuf);
} |
6fbf66fa |
|
81d882d5 |
if (sbs->rcvbuf)
{
socket_set_rcvbuf(fd, sbs->rcvbuf);
} |
6fbf66fa |
|
81d882d5 |
msg(D_OSBUF, "Socket Buffers: R=[%d->%d] S=[%d->%d]",
rcvbuf_old,
socket_get_rcvbuf(fd),
sndbuf_old,
socket_get_sndbuf(fd)); |
6fbf66fa |
}
}
/* |
00d39170 |
* Set other socket options
*/
static bool |
81d882d5 |
socket_set_tcp_nodelay(int sd, int state) |
00d39170 |
{ |
445b192a |
#if defined(_WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) |
81d882d5 |
if (setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof(state)) != 0) |
00d39170 |
{ |
81d882d5 |
msg(M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state);
return false; |
00d39170 |
} |
81d882d5 |
else |
00d39170 |
{ |
81d882d5 |
dmsg(D_OSBUF, "Socket flags: TCP_NODELAY=%d succeeded", state);
return true; |
00d39170 |
} |
81d882d5 |
#else /* if defined(_WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY)) */
msg(M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed (No kernel support)", state);
return false; |
00d39170 |
#endif
}
|
032f0045 |
static inline void |
81d882d5 |
socket_set_mark(int sd, int mark) |
d90428d1 |
{ |
51bd56f4 |
#if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK |
81d882d5 |
if (mark && setsockopt(sd, SOL_SOCKET, SO_MARK, (void *) &mark, sizeof(mark)) != 0)
{
msg(M_WARN, "NOTE: setsockopt SO_MARK=%d failed", mark);
} |
d90428d1 |
#endif
}
|
00d39170 |
static bool |
81d882d5 |
socket_set_flags(int sd, unsigned int sockflags) |
00d39170 |
{ |
81d882d5 |
if (sockflags & SF_TCP_NODELAY)
{
return socket_set_tcp_nodelay(sd, 1);
}
else
{
return true;
} |
00d39170 |
}
bool |
81d882d5 |
link_socket_update_flags(struct link_socket *ls, unsigned int sockflags) |
00d39170 |
{ |
81d882d5 |
if (ls && socket_defined(ls->sd))
{
return socket_set_flags(ls->sd, ls->sockflags = sockflags);
}
else
{
return false;
} |
00d39170 |
}
void |
81d882d5 |
link_socket_update_buffer_sizes(struct link_socket *ls, int rcvbuf, int sndbuf) |
00d39170 |
{ |
81d882d5 |
if (ls && socket_defined(ls->sd)) |
00d39170 |
{ |
81d882d5 |
ls->socket_buffer_sizes.sndbuf = sndbuf;
ls->socket_buffer_sizes.rcvbuf = rcvbuf;
socket_set_buffers(ls->sd, &ls->socket_buffer_sizes); |
00d39170 |
}
}
/* |
6fbf66fa |
* SOCKET INITALIZATION CODE.
* Create a TCP/UDP socket
*/
socket_descriptor_t |
81d882d5 |
create_socket_tcp(struct addrinfo *addrinfo) |
6fbf66fa |
{ |
81d882d5 |
socket_descriptor_t sd; |
6fbf66fa |
|
81d882d5 |
ASSERT(addrinfo);
ASSERT(addrinfo->ai_socktype == SOCK_STREAM); |
7dbe04de |
|
81d882d5 |
if ((sd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0)
{
msg(M_ERR, "Cannot create TCP socket");
} |
6fbf66fa |
|
445b192a |
#ifndef _WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */ |
81d882d5 |
/* set SO_REUSEADDR on socket */
{
int on = 1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
(void *) &on, sizeof(on)) < 0)
{
msg(M_ERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP socket");
}
} |
7d770f1e |
#endif |
6fbf66fa |
|
81d882d5 |
/* set socket file descriptor to not pass across execs, so that
* scripts don't have access to it */
set_cloexec(sd); |
e35a7883 |
|
81d882d5 |
return sd; |
6fbf66fa |
}
static socket_descriptor_t |
81d882d5 |
create_socket_udp(struct addrinfo *addrinfo, const unsigned int flags) |
6fbf66fa |
{ |
81d882d5 |
socket_descriptor_t sd; |
6fbf66fa |
|
81d882d5 |
ASSERT(addrinfo);
ASSERT(addrinfo->ai_socktype == SOCK_DGRAM); |
7dbe04de |
|
81d882d5 |
if ((sd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0)
{
msg(M_ERR, "UDP: Cannot create UDP/UDP6 socket");
} |
8bc93d7f |
#if ENABLE_IP_PKTINFO |
81d882d5 |
else if (flags & SF_USE_IP_PKTINFO) |
8bc93d7f |
{ |
81d882d5 |
int pad = 1;
if (addrinfo->ai_family == AF_INET) |
23d61c56 |
{ |
7efa60d9 |
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) |
81d882d5 |
if (setsockopt(sd, SOL_IP, IP_PKTINFO,
(void *)&pad, sizeof(pad)) < 0)
{
msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO");
} |
d3774cdf |
#elif defined(IP_RECVDSTADDR) |
81d882d5 |
if (setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR,
(void *)&pad, sizeof(pad)) < 0)
{
msg(M_ERR, "UDP: failed setsockopt for IP_RECVDSTADDR");
}
#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ |
d3774cdf |
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif |
23d61c56 |
} |
81d882d5 |
else if (addrinfo->ai_family == AF_INET6) |
23d61c56 |
{ |
fe8a7f0c |
#ifndef IPV6_RECVPKTINFO /* Some older Darwin platforms require this */ |
81d882d5 |
if (setsockopt(sd, IPPROTO_IPV6, IPV6_PKTINFO,
(void *)&pad, sizeof(pad)) < 0) |
fe8a7f0c |
#else |
81d882d5 |
if (setsockopt(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
(void *)&pad, sizeof(pad)) < 0) |
fe8a7f0c |
#endif |
81d882d5 |
{ msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");} |
23d61c56 |
} |
8335caf9 |
} |
81d882d5 |
#endif /* if ENABLE_IP_PKTINFO */ |
e35a7883 |
|
81d882d5 |
/* set socket file descriptor to not pass across execs, so that
* scripts don't have access to it */
set_cloexec(sd); |
e35a7883 |
|
81d882d5 |
return sd; |
8335caf9 |
}
|
81d882d5 |
static void
bind_local(struct link_socket *sock, const sa_family_t ai_family) |
6fbf66fa |
{ |
7dbe04de |
/* bind to local address/port */
if (sock->bind_local) |
81d882d5 |
{ |
7dbe04de |
if (sock->socks_proxy && sock->info.proto == PROTO_UDP) |
81d882d5 |
{
socket_bind(sock->ctrl_sd, sock->info.lsa->bind_local,
ai_family, "SOCKS", false);
} |
7dbe04de |
else |
81d882d5 |
{
socket_bind(sock->sd, sock->info.lsa->bind_local,
ai_family,
"TCP/UDP", sock->info.bind_ipv6_only);
}
} |
7dbe04de |
} |
23d61c56 |
|
7dbe04de |
static void |
81d882d5 |
create_socket(struct link_socket *sock, struct addrinfo *addr) |
7dbe04de |
{ |
81d882d5 |
if (addr->ai_protocol == IPPROTO_UDP || addr->ai_socktype == SOCK_DGRAM) |
6fbf66fa |
{ |
81d882d5 |
sock->sd = create_socket_udp(addr, sock->sockflags);
sock->sockflags |= SF_GETADDRINFO_DGRAM; |
6fbf66fa |
|
81d882d5 |
/* Assume that control socket and data socket to the socks proxy
* are using the same IP family */
if (sock->socks_proxy)
{
/* Construct a temporary addrinfo to create the socket,
* currently resolve two remote addresses is not supported,
* TODO: Rewrite the whole resolve_remote */
struct addrinfo addrinfo_tmp = *addr;
addrinfo_tmp.ai_socktype = SOCK_STREAM;
addrinfo_tmp.ai_protocol = IPPROTO_TCP;
sock->ctrl_sd = create_socket_tcp(&addrinfo_tmp);
} |
6fbf66fa |
} |
81d882d5 |
else if (addr->ai_protocol == IPPROTO_TCP || addr->ai_socktype == SOCK_STREAM) |
8335caf9 |
{ |
81d882d5 |
sock->sd = create_socket_tcp(addr); |
8335caf9 |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
ASSERT(0); |
6fbf66fa |
} |
23d61c56 |
/* set socket buffers based on --sndbuf and --rcvbuf options */ |
81d882d5 |
socket_set_buffers(sock->sd, &sock->socket_buffer_sizes); |
23d61c56 |
/* set socket to --mark packets with given value */ |
81d882d5 |
socket_set_mark(sock->sd, sock->mark); |
7dbe04de |
|
81d882d5 |
bind_local(sock, addr->ai_family); |
51187871 |
} |
23d61c56 |
|
a55b3cdb |
#ifdef TARGET_ANDROID |
81d882d5 |
static void
protect_fd_nonlocal(int fd, const struct sockaddr *addr) |
51187871 |
{ |
81d882d5 |
/* pass socket FD to management interface to pass on to VPNService API
* as "protected socket" (exempt from being routed into tunnel)
*/
if (addr_local(addr))
{
msg(D_SOCKET_DEBUG, "Address is local, not protecting socket fd %d", fd);
return;
} |
a55b3cdb |
|
81d882d5 |
msg(D_SOCKET_DEBUG, "Protecting socket fd %d", fd);
management->connection.fdtosend = fd;
management_android_control(management, "PROTECTFD", __func__); |
6fbf66fa |
} |
51187871 |
#endif |
6fbf66fa |
/*
* Functions used for establishing a TCP stream connection.
*/
static void |
81d882d5 |
socket_do_listen(socket_descriptor_t sd,
const struct addrinfo *local,
bool do_listen,
bool do_set_nonblock)
{
struct gc_arena gc = gc_new();
if (do_listen)
{
ASSERT(local);
msg(M_INFO, "Listening for incoming TCP connection on %s",
print_sockaddr(local->ai_addr, &gc));
if (listen(sd, 1))
{
msg(M_ERR, "TCP: listen() failed");
} |
6fbf66fa |
}
|
81d882d5 |
/* set socket to non-blocking mode */
if (do_set_nonblock)
{
set_nonblock(sd);
} |
6fbf66fa |
|
81d882d5 |
gc_free(&gc); |
6fbf66fa |
}
socket_descriptor_t |
81d882d5 |
socket_do_accept(socket_descriptor_t sd,
struct link_socket_actual *act,
const bool nowait) |
6fbf66fa |
{ |
81d882d5 |
/* af_addr_size WILL return 0 in this case if AFs other than AF_INET
* are compiled because act is empty here.
* could use getsockname() to support later remote_len check
*/
socklen_t remote_len_af = af_addr_size(act->dest.addr.sa.sa_family);
socklen_t remote_len = sizeof(act->dest.addr);
socket_descriptor_t new_sd = SOCKET_UNDEFINED; |
6fbf66fa |
|
81d882d5 |
CLEAR(*act); |
8bc93d7f |
|
6fbf66fa |
#ifdef HAVE_GETPEERNAME |
81d882d5 |
if (nowait) |
6fbf66fa |
{ |
81d882d5 |
new_sd = getpeername(sd, &act->dest.addr.sa, &remote_len); |
6fbf66fa |
|
81d882d5 |
if (!socket_defined(new_sd))
{
msg(D_LINK_ERRORS | M_ERRNO, "TCP: getpeername() failed");
}
else
{
new_sd = sd;
}
}
#else /* ifdef HAVE_GETPEERNAME */
if (nowait)
{
msg(M_WARN, "TCP: this OS does not provide the getpeername() function"); |
6fbf66fa |
}
#endif |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
new_sd = accept(sd, &act->dest.addr.sa, &remote_len); |
6fbf66fa |
}
|
e8c1720d |
#if 0 /* For debugging only, test the effect of accept() failures */ |
81d882d5 |
{
static int foo = 0;
++foo;
if (foo & 1)
{
new_sd = -1;
}
} |
e8c1720d |
#endif
|
81d882d5 |
if (!socket_defined(new_sd)) |
6fbf66fa |
{ |
06ad53e0 |
msg(D_LINK_ERRORS | M_ERRNO, "TCP: accept(%d) failed", (int)sd); |
6fbf66fa |
} |
81d882d5 |
/* only valid if we have remote_len_af!=0 */
else if (remote_len_af && remote_len != remote_len_af) |
6fbf66fa |
{ |
81d882d5 |
msg(D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len);
openvpn_close_socket(new_sd);
new_sd = SOCKET_UNDEFINED; |
6fbf66fa |
} |
81d882d5 |
else |
e35a7883 |
{ |
81d882d5 |
/* set socket file descriptor to not pass across execs, so that
* scripts don't have access to it */
set_cloexec(sd); |
e35a7883 |
} |
81d882d5 |
return new_sd; |
6fbf66fa |
}
static void |
81d882d5 |
tcp_connection_established(const struct link_socket_actual *act) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
msg(M_INFO, "TCP connection established with %s",
print_link_socket_actual(act, &gc));
gc_free(&gc); |
6fbf66fa |
}
|
33e1a869 |
static socket_descriptor_t |
81d882d5 |
socket_listen_accept(socket_descriptor_t sd,
struct link_socket_actual *act,
const char *remote_dynamic,
const struct addrinfo *local,
bool do_listen,
bool nowait,
volatile int *signal_received)
{
struct gc_arena gc = gc_new();
/* struct openvpn_sockaddr *remote = &act->dest; */
struct openvpn_sockaddr remote_verify = act->dest; |
33e1a869 |
socket_descriptor_t new_sd = SOCKET_UNDEFINED; |
81d882d5 |
CLEAR(*act);
socket_do_listen(sd, local, do_listen, true);
while (true)
{
int status;
fd_set reads;
struct timeval tv;
FD_ZERO(&reads);
openvpn_fd_set(sd, &reads);
tv.tv_sec = 0;
tv.tv_usec = 0;
status = select(sd + 1, &reads, NULL, NULL, &tv);
get_signal(signal_received);
if (*signal_received)
{
gc_free(&gc);
return sd;
}
if (status < 0)
{
msg(D_LINK_ERRORS | M_ERRNO, "TCP: select() failed");
}
if (status <= 0)
{ |
45b2af9c |
management_sleep(1); |
81d882d5 |
continue;
}
new_sd = socket_do_accept(sd, act, nowait);
if (socket_defined(new_sd))
{
struct addrinfo *ai = NULL;
if (remote_dynamic)
{
openvpn_getaddrinfo(0, remote_dynamic, NULL, 1, NULL, |
23d61c56 |
remote_verify.addr.sa.sa_family, &ai); |
81d882d5 |
} |
23d61c56 |
|
81d882d5 |
if (ai && !addrlist_match(&remote_verify, ai)) |
23d61c56 |
{ |
81d882d5 |
msg(M_WARN,
"TCP NOTE: Rejected connection attempt from %s due to --remote setting",
print_link_socket_actual(act, &gc));
if (openvpn_close_socket(new_sd))
{
msg(M_ERR, "TCP: close socket failed (new_sd)");
}
freeaddrinfo(ai); |
23d61c56 |
} |
81d882d5 |
else |
23d61c56 |
{ |
81d882d5 |
if (ai)
{
freeaddrinfo(ai);
}
break; |
23d61c56 |
} |
81d882d5 |
} |
45b2af9c |
management_sleep(1); |
6fbf66fa |
}
|
81d882d5 |
if (!nowait && openvpn_close_socket(sd))
{
msg(M_ERR, "TCP: close socket failed (sd)");
} |
6fbf66fa |
|
81d882d5 |
tcp_connection_established(act); |
6fbf66fa |
|
81d882d5 |
gc_free(&gc);
return new_sd; |
6fbf66fa |
}
|
188a6515 |
/* older mingw versions and WinXP do not have this define,
* but Vista and up support the functionality - just define it here
*/ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
#ifndef IPV6_V6ONLY
#define IPV6_V6ONLY 27
#endif |
188a6515 |
#endif |
7ef85434 |
void |
81d882d5 |
socket_bind(socket_descriptor_t sd,
struct addrinfo *local,
int ai_family,
const char *prefix,
bool ipv6only) |
04f4b793 |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
04f4b793 |
|
81d882d5 |
/* FIXME (schwabe)
* getaddrinfo for the bind address might return multiple AF_INET/AF_INET6
* entries for the requested protocol.
* For example if an address has multiple A records
* What is the correct way to deal with it?
*/ |
23d61c56 |
|
81d882d5 |
struct addrinfo *cur; |
23d61c56 |
|
81d882d5 |
ASSERT(local); |
8832c6c4 |
|
81d882d5 |
/* find the first addrinfo with correct ai_family */
for (cur = local; cur; cur = cur->ai_next)
{
if (cur->ai_family == ai_family)
{
break;
}
}
if (!cur) |
23d61c56 |
{ |
81d882d5 |
msg(M_FATAL, "%s: Socket bind failed: Addr to bind has no %s record",
prefix, addr_family_name(ai_family)); |
23d61c56 |
}
|
81d882d5 |
if (ai_family == AF_INET6) |
8832c6c4 |
{ |
81d882d5 |
int v6only = ipv6only ? 1 : 0; /* setsockopt must have an "int" */ |
8832c6c4 |
|
81d882d5 |
msg(M_INFO, "setsockopt(IPV6_V6ONLY=%d)", v6only);
if (setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only)))
{
msg(M_NONFATAL|M_ERRNO, "Setting IPV6_V6ONLY=%d failed", v6only);
} |
8832c6c4 |
} |
81d882d5 |
if (bind(sd, cur->ai_addr, cur->ai_addrlen)) |
04f4b793 |
{ |
56b396dc |
msg(M_FATAL | M_ERRNO, "%s: Socket bind failed on local address %s", |
81d882d5 |
prefix, |
56b396dc |
print_sockaddr_ex(local->ai_addr, ":", PS_SHOW_PORT, &gc)); |
04f4b793 |
} |
81d882d5 |
gc_free(&gc); |
04f4b793 |
}
|
4f404ad3 |
int |
81d882d5 |
openvpn_connect(socket_descriptor_t sd,
const struct sockaddr *remote,
int connect_timeout,
volatile int *signal_received) |
1ae9d051 |
{ |
81d882d5 |
int status = 0; |
1ae9d051 |
|
51187871 |
#ifdef TARGET_ANDROID |
81d882d5 |
protect_fd_nonlocal(sd, remote); |
51187871 |
#endif
|
1ae9d051 |
#ifdef CONNECT_NONBLOCK |
81d882d5 |
set_nonblock(sd);
status = connect(sd, remote, af_addr_size(remote->sa_family));
if (status)
{
status = openvpn_errno();
}
if ( |
445b192a |
#ifdef _WIN32 |
81d882d5 |
status == WSAEWOULDBLOCK |
9081e0ad |
#else |
81d882d5 |
status == EINPROGRESS |
9081e0ad |
#endif |
81d882d5 |
) |
1ae9d051 |
{ |
81d882d5 |
while (true)
{ |
007738e9 |
#if POLL |
81d882d5 |
struct pollfd fds[1];
fds[0].fd = sd;
fds[0].events = POLLOUT;
status = poll(fds, 1, 0); |
007738e9 |
#else |
81d882d5 |
fd_set writes;
struct timeval tv; |
1ae9d051 |
|
81d882d5 |
FD_ZERO(&writes);
openvpn_fd_set(sd, &writes);
tv.tv_sec = 0;
tv.tv_usec = 0; |
1ae9d051 |
|
81d882d5 |
status = select(sd + 1, NULL, &writes, NULL, &tv); |
007738e9 |
#endif |
81d882d5 |
if (signal_received)
{
get_signal(signal_received);
if (*signal_received)
{
status = 0;
break;
}
}
if (status < 0)
{
status = openvpn_errno();
break;
}
if (status <= 0)
{
if (--connect_timeout < 0)
{ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
status = WSAETIMEDOUT; |
5f5229e4 |
#else |
81d882d5 |
status = ETIMEDOUT; |
5f5229e4 |
#endif |
81d882d5 |
break;
} |
45b2af9c |
management_sleep(1); |
81d882d5 |
continue;
}
/* got it */
{
int val = 0;
socklen_t len;
len = sizeof(val);
if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (void *) &val, &len) == 0
&& len == sizeof(val))
{
status = val;
}
else
{
status = openvpn_errno();
}
break;
}
} |
1ae9d051 |
} |
81d882d5 |
#else /* ifdef CONNECT_NONBLOCK */
status = connect(sd, remote, af_addr_size(remote->sa_family));
if (status)
{
status = openvpn_errno();
}
#endif /* ifdef CONNECT_NONBLOCK */ |
1ae9d051 |
|
81d882d5 |
return status; |
1ae9d051 |
}
|
81d882d5 |
void
set_actual_address(struct link_socket_actual *actual, struct addrinfo *ai) |
23d61c56 |
{ |
81d882d5 |
CLEAR(*actual);
ASSERT(ai); |
23d61c56 |
if (ai->ai_family == AF_INET) |
81d882d5 |
{ |
23d61c56 |
actual->dest.addr.in4 = |
81d882d5 |
*((struct sockaddr_in *) ai->ai_addr);
} |
23d61c56 |
else if (ai->ai_family == AF_INET6) |
81d882d5 |
{ |
23d61c56 |
actual->dest.addr.in6 = |
81d882d5 |
*((struct sockaddr_in6 *) ai->ai_addr);
} |
23d61c56 |
else |
81d882d5 |
{ |
23d61c56 |
ASSERT(0); |
81d882d5 |
} |
23d61c56 |
}
|
72bcdfdc |
static void |
81d882d5 |
socket_connect(socket_descriptor_t *sd,
const struct sockaddr *dest,
const int connect_timeout,
struct signal_info *sig_info) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int status; |
6fbf66fa |
|
1ae9d051 |
#ifdef CONNECT_NONBLOCK |
81d882d5 |
msg(M_INFO, "Attempting to establish TCP connection with %s [nonblock]",
print_sockaddr(dest, &gc)); |
1ae9d051 |
#else |
81d882d5 |
msg(M_INFO, "Attempting to establish TCP connection with %s",
print_sockaddr(dest, &gc)); |
1ae9d051 |
#endif
|
e482a632 |
#ifdef ENABLE_MANAGEMENT |
81d882d5 |
if (management)
{
management_set_state(management,
OPENVPN_STATE_TCP_CONNECT,
NULL,
NULL,
NULL,
NULL,
NULL);
} |
e482a632 |
#endif
|
81d882d5 |
/* Set the actual address */
status = openvpn_connect(*sd, dest, connect_timeout, &sig_info->signal_received); |
a17f6969 |
|
81d882d5 |
get_signal(&sig_info->signal_received);
if (sig_info->signal_received)
{
goto done;
} |
a17f6969 |
|
81d882d5 |
if (status)
{ |
97549c67 |
|
fd2a29ab |
msg(D_LINK_ERRORS, "TCP: connect to %s failed: %s",
print_sockaddr(dest, &gc), strerror(status)); |
6fbf66fa |
|
81d882d5 |
openvpn_close_socket(*sd);
*sd = SOCKET_UNDEFINED;
sig_info->signal_received = SIGUSR1;
sig_info->source = SIG_SOURCE_CONNECTION_FAILED;
}
else
{
msg(M_INFO, "TCP connection established with %s",
print_sockaddr(dest, &gc));
} |
6fbf66fa |
|
81d882d5 |
done:
gc_free(&gc); |
6fbf66fa |
}
/* For stream protocols, allocate a buffer to build up packet. |
81d882d5 |
* Called after frame has been finalized. */ |
6fbf66fa |
static void |
81d882d5 |
socket_frame_init(const struct frame *frame, struct link_socket *sock) |
6fbf66fa |
{ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
overlapped_io_init(&sock->reads, frame, FALSE, false);
overlapped_io_init(&sock->writes, frame, TRUE, false);
sock->rw_handle.read = sock->reads.overlapped.hEvent;
sock->rw_handle.write = sock->writes.overlapped.hEvent; |
6fbf66fa |
#endif
|
81d882d5 |
if (link_socket_connection_oriented(sock)) |
6fbf66fa |
{ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
stream_buf_init(&sock->stream_buf,
&sock->reads.buf_init,
sock->sockflags,
sock->info.proto); |
6fbf66fa |
#else |
81d882d5 |
alloc_buf_sock_tun(&sock->stream_buf_data,
frame,
false,
FRAME_HEADROOM_MARKER_READ_STREAM);
stream_buf_init(&sock->stream_buf,
&sock->stream_buf_data,
sock->sockflags,
sock->info.proto); |
6fbf66fa |
#endif
}
}
/*
* Adjust frame structure based on a Path MTU value given
* to us by the OS.
*/
void |
81d882d5 |
frame_adjust_path_mtu(struct frame *frame, int pmtu, int proto) |
6fbf66fa |
{ |
81d882d5 |
frame_set_mtu_dynamic(frame, pmtu - datagram_overhead(proto), SET_MTU_UPPER_BOUND); |
6fbf66fa |
}
static void |
81d882d5 |
resolve_bind_local(struct link_socket *sock, const sa_family_t af) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
6fbf66fa |
|
81d882d5 |
/* resolve local address if undefined */
if (!sock->info.lsa->bind_local) |
d9c04efc |
{ |
81d882d5 |
int flags = GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL
|GETADDR_FATAL | GETADDR_PASSIVE;
int status; |
23d61c56 |
|
81d882d5 |
if (proto_is_dgram(sock->info.proto))
{
flags |= GETADDR_DATAGRAM;
} |
23d61c56 |
|
81d882d5 |
/* will return AF_{INET|INET6}from local_host */
status = get_cached_dns_entry(sock->dns_cache,
sock->local_host,
sock->local_port,
af,
flags,
&sock->info.lsa->bind_local); |
e719a053 |
|
81d882d5 |
if (status)
{
status = openvpn_getaddrinfo(flags, sock->local_host, sock->local_port, 0,
NULL, af, &sock->info.lsa->bind_local);
} |
23d61c56 |
|
81d882d5 |
if (status !=0)
{
msg(M_FATAL, "getaddrinfo() failed for local \"%s:%s\": %s",
sock->local_host, sock->local_port,
gai_strerror(status));
} |
6fbf66fa |
} |
076fd3e4 |
|
81d882d5 |
gc_free(&gc); |
23d61c56 |
}
|
6fbf66fa |
static void |
81d882d5 |
resolve_remote(struct link_socket *sock,
int phase,
const char **remote_dynamic,
volatile int *signal_received)
{
struct gc_arena gc = gc_new();
/* resolve remote address if undefined */
if (!sock->info.lsa->remote_list)
{
if (sock->remote_host)
{
unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags);
int retry = 0;
int status = -1;
struct addrinfo *ai;
if (proto_is_dgram(sock->info.proto))
{
flags |= GETADDR_DATAGRAM;
}
if (sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE)
{
if (phase == 2)
{
flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL);
}
retry = 0;
}
else if (phase == 1)
{
if (sock->resolve_retry_seconds)
{
retry = 0;
}
else
{
flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY);
retry = 0;
}
}
else if (phase == 2)
{
if (sock->resolve_retry_seconds)
{
flags |= GETADDR_FATAL;
retry = sock->resolve_retry_seconds;
}
else
{
ASSERT(0);
}
}
else
{
ASSERT(0);
}
status = get_cached_dns_entry(sock->dns_cache,
sock->remote_host,
sock->remote_port,
sock->info.af,
flags, &ai);
if (status)
{
status = openvpn_getaddrinfo(flags, sock->remote_host, sock->remote_port,
retry, signal_received, sock->info.af, &ai);
}
if (status == 0)
{
sock->info.lsa->remote_list = ai;
sock->info.lsa->current_remote = ai;
dmsg(D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
flags,
phase,
retry,
signal_received ? *signal_received : -1,
status);
}
if (signal_received)
{
if (*signal_received)
{
goto done;
}
}
if (status!=0)
{
if (signal_received)
{
*signal_received = SIGUSR1;
}
goto done;
}
}
}
/* should we re-use previous active remote address? */
if (link_socket_actual_defined(&sock->info.lsa->actual))
{
msg(M_INFO, "TCP/UDP: Preserving recently used remote address: %s",
print_link_socket_actual(&sock->info.lsa->actual, &gc));
if (remote_dynamic)
{
*remote_dynamic = NULL;
}
}
else
{
CLEAR(sock->info.lsa->actual);
if (sock->info.lsa->current_remote)
{
set_actual_address(&sock->info.lsa->actual,
sock->info.lsa->current_remote);
}
}
done:
gc_free(&gc); |
6fbf66fa |
}
|
23d61c56 |
|
6fbf66fa |
struct link_socket * |
81d882d5 |
link_socket_new(void) |
6fbf66fa |
{ |
81d882d5 |
struct link_socket *sock; |
6fbf66fa |
|
81d882d5 |
ALLOC_OBJ_CLEAR(sock, struct link_socket);
sock->sd = SOCKET_UNDEFINED;
sock->ctrl_sd = SOCKET_UNDEFINED;
return sock; |
6fbf66fa |
}
|
48f1d41a |
void |
81d882d5 |
link_socket_init_phase1(struct link_socket *sock,
const char *local_host,
const char *local_port,
const char *remote_host,
const char *remote_port,
struct cached_dns_entry *dns_cache,
int proto,
sa_family_t af,
bool bind_ipv6_only,
int mode,
const struct link_socket *accept_from,
struct http_proxy_info *http_proxy,
struct socks_proxy_info *socks_proxy, |
6fbf66fa |
#ifdef ENABLE_DEBUG |
81d882d5 |
int gremlin, |
6fbf66fa |
#endif |
81d882d5 |
bool bind_local,
bool remote_float,
int inetd,
struct link_socket_addr *lsa,
const char *ipchange_command,
const struct plugin_list *plugins,
int resolve_retry_seconds,
int mtu_discover_type,
int rcvbuf,
int sndbuf,
int mark,
struct event_timeout *server_poll_timeout,
unsigned int sockflags)
{
ASSERT(sock);
sock->local_host = local_host;
sock->local_port = local_port;
sock->remote_host = remote_host;
sock->remote_port = remote_port;
sock->dns_cache = dns_cache;
sock->http_proxy = http_proxy;
sock->socks_proxy = socks_proxy;
sock->bind_local = bind_local;
sock->inetd = inetd;
sock->resolve_retry_seconds = resolve_retry_seconds;
sock->mtu_discover_type = mtu_discover_type; |
6fbf66fa |
#ifdef ENABLE_DEBUG |
81d882d5 |
sock->gremlin = gremlin; |
6fbf66fa |
#endif
|
81d882d5 |
sock->socket_buffer_sizes.rcvbuf = rcvbuf;
sock->socket_buffer_sizes.sndbuf = sndbuf; |
6fbf66fa |
|
81d882d5 |
sock->sockflags = sockflags;
sock->mark = mark; |
00d39170 |
|
81d882d5 |
sock->info.proto = proto;
sock->info.af = af;
sock->info.remote_float = remote_float;
sock->info.lsa = lsa;
sock->info.bind_ipv6_only = bind_ipv6_only;
sock->info.ipchange_command = ipchange_command;
sock->info.plugins = plugins;
sock->server_poll_timeout = server_poll_timeout; |
6fbf66fa |
|
81d882d5 |
sock->mode = mode;
if (mode == LS_MODE_TCP_ACCEPT_FROM) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(accept_from);
ASSERT(sock->info.proto == PROTO_TCP_SERVER);
ASSERT(!sock->inetd);
sock->sd = accept_from->sd; |
682e7fea |
/* inherit (possibly guessed) info AF from parent context */
sock->info.af = accept_from->info.af; |
6fbf66fa |
}
|
81d882d5 |
/* are we running in HTTP proxy mode? */
if (sock->http_proxy) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(sock->info.proto == PROTO_TCP_CLIENT);
ASSERT(!sock->inetd); |
6fbf66fa |
|
81d882d5 |
/* the proxy server */
sock->remote_host = http_proxy->options.server;
sock->remote_port = http_proxy->options.port; |
6fbf66fa |
|
81d882d5 |
/* the OpenVPN server we will use the proxy to connect to */
sock->proxy_dest_host = remote_host;
sock->proxy_dest_port = remote_port; |
6fbf66fa |
} |
81d882d5 |
/* or in Socks proxy mode? */
else if (sock->socks_proxy) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(!sock->inetd); |
6fbf66fa |
|
81d882d5 |
/* the proxy server */
sock->remote_host = socks_proxy->server;
sock->remote_port = socks_proxy->port; |
6fbf66fa |
|
81d882d5 |
/* the OpenVPN server we will use the proxy to connect to */
sock->proxy_dest_host = remote_host;
sock->proxy_dest_port = remote_port; |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
sock->remote_host = remote_host;
sock->remote_port = remote_port; |
6fbf66fa |
}
|
81d882d5 |
/* bind behavior for TCP server vs. client */
if (sock->info.proto == PROTO_TCP_SERVER) |
6fbf66fa |
{ |
81d882d5 |
if (sock->mode == LS_MODE_TCP_ACCEPT_FROM)
{
sock->bind_local = false;
}
else
{
sock->bind_local = true;
} |
6fbf66fa |
}
|
81d882d5 |
/* were we started by inetd or xinetd? */
if (sock->inetd) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(sock->info.proto != PROTO_TCP_CLIENT);
ASSERT(socket_defined(inetd_socket_descriptor));
sock->sd = inetd_socket_descriptor;
set_cloexec(sock->sd); /* not created by create_socket*() */ |
6fbf66fa |
} |
81d882d5 |
else if (mode != LS_MODE_TCP_ACCEPT_FROM) |
6fbf66fa |
{ |
81d882d5 |
if (sock->bind_local)
{
resolve_bind_local(sock, sock->info.af);
}
resolve_remote(sock, 1, NULL, NULL); |
48f1d41a |
}
}
static |
81d882d5 |
void
phase2_inetd(struct link_socket *sock, const struct frame *frame,
const char *remote_dynamic, volatile int *signal_received) |
48f1d41a |
{ |
81d882d5 |
bool remote_changed = false; |
48f1d41a |
|
81d882d5 |
if (sock->info.proto == PROTO_TCP_SERVER) |
48f1d41a |
{ |
81d882d5 |
/* AF_INET as default (and fallback) for inetd */
sock->info.lsa->actual.dest.addr.sa.sa_family = AF_INET;
#ifdef HAVE_GETSOCKNAME
{
/* inetd: hint family type for dest = local's */
struct openvpn_sockaddr local_addr;
socklen_t addrlen = sizeof(local_addr);
if (getsockname(sock->sd, &local_addr.addr.sa, &addrlen) == 0)
{
sock->info.lsa->actual.dest.addr.sa.sa_family = local_addr.addr.sa.sa_family;
dmsg(D_SOCKET_DEBUG, "inetd(%s): using sa_family=%d from getsockname(%d)",
proto2ascii(sock->info.proto, sock->info.af, false), |
06ad53e0 |
local_addr.addr.sa.sa_family, (int)sock->sd); |
81d882d5 |
}
else
{
msg(M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET", |
06ad53e0 |
proto2ascii(sock->info.proto, sock->info.af, false), (int)sock->sd); |
81d882d5 |
}
}
#else /* ifdef HAVE_GETSOCKNAME */
msg(M_WARN, "inetd(%s): this OS does not provide the getsockname() "
"function, using AF_INET",
proto2ascii(sock->info.proto, false));
#endif /* ifdef HAVE_GETSOCKNAME */
sock->sd =
socket_listen_accept(sock->sd,
&sock->info.lsa->actual,
remote_dynamic,
sock->info.lsa->bind_local,
false,
sock->inetd == INETD_NOWAIT,
signal_received); |
23d61c56 |
|
81d882d5 |
}
ASSERT(!remote_changed); |
48f1d41a |
} |
2b9b4c8a |
|
48f1d41a |
static void |
81d882d5 |
phase2_set_socket_flags(struct link_socket *sock) |
48f1d41a |
{ |
81d882d5 |
/* set misc socket parameters */
socket_set_flags(sock->sd, sock->sockflags); |
48f1d41a |
|
81d882d5 |
/* set socket to non-blocking mode */
set_nonblock(sock->sd); |
48f1d41a |
|
81d882d5 |
/* set Path MTU discovery options on the socket */
set_mtu_discover_type(sock->sd, sock->mtu_discover_type, sock->info.af); |
48f1d41a |
#if EXTENDED_SOCKET_ERROR_CAPABILITY |
81d882d5 |
/* if the OS supports it, enable extended error passing on the socket */
set_sock_extended_error_passing(sock->sd); |
48f1d41a |
#endif
} |
2b9b4c8a |
|
d90428d1 |
|
48f1d41a |
static void |
81d882d5 |
linksock_print_addr(struct link_socket *sock)
{
struct gc_arena gc = gc_new();
const int msglevel = (sock->mode == LS_MODE_TCP_ACCEPT_FROM) ? D_INIT_MEDIUM : M_INFO;
/* print local address */
if (sock->inetd)
{
msg(msglevel, "%s link local: [inetd]", proto2ascii(sock->info.proto, sock->info.af, true));
}
else if (sock->bind_local)
{
sa_family_t ai_family = sock->info.lsa->actual.dest.addr.sa.sa_family;
/* Socket is always bound on the first matching address,
* For bound sockets with no remote addr this is the element of
* the list */
struct addrinfo *cur;
for (cur = sock->info.lsa->bind_local; cur; cur = cur->ai_next)
{
if (!ai_family || ai_family == cur->ai_family)
{
break;
}
}
ASSERT(cur);
msg(msglevel, "%s link local (bound): %s",
proto2ascii(sock->info.proto, sock->info.af, true),
print_sockaddr(cur->ai_addr,&gc));
}
else
{
msg(msglevel, "%s link local: (not bound)",
proto2ascii(sock->info.proto, sock->info.af, true));
}
/* print active remote address */
msg(msglevel, "%s link remote: %s",
proto2ascii(sock->info.proto, sock->info.af, true),
print_link_socket_actual_ex(&sock->info.lsa->actual,
":",
PS_SHOW_PORT_IF_DEFINED,
&gc));
gc_free(&gc); |
48f1d41a |
}
static void |
81d882d5 |
phase2_tcp_server(struct link_socket *sock, const char *remote_dynamic,
volatile int *signal_received)
{
switch (sock->mode)
{
case LS_MODE_DEFAULT:
sock->sd = socket_listen_accept(sock->sd,
&sock->info.lsa->actual,
remote_dynamic,
sock->info.lsa->bind_local,
true,
false,
signal_received);
break;
case LS_MODE_TCP_LISTEN:
socket_do_listen(sock->sd,
sock->info.lsa->bind_local,
true,
false);
break;
case LS_MODE_TCP_ACCEPT_FROM:
sock->sd = socket_do_accept(sock->sd,
&sock->info.lsa->actual,
false);
if (!socket_defined(sock->sd))
{
*signal_received = SIGTERM;
return;
}
tcp_connection_established(&sock->info.lsa->actual);
break;
default:
ASSERT(0); |
6fbf66fa |
}
}
|
48f1d41a |
static void |
81d882d5 |
phase2_tcp_client(struct link_socket *sock, struct signal_info *sig_info) |
48f1d41a |
{ |
81d882d5 |
bool proxy_retry = false; |
4cd4899e |
do
{ |
81d882d5 |
socket_connect(&sock->sd,
sock->info.lsa->current_remote->ai_addr,
get_server_poll_remaining_time(sock->server_poll_timeout),
sig_info); |
23d61c56 |
|
81d882d5 |
if (sig_info->signal_received)
{
return;
} |
23d61c56 |
|
81d882d5 |
if (sock->http_proxy)
{
proxy_retry = establish_http_proxy_passthru(sock->http_proxy,
sock->sd,
sock->proxy_dest_host,
sock->proxy_dest_port,
sock->server_poll_timeout,
&sock->stream_buf.residual,
&sig_info->signal_received);
}
else if (sock->socks_proxy)
{
establish_socks_proxy_passthru(sock->socks_proxy,
sock->sd,
sock->proxy_dest_host,
sock->proxy_dest_port,
&sig_info->signal_received);
}
if (proxy_retry)
{
openvpn_close_socket(sock->sd);
sock->sd = create_socket_tcp(sock->info.lsa->current_remote);
} |
23d61c56 |
|
81d882d5 |
} while (proxy_retry); |
23d61c56 |
|
48f1d41a |
}
static void |
81d882d5 |
phase2_socks_client(struct link_socket *sock, struct signal_info *sig_info) |
48f1d41a |
{ |
81d882d5 |
socket_connect(&sock->ctrl_sd,
sock->info.lsa->current_remote->ai_addr,
get_server_poll_remaining_time(sock->server_poll_timeout),
sig_info); |
48f1d41a |
|
23d61c56 |
if (sig_info->signal_received) |
81d882d5 |
{
return;
} |
48f1d41a |
|
81d882d5 |
establish_socks_proxy_udpassoc(sock->socks_proxy,
sock->ctrl_sd,
sock->sd,
&sock->socks_relay.dest,
&sig_info->signal_received); |
48f1d41a |
|
23d61c56 |
if (sig_info->signal_received) |
81d882d5 |
{
return;
} |
48f1d41a |
sock->remote_host = sock->proxy_dest_host;
sock->remote_port = sock->proxy_dest_port;
addr_zero_host(&sock->info.lsa->actual.dest); |
23d61c56 |
if (sock->info.lsa->remote_list) |
81d882d5 |
{
freeaddrinfo(sock->info.lsa->remote_list);
sock->info.lsa->current_remote = NULL;
sock->info.lsa->remote_list = NULL;
} |
48f1d41a |
|
81d882d5 |
resolve_remote(sock, 1, NULL, &sig_info->signal_received); |
48f1d41a |
}
|
6fbf66fa |
/* finalize socket initialization */
void |
81d882d5 |
link_socket_init_phase2(struct link_socket *sock,
const struct frame *frame,
struct signal_info *sig_info)
{
const char *remote_dynamic = NULL;
int sig_save = 0;
ASSERT(sock);
ASSERT(sig_info);
if (sig_info->signal_received)
{
sig_save = sig_info->signal_received;
sig_info->signal_received = 0;
}
/* initialize buffers */
socket_frame_init(frame, sock);
/*
* Pass a remote name to connect/accept so that
* they can test for dynamic IP address changes
* and throw a SIGUSR1 if appropriate.
*/
if (sock->resolve_retry_seconds)
{
remote_dynamic = sock->remote_host;
}
/* were we started by inetd or xinetd? */
if (sock->inetd)
{
phase2_inetd(sock, frame, remote_dynamic, &sig_info->signal_received);
if (sig_info->signal_received)
{
goto done;
}
}
else
{
/* Second chance to resolv/create socket */
resolve_remote(sock, 2, &remote_dynamic, &sig_info->signal_received);
/* If a valid remote has been found, create the socket with its addrinfo */
if (sock->info.lsa->current_remote)
{
create_socket(sock, sock->info.lsa->current_remote);
}
/* If socket has not already been created create it now */
if (sock->sd == SOCKET_UNDEFINED)
{
/* If we have no --remote and have still not figured out the
* protocol family to use we will use the first of the bind */
if (sock->bind_local && !sock->remote_host && sock->info.lsa->bind_local)
{
/* Warn if this is because neither v4 or v6 was specified
* and we should not connect a remote */
if (sock->info.af == AF_UNSPEC)
{
msg(M_WARN, "Could not determine IPv4/IPv6 protocol. Using %s",
addr_family_name(sock->info.lsa->bind_local->ai_family));
sock->info.af = sock->info.lsa->bind_local->ai_family;
}
create_socket(sock, sock->info.lsa->bind_local);
}
}
/* Socket still undefined, give a warning and abort connection */
if (sock->sd == SOCKET_UNDEFINED)
{
msg(M_WARN, "Could not determine IPv4/IPv6 protocol");
sig_info->signal_received = SIGUSR1;
goto done;
}
if (sig_info->signal_received)
{
goto done;
}
if (sock->info.proto == PROTO_TCP_SERVER)
{
phase2_tcp_server(sock, remote_dynamic,
&sig_info->signal_received);
}
else if (sock->info.proto == PROTO_TCP_CLIENT)
{
phase2_tcp_client(sock, sig_info);
}
else if (sock->info.proto == PROTO_UDP && sock->socks_proxy)
{
phase2_socks_client(sock, sig_info);
} |
51187871 |
#ifdef TARGET_ANDROID |
81d882d5 |
if (sock->sd != -1)
{
protect_fd_nonlocal(sock->sd, &sock->info.lsa->actual.dest.addr.sa);
} |
51187871 |
#endif |
81d882d5 |
if (sig_info->signal_received)
{
goto done;
} |
6fbf66fa |
}
|
81d882d5 |
phase2_set_socket_flags(sock);
linksock_print_addr(sock); |
6fbf66fa |
|
81d882d5 |
done:
if (sig_save) |
76a59eae |
{ |
81d882d5 |
if (!sig_info->signal_received)
{
sig_info->signal_received = sig_save;
} |
76a59eae |
} |
6fbf66fa |
}
void |
81d882d5 |
link_socket_close(struct link_socket *sock) |
6fbf66fa |
{ |
81d882d5 |
if (sock) |
6fbf66fa |
{
#ifdef ENABLE_DEBUG |
81d882d5 |
const int gremlin = GREMLIN_CONNECTION_FLOOD_LEVEL(sock->gremlin); |
6fbf66fa |
#else |
81d882d5 |
const int gremlin = 0; |
6fbf66fa |
#endif
|
81d882d5 |
if (socket_defined(sock->sd))
{ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
close_net_event_win32(&sock->listen_handle, sock->sd, 0); |
6fbf66fa |
#endif |
81d882d5 |
if (!gremlin)
{
msg(D_LOW, "TCP/UDP: Closing socket");
if (openvpn_close_socket(sock->sd))
{
msg(M_WARN | M_ERRNO, "TCP/UDP: Close Socket failed");
}
}
sock->sd = SOCKET_UNDEFINED; |
445b192a |
#ifdef _WIN32 |
81d882d5 |
if (!gremlin)
{
overlapped_io_close(&sock->reads);
overlapped_io_close(&sock->writes);
} |
6fbf66fa |
#endif |
81d882d5 |
} |
6fbf66fa |
|
81d882d5 |
if (socket_defined(sock->ctrl_sd))
{
if (openvpn_close_socket(sock->ctrl_sd))
{
msg(M_WARN | M_ERRNO, "TCP/UDP: Close Socket (ctrl_sd) failed");
}
sock->ctrl_sd = SOCKET_UNDEFINED;
} |
6fbf66fa |
|
81d882d5 |
stream_buf_close(&sock->stream_buf);
free_buf(&sock->stream_buf_data);
if (!gremlin)
{
free(sock);
} |
6fbf66fa |
}
}
/* for stream protocols, allow for packet length prefix */
void |
81d882d5 |
socket_adjust_frame_parameters(struct frame *frame, int proto) |
6fbf66fa |
{ |
81d882d5 |
if (link_socket_proto_connection_oriented(proto))
{
frame_add_to_extra_frame(frame, sizeof(packet_size_type));
} |
6fbf66fa |
}
void |
81d882d5 |
setenv_trusted(struct env_set *es, const struct link_socket_info *info) |
6fbf66fa |
{ |
81d882d5 |
setenv_link_socket_actual(es, "trusted", &info->lsa->actual, SA_IP_PORT); |
6fbf66fa |
}
|
5a2e9a25 |
static void |
81d882d5 |
ipchange_fmt(const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc) |
5a2e9a25 |
{ |
81d882d5 |
const char *host = print_sockaddr_ex(&info->lsa->actual.dest.addr.sa, " ", PS_SHOW_PORT, gc);
if (include_cmd) |
25360912 |
{ |
81d882d5 |
argv_parse_cmd(argv, info->ipchange_command);
argv_printf_cat(argv, "%s", host);
}
else
{
argv_printf(argv, "%s", host); |
25360912 |
} |
d3310d2e |
|
5a2e9a25 |
}
|
6fbf66fa |
void |
81d882d5 |
link_socket_connection_initiated(const struct buffer *buf,
struct link_socket_info *info,
const struct link_socket_actual *act,
const char *common_name,
struct env_set *es) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
6fbf66fa |
|
81d882d5 |
info->lsa->actual = *act; /* Note: skip this line for --force-dest */
setenv_trusted(es, info);
info->connection_established = true; |
6fbf66fa |
|
81d882d5 |
/* Print connection initiated message, with common name if available */
{
struct buffer out = alloc_buf_gc(256, &gc);
if (common_name)
{
buf_printf(&out, "[%s] ", common_name);
}
buf_printf(&out, "Peer Connection Initiated with %s", print_link_socket_actual(&info->lsa->actual, &gc));
msg(M_INFO, "%s", BSTR(&out));
}
/* set environmental vars */
setenv_str(es, "common_name", common_name); |
6fbf66fa |
|
81d882d5 |
/* Process --ipchange plugin */
if (plugin_defined(info->plugins, OPENVPN_PLUGIN_IPCHANGE)) |
6fbf66fa |
{ |
81d882d5 |
struct argv argv = argv_new();
ipchange_fmt(false, &argv, info, &gc);
if (plugin_call(info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg(M_WARN, "WARNING: ipchange plugin call failed");
}
argv_reset(&argv); |
6fbf66fa |
}
|
81d882d5 |
/* Process --ipchange option */
if (info->ipchange_command) |
6fbf66fa |
{ |
81d882d5 |
struct argv argv = argv_new();
setenv_str(es, "script_type", "ipchange");
ipchange_fmt(true, &argv, info, &gc);
openvpn_run_script(&argv, es, 0, "--ipchange");
argv_reset(&argv); |
6fbf66fa |
}
|
81d882d5 |
gc_free(&gc); |
6fbf66fa |
}
void |
81d882d5 |
link_socket_bad_incoming_addr(struct buffer *buf,
const struct link_socket_info *info,
const struct link_socket_actual *from_addr)
{
struct gc_arena gc = gc_new();
struct addrinfo *ai;
switch (from_addr->dest.addr.sa.sa_family)
{
case AF_INET:
case AF_INET6:
msg(D_LINK_ERRORS,
"TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
print_link_socket_actual(from_addr, &gc),
(int)from_addr->dest.addr.sa.sa_family,
print_sockaddr_ex(info->lsa->remote_list->ai_addr,":",PS_SHOW_PORT, &gc));
/* print additional remote addresses */ |
4cd4899e |
for (ai = info->lsa->remote_list->ai_next; ai; ai = ai->ai_next)
{ |
81d882d5 |
msg(D_LINK_ERRORS,"or from peer address: %s",
print_sockaddr_ex(ai->ai_addr,":",PS_SHOW_PORT, &gc));
}
break; |
d9c04efc |
} |
81d882d5 |
buf->len = 0;
gc_free(&gc); |
6fbf66fa |
}
void |
81d882d5 |
link_socket_bad_outgoing_addr(void) |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_READ_WRITE, "TCP/UDP: No outgoing address to send packet"); |
6fbf66fa |
}
in_addr_t |
81d882d5 |
link_socket_current_remote(const struct link_socket_info *info) |
6fbf66fa |
{ |
81d882d5 |
const struct link_socket_addr *lsa = info->lsa; |
6fbf66fa |
|
81d882d5 |
/*
* This logic supports "redirect-gateway" semantic, which |
8335caf9 |
* makes sense only for PF_INET routes over PF_INET endpoints
*
* Maybe in the future consider PF_INET6 endpoints also ...
* by now just ignore it
* |
23d61c56 |
* For --remote entries with multiple addresses this
* only return the actual endpoint we have sucessfully connected to |
8335caf9 |
*/ |
81d882d5 |
if (lsa->actual.dest.addr.sa.sa_family != AF_INET)
{
return IPV4_INVALID_ADDR;
}
if (link_socket_actual_defined(&lsa->actual))
{
return ntohl(lsa->actual.dest.addr.in4.sin_addr.s_addr);
}
else if (lsa->current_remote)
{
return ntohl(((struct sockaddr_in *)lsa->current_remote->ai_addr)
->sin_addr.s_addr);
}
else
{
return 0;
} |
6fbf66fa |
}
|
3ddb5643 |
const struct in6_addr * |
81d882d5 |
link_socket_current_remote_ipv6(const struct link_socket_info *info) |
3ddb5643 |
{ |
81d882d5 |
const struct link_socket_addr *lsa = info->lsa; |
3ddb5643 |
/* This logic supports "redirect-gateway" semantic,
* for PF_INET6 routes over PF_INET6 endpoints
*
* For --remote entries with multiple addresses this
* only return the actual endpoint we have sucessfully connected to
*/ |
81d882d5 |
if (lsa->actual.dest.addr.sa.sa_family != AF_INET6)
{
return NULL;
} |
3ddb5643 |
|
81d882d5 |
if (link_socket_actual_defined(&lsa->actual))
{
return &(lsa->actual.dest.addr.in6.sin6_addr);
}
else if (lsa->current_remote)
{
return &(((struct sockaddr_in6 *)lsa->current_remote->ai_addr)->sin6_addr);
}
else
{
return NULL;
} |
3ddb5643 |
}
|
6fbf66fa |
/*
* Return a status string describing socket state.
*/
const char * |
81d882d5 |
socket_stat(const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
struct buffer out = alloc_buf_gc(64, gc);
if (s) |
6fbf66fa |
{ |
81d882d5 |
if (rwflags & EVENT_READ)
{
buf_printf(&out, "S%s",
(s->rwflags_debug & EVENT_READ) ? "R" : "r"); |
445b192a |
#ifdef _WIN32 |
81d882d5 |
buf_printf(&out, "%s",
overlapped_io_state_ascii(&s->reads)); |
6fbf66fa |
#endif |
81d882d5 |
}
if (rwflags & EVENT_WRITE)
{
buf_printf(&out, "S%s",
(s->rwflags_debug & EVENT_WRITE) ? "W" : "w"); |
445b192a |
#ifdef _WIN32 |
81d882d5 |
buf_printf(&out, "%s",
overlapped_io_state_ascii(&s->writes)); |
6fbf66fa |
#endif |
81d882d5 |
} |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
buf_printf(&out, "S?"); |
6fbf66fa |
} |
81d882d5 |
return BSTR(&out); |
6fbf66fa |
}
/*
* Stream buffer functions, used to packetize a TCP
* stream connection.
*/
static inline void |
81d882d5 |
stream_buf_reset(struct stream_buf *sb) |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_STREAM_DEBUG, "STREAM: RESET");
sb->residual_fully_formed = false;
sb->buf = sb->buf_init;
buf_reset(&sb->next);
sb->len = -1; |
6fbf66fa |
}
void |
81d882d5 |
stream_buf_init(struct stream_buf *sb,
struct buffer *buf,
const unsigned int sockflags,
const int proto)
{
sb->buf_init = *buf;
sb->maxlen = sb->buf_init.len;
sb->buf_init.len = 0;
sb->residual = alloc_buf(sb->maxlen);
sb->error = false; |
6add6b2f |
#if PORT_SHARE |
81d882d5 |
sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCP_SERVER))
? PS_ENABLED
: PS_DISABLED; |
6add6b2f |
#endif |
81d882d5 |
stream_buf_reset(sb); |
6fbf66fa |
|
81d882d5 |
dmsg(D_STREAM_DEBUG, "STREAM: INIT maxlen=%d", sb->maxlen); |
6fbf66fa |
}
static inline void |
81d882d5 |
stream_buf_set_next(struct stream_buf *sb) |
6fbf66fa |
{ |
81d882d5 |
/* set up 'next' for next i/o read */
sb->next = sb->buf;
sb->next.offset = sb->buf.offset + sb->buf.len;
sb->next.len = (sb->len >= 0 ? sb->len : sb->maxlen) - sb->buf.len;
dmsg(D_STREAM_DEBUG, "STREAM: SET NEXT, buf=[%d,%d] next=[%d,%d] len=%d maxlen=%d",
sb->buf.offset, sb->buf.len,
sb->next.offset, sb->next.len,
sb->len, sb->maxlen);
ASSERT(sb->next.len > 0);
ASSERT(buf_safe(&sb->buf, sb->next.len)); |
6fbf66fa |
}
static inline void |
81d882d5 |
stream_buf_get_final(struct stream_buf *sb, struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_STREAM_DEBUG, "STREAM: GET FINAL len=%d",
buf_defined(&sb->buf) ? sb->buf.len : -1);
ASSERT(buf_defined(&sb->buf));
*buf = sb->buf; |
6fbf66fa |
}
static inline void |
81d882d5 |
stream_buf_get_next(struct stream_buf *sb, struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_STREAM_DEBUG, "STREAM: GET NEXT len=%d",
buf_defined(&sb->next) ? sb->next.len : -1);
ASSERT(buf_defined(&sb->next));
*buf = sb->next; |
6fbf66fa |
}
bool |
81d882d5 |
stream_buf_read_setup_dowork(struct link_socket *sock) |
6fbf66fa |
{ |
81d882d5 |
if (sock->stream_buf.residual.len && !sock->stream_buf.residual_fully_formed) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(buf_copy(&sock->stream_buf.buf, &sock->stream_buf.residual));
ASSERT(buf_init(&sock->stream_buf.residual, 0));
sock->stream_buf.residual_fully_formed = stream_buf_added(&sock->stream_buf, 0);
dmsg(D_STREAM_DEBUG, "STREAM: RESIDUAL FULLY FORMED [%s], len=%d",
sock->stream_buf.residual_fully_formed ? "YES" : "NO",
sock->stream_buf.residual.len); |
6fbf66fa |
}
|
81d882d5 |
if (!sock->stream_buf.residual_fully_formed)
{
stream_buf_set_next(&sock->stream_buf);
}
return !sock->stream_buf.residual_fully_formed; |
6fbf66fa |
}
bool |
81d882d5 |
stream_buf_added(struct stream_buf *sb,
int length_added) |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_STREAM_DEBUG, "STREAM: ADD length_added=%d", length_added);
if (length_added > 0)
{
sb->buf.len += length_added;
} |
6fbf66fa |
|
81d882d5 |
/* if length unknown, see if we can get the length prefix from
* the head of the buffer */
if (sb->len < 0 && sb->buf.len >= (int) sizeof(packet_size_type)) |
6fbf66fa |
{ |
81d882d5 |
packet_size_type net_size; |
6add6b2f |
#if PORT_SHARE |
81d882d5 |
if (sb->port_share_state == PS_ENABLED)
{
if (!is_openvpn_protocol(&sb->buf))
{
msg(D_STREAM_ERRORS, "Non-OpenVPN client protocol detected");
sb->port_share_state = PS_FOREIGN;
sb->error = true;
return false;
}
else
{
sb->port_share_state = PS_DISABLED;
}
} |
6add6b2f |
#endif
|
81d882d5 |
ASSERT(buf_read(&sb->buf, &net_size, sizeof(net_size)));
sb->len = ntohps(net_size); |
6fbf66fa |
|
81d882d5 |
if (sb->len < 1 || sb->len > sb->maxlen)
{
msg(M_WARN, "WARNING: Bad encapsulated packet length from peer (%d), which must be > 0 and <= %d -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attempting restart...]", sb->len, sb->maxlen);
stream_buf_reset(sb);
sb->error = true;
return false;
} |
6fbf66fa |
}
|
81d882d5 |
/* is our incoming packet fully read? */
if (sb->len > 0 && sb->buf.len >= sb->len) |
6fbf66fa |
{ |
81d882d5 |
/* save any residual data that's part of the next packet */
ASSERT(buf_init(&sb->residual, 0));
if (sb->buf.len > sb->len)
{
ASSERT(buf_copy_excess(&sb->residual, &sb->buf, sb->len));
}
dmsg(D_STREAM_DEBUG, "STREAM: ADD returned TRUE, buf_len=%d, residual_len=%d",
BLEN(&sb->buf),
BLEN(&sb->residual));
return true; |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
dmsg(D_STREAM_DEBUG, "STREAM: ADD returned FALSE (have=%d need=%d)", sb->buf.len, sb->len);
stream_buf_set_next(sb);
return false; |
6fbf66fa |
}
}
void |
81d882d5 |
stream_buf_close(struct stream_buf *sb) |
6fbf66fa |
{ |
81d882d5 |
free_buf(&sb->residual); |
6fbf66fa |
}
/*
* The listen event is a special event whose sole purpose is
* to tell us that there's a new incoming connection on a
* TCP socket, for use in server mode.
*/
event_t |
81d882d5 |
socket_listen_event_handle(struct link_socket *s) |
6fbf66fa |
{ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
if (!defined_net_event_win32(&s->listen_handle))
{
init_net_event_win32(&s->listen_handle, FD_ACCEPT, s->sd, 0);
}
return &s->listen_handle;
#else /* ifdef _WIN32 */
return s->sd; |
6fbf66fa |
#endif
}
/*
* Format IP addresses in ascii
*/
const char * |
81d882d5 |
print_sockaddr_ex(const struct sockaddr *sa,
const char *separator,
const unsigned int flags,
struct gc_arena *gc)
{
struct buffer out = alloc_buf_gc(128, gc);
bool addr_is_defined = false;
char hostaddr[NI_MAXHOST] = "";
char servname[NI_MAXSERV] = "";
int status;
socklen_t salen = 0;
switch (sa->sa_family)
{
case AF_INET:
if (!(flags & PS_DONT_SHOW_FAMILY))
{
buf_puts(&out, "[AF_INET]");
}
salen = sizeof(struct sockaddr_in);
addr_is_defined = ((struct sockaddr_in *) sa)->sin_addr.s_addr != 0;
break; |
d3310d2e |
|
81d882d5 |
case AF_INET6:
if (!(flags & PS_DONT_SHOW_FAMILY))
{
buf_puts(&out, "[AF_INET6]");
}
salen = sizeof(struct sockaddr_in6);
addr_is_defined = !IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) sa)->sin6_addr);
break;
case AF_UNSPEC:
if (!(flags & PS_DONT_SHOW_FAMILY))
{
return "[AF_UNSPEC]";
}
else
{
return "";
}
default:
ASSERT(0); |
d9c04efc |
} |
d3310d2e |
|
81d882d5 |
status = getnameinfo(sa, salen, hostaddr, sizeof(hostaddr),
servname, sizeof(servname), NI_NUMERICHOST | NI_NUMERICSERV); |
d3310d2e |
|
81d882d5 |
if (status!=0)
{
buf_printf(&out,"[nameinfo() err: %s]",gai_strerror(status));
return BSTR(&out);
} |
d3310d2e |
|
81d882d5 |
if (!(flags & PS_DONT_SHOW_ADDR)) |
d3310d2e |
{ |
81d882d5 |
if (addr_is_defined)
{
buf_puts(&out, hostaddr);
}
else
{
buf_puts(&out, "[undef]");
} |
d3310d2e |
}
|
81d882d5 |
if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED)) |
d3310d2e |
{ |
81d882d5 |
if (separator)
{
buf_puts(&out, separator);
} |
d3310d2e |
|
81d882d5 |
buf_puts(&out, servname); |
d3310d2e |
}
|
81d882d5 |
return BSTR(&out); |
8bc93d7f |
}
const char * |
81d882d5 |
print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena *gc) |
8bc93d7f |
{ |
81d882d5 |
return print_link_socket_actual_ex(act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc); |
8bc93d7f |
}
|
6dbf82a9 |
#ifndef IF_NAMESIZE
#define IF_NAMESIZE 16
#endif
|
8bc93d7f |
const char * |
81d882d5 |
print_link_socket_actual_ex(const struct link_socket_actual *act,
const char *separator,
const unsigned int flags,
struct gc_arena *gc) |
8bc93d7f |
{ |
81d882d5 |
if (act) |
8bc93d7f |
{ |
81d882d5 |
char ifname[IF_NAMESIZE] = "[undef]";
struct buffer out = alloc_buf_gc(128, gc);
buf_printf(&out, "%s", print_sockaddr_ex(&act->dest.addr.sa, separator, flags, gc)); |
8bc93d7f |
#if ENABLE_IP_PKTINFO |
81d882d5 |
if ((flags & PS_SHOW_PKTINFO) && addr_defined_ipi(act))
{
switch (act->dest.addr.sa.sa_family)
{
case AF_INET:
{
struct openvpn_sockaddr sa;
CLEAR(sa);
sa.addr.in4.sin_family = AF_INET; |
7efa60d9 |
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) |
81d882d5 |
sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
if_indextoname(act->pi.in4.ipi_ifindex, ifname); |
d3774cdf |
#elif defined(IP_RECVDSTADDR) |
81d882d5 |
sa.addr.in4.sin_addr = act->pi.in4;
ifname[0] = 0;
#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ |
d3774cdf |
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif |
81d882d5 |
buf_printf(&out, " (via %s%%%s)",
print_sockaddr_ex(&sa.addr.sa, separator, 0, gc),
ifname);
}
break;
case AF_INET6:
{
struct sockaddr_in6 sin6;
char buf[INET6_ADDRSTRLEN] = "[undef]";
CLEAR(sin6);
sin6.sin6_family = AF_INET6;
sin6.sin6_addr = act->pi.in6.ipi6_addr;
if_indextoname(act->pi.in6.ipi6_ifindex, ifname);
if (getnameinfo((struct sockaddr *)&sin6, sizeof(struct sockaddr_in6),
buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) == 0)
{
buf_printf(&out, " (via %s%%%s)", buf, ifname);
}
else
{
buf_printf(&out, " (via [getnameinfo() err]%%%s)", ifname);
}
}
break;
}
}
#endif /* if ENABLE_IP_PKTINFO */
return BSTR(&out);
}
else
{
return "[NULL]"; |
8bc93d7f |
} |
6fbf66fa |
}
/*
* Convert an in_addr_t in host byte order
* to an ascii dotted quad.
*/
const char * |
81d882d5 |
print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
struct in_addr ia;
struct buffer out = alloc_buf_gc(64, gc); |
6fbf66fa |
|
81d882d5 |
if (addr || !(flags & IA_EMPTY_IF_UNDEF)) |
6fbf66fa |
{ |
81d882d5 |
CLEAR(ia);
ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl(addr); |
6fbf66fa |
|
81d882d5 |
buf_printf(&out, "%s", inet_ntoa(ia)); |
6fbf66fa |
} |
81d882d5 |
return BSTR(&out); |
6fbf66fa |
}
|
512cda46 |
/*
* Convert an in6_addr in host byte order
* to an ascii representation of an IPv6 address
*/
const char * |
81d882d5 |
print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc) |
512cda46 |
{ |
81d882d5 |
struct buffer out = alloc_buf_gc(64, gc);
char tmp_out_buf[64]; /* inet_ntop wants pointer to buffer */ |
512cda46 |
|
81d882d5 |
if (memcmp(&a6, &in6addr_any, sizeof(a6)) != 0
|| !(flags & IA_EMPTY_IF_UNDEF)) |
512cda46 |
{ |
81d882d5 |
inet_ntop(AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
buf_printf(&out, "%s", tmp_out_buf ); |
512cda46 |
} |
81d882d5 |
return BSTR(&out); |
512cda46 |
}
|
1ffdb2c9 |
#ifndef UINT8_MAX |
81d882d5 |
#define UINT8_MAX 0xff |
1ffdb2c9 |
#endif
|
512cda46 |
/* add some offset to an ipv6 address |
1ffdb2c9 |
* (add in steps of 8 bits, taking overflow into next round) |
512cda46 |
*/ |
81d882d5 |
struct in6_addr
add_in6_addr( struct in6_addr base, uint32_t add ) |
512cda46 |
{
int i;
|
81d882d5 |
for (i = 15; i>=0 && add > 0; i--) |
512cda46 |
{ |
81d882d5 |
register int carry;
register uint32_t h; |
1ffdb2c9 |
|
81d882d5 |
h = (unsigned char) base.s6_addr[i];
base.s6_addr[i] = (h+add) & UINT8_MAX; |
1ffdb2c9 |
|
81d882d5 |
/* using explicit carry for the 8-bit additions will catch |
1ffdb2c9 |
* 8-bit and(!) 32-bit overruns nicely |
512cda46 |
*/ |
81d882d5 |
carry = ((h & 0xff) + (add & 0xff)) >> 8;
add = (add>>8) + carry; |
512cda46 |
}
return base;
}
|
6fbf66fa |
/* set environmental variables for ip/port in *addr */
void |
81d882d5 |
setenv_sockaddr(struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const unsigned int flags)
{
char name_buf[256];
char buf[128];
switch (addr->addr.sa.sa_family)
{
case AF_INET:
if (flags & SA_IP_PORT)
{
openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
}
else
{
openvpn_snprintf(name_buf, sizeof(name_buf), "%s", name_prefix);
}
setenv_str(es, name_buf, inet_ntoa(addr->addr.in4.sin_addr));
if ((flags & SA_IP_PORT) && addr->addr.in4.sin_port)
{
openvpn_snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix);
setenv_int(es, name_buf, ntohs(addr->addr.in4.sin_port));
}
break;
case AF_INET6:
if (IN6_IS_ADDR_V4MAPPED( &addr->addr.in6.sin6_addr ))
{
struct in_addr ia;
memcpy(&ia.s_addr, &addr->addr.in6.sin6_addr.s6_addr[12],
sizeof(ia.s_addr));
openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip", name_prefix);
openvpn_snprintf(buf, sizeof(buf), "%s", inet_ntoa(ia) );
}
else
{
openvpn_snprintf(name_buf, sizeof(name_buf), "%s_ip6", name_prefix);
getnameinfo(&addr->addr.sa, sizeof(struct sockaddr_in6),
buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
}
setenv_str(es, name_buf, buf);
if ((flags & SA_IP_PORT) && addr->addr.in6.sin6_port)
{
openvpn_snprintf(name_buf, sizeof(name_buf), "%s_port", name_prefix);
setenv_int(es, name_buf, ntohs(addr->addr.in6.sin6_port));
}
break; |
d9c04efc |
} |
6fbf66fa |
}
void |
81d882d5 |
setenv_in_addr_t(struct env_set *es, const char *name_prefix, in_addr_t addr, const unsigned int flags) |
6fbf66fa |
{ |
81d882d5 |
if (addr || !(flags & SA_SET_IF_NONZERO)) |
6fbf66fa |
{ |
81d882d5 |
struct openvpn_sockaddr si;
CLEAR(si);
si.addr.in4.sin_family = AF_INET;
si.addr.in4.sin_addr.s_addr = htonl(addr);
setenv_sockaddr(es, name_prefix, &si, flags); |
6fbf66fa |
}
}
|
8bc93d7f |
void |
81d882d5 |
setenv_in6_addr(struct env_set *es,
const char *name_prefix,
const struct in6_addr *addr,
const unsigned int flags) |
a8f8b926 |
{ |
81d882d5 |
if (!IN6_IS_ADDR_UNSPECIFIED(addr) || !(flags & SA_SET_IF_NONZERO)) |
a8f8b926 |
{ |
81d882d5 |
struct openvpn_sockaddr si;
CLEAR(si);
si.addr.in6.sin6_family = AF_INET6;
si.addr.in6.sin6_addr = *addr;
setenv_sockaddr(es, name_prefix, &si, flags); |
a8f8b926 |
}
}
void |
81d882d5 |
setenv_link_socket_actual(struct env_set *es,
const char *name_prefix,
const struct link_socket_actual *act,
const unsigned int flags) |
8bc93d7f |
{ |
81d882d5 |
setenv_sockaddr(es, name_prefix, &act->dest, flags); |
8bc93d7f |
}
|
6fbf66fa |
/*
* Convert protocol names between index and ascii form.
*/
struct proto_names { |
81d882d5 |
const char *short_form;
const char *display_form;
sa_family_t proto_af;
int proto; |
6fbf66fa |
};
/* Indexed by PROTO_x */ |
30077d1f |
static const struct proto_names proto_names[] = { |
81d882d5 |
{"proto-uninitialized", "proto-NONE", AF_UNSPEC, PROTO_NONE},
/* try IPv4 and IPv6 (client), bind dual-stack (server) */
{"udp", "UDP", AF_UNSPEC, PROTO_UDP},
{"tcp-server", "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER},
{"tcp-client", "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT},
{"tcp", "TCP", AF_UNSPEC, PROTO_TCP},
/* force IPv4 */
{"udp4", "UDPv4", AF_INET, PROTO_UDP},
{"tcp4-server","TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER},
{"tcp4-client","TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT},
{"tcp4", "TCPv4", AF_INET, PROTO_TCP},
/* force IPv6 */
{"udp6","UDPv6", AF_INET6, PROTO_UDP},
{"tcp6-server","TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER},
{"tcp6-client","TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT},
{"tcp6","TCPv6", AF_INET6, PROTO_TCP}, |
6fbf66fa |
};
|
8335caf9 |
bool
proto_is_net(int proto)
{ |
81d882d5 |
if (proto < 0 || proto >= PROTO_N)
{
ASSERT(0);
} |
30077d1f |
return proto != PROTO_NONE; |
8335caf9 |
}
bool
proto_is_dgram(int proto)
{ |
30077d1f |
return proto_is_udp(proto); |
8335caf9 |
} |
30077d1f |
|
8335caf9 |
bool
proto_is_udp(int proto)
{ |
81d882d5 |
if (proto < 0 || proto >= PROTO_N)
{
ASSERT(0);
}
return proto == PROTO_UDP; |
8335caf9 |
} |
30077d1f |
|
8335caf9 |
bool
proto_is_tcp(int proto)
{ |
81d882d5 |
if (proto < 0 || proto >= PROTO_N)
{
ASSERT(0);
}
return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER; |
8335caf9 |
}
|
6fbf66fa |
int |
81d882d5 |
ascii2proto(const char *proto_name) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; i < SIZE(proto_names); ++i) |
4cd4899e |
{ |
81d882d5 |
if (!strcmp(proto_name, proto_names[i].short_form))
{
return proto_names[i].proto;
} |
4cd4899e |
} |
81d882d5 |
return -1; |
6fbf66fa |
}
|
30077d1f |
sa_family_t |
81d882d5 |
ascii2af(const char *proto_name) |
30077d1f |
{
int i; |
81d882d5 |
for (i = 0; i < SIZE(proto_names); ++i) |
4cd4899e |
{ |
81d882d5 |
if (!strcmp(proto_name, proto_names[i].short_form))
{ |
30077d1f |
return proto_names[i].proto_af; |
81d882d5 |
} |
4cd4899e |
} |
30077d1f |
return 0;
}
|
6fbf66fa |
const char * |
81d882d5 |
proto2ascii(int proto, sa_family_t af, bool display_form) |
6fbf66fa |
{ |
81d882d5 |
unsigned int i;
for (i = 0; i < SIZE(proto_names); ++i) |
30077d1f |
{ |
81d882d5 |
if (proto_names[i].proto_af == af && proto_names[i].proto == proto) |
30077d1f |
{ |
81d882d5 |
if (display_form)
{
return proto_names[i].display_form;
}
else
{
return proto_names[i].short_form;
} |
30077d1f |
}
}
|
81d882d5 |
return "[unknown protocol]"; |
6fbf66fa |
}
const char * |
81d882d5 |
proto2ascii_all(struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
struct buffer out = alloc_buf_gc(256, gc);
int i; |
6fbf66fa |
|
81d882d5 |
for (i = 0; i < SIZE(proto_names); ++i) |
6fbf66fa |
{ |
81d882d5 |
if (i)
{
buf_printf(&out, " ");
}
buf_printf(&out, "[%s]", proto_names[i].short_form); |
6fbf66fa |
} |
81d882d5 |
return BSTR(&out); |
6fbf66fa |
}
|
8335caf9 |
const char * |
81d882d5 |
addr_family_name(int af) |
8335caf9 |
{ |
81d882d5 |
switch (af) |
51afc8b8 |
{ |
81d882d5 |
case AF_INET: return "AF_INET";
case AF_INET6: return "AF_INET6"; |
51afc8b8 |
} |
81d882d5 |
return "AF_UNSPEC"; |
8335caf9 |
}
|
6fbf66fa |
/*
* Given a local proto, return local proto
* if !remote, or compatible remote proto
* if remote.
*
* This is used for options compatibility
* checking. |
38727e09 |
*
* IPv6 and IPv4 protocols are comptabile but OpenVPN
* has always sent UDPv4, TCPv4 over the wire. Keep these
* strings for backward compatbility |
6fbf66fa |
*/ |
81d882d5 |
const char *
proto_remote(int proto, bool remote) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(proto >= 0 && proto < PROTO_N);
if (proto == PROTO_UDP)
{
return "UDPv4";
} |
34136dd8 |
|
81d882d5 |
if ( (remote && proto == PROTO_TCP_CLIENT)
|| (!remote && proto == PROTO_TCP_SERVER))
{
return "TCPv4_SERVER";
}
if ( (remote && proto == PROTO_TCP_SERVER)
|| (!remote && proto == PROTO_TCP_CLIENT))
{
return "TCPv4_CLIENT";
} |
34136dd8 |
|
81d882d5 |
ASSERT(0);
return ""; /* Make the compiler happy */ |
6fbf66fa |
}
/*
* Bad incoming address lengths that differ from what
* we expect are considered to be fatal errors.
*/
void |
81d882d5 |
bad_address_length(int actual, int expected) |
6fbf66fa |
{ |
81d882d5 |
msg(M_FATAL, "ERROR: received strange incoming packet with an address length of %d -- we only accept address lengths of %d.",
actual,
expected); |
6fbf66fa |
}
/*
* Socket Read Routines
*/
int |
81d882d5 |
link_socket_read_tcp(struct link_socket *sock,
struct buffer *buf) |
6fbf66fa |
{ |
81d882d5 |
int len = 0; |
6fbf66fa |
|
81d882d5 |
if (!sock->stream_buf.residual_fully_formed) |
6fbf66fa |
{ |
445b192a |
#ifdef _WIN32 |
81d882d5 |
len = socket_finalize(sock->sd, &sock->reads, buf, NULL); |
6fbf66fa |
#else |
81d882d5 |
struct buffer frag;
stream_buf_get_next(&sock->stream_buf, &frag);
len = recv(sock->sd, BPTR(&frag), BLEN(&frag), MSG_NOSIGNAL); |
6fbf66fa |
#endif
|
81d882d5 |
if (!len)
{
sock->stream_reset = true;
}
if (len <= 0)
{
return buf->len = len;
} |
6fbf66fa |
}
|
81d882d5 |
if (sock->stream_buf.residual_fully_formed
|| stream_buf_added(&sock->stream_buf, len)) /* packet complete? */ |
6fbf66fa |
{ |
81d882d5 |
stream_buf_get_final(&sock->stream_buf, buf);
stream_buf_reset(&sock->stream_buf);
return buf->len;
}
else
{
return buf->len = 0; /* no error, but packet is still incomplete */ |
6fbf66fa |
}
}
|
445b192a |
#ifndef _WIN32 |
6fbf66fa |
|
8bc93d7f |
#if ENABLE_IP_PKTINFO
|
3fb246e3 |
/* make the buffer large enough to handle ancilliary socket data for
* both IPv4 and IPv6 destination addresses, plus padding (see RFC 2292)
*/ |
7efa60d9 |
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) |
81d882d5 |
#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof(struct in6_pktinfo)), \
CMSG_SPACE(sizeof(struct in_pktinfo)) ) |
3fb246e3 |
#else |
81d882d5 |
#define PKTINFO_BUF_SIZE max_int( CMSG_SPACE(sizeof(struct in6_pktinfo)), \
CMSG_SPACE(sizeof(struct in_addr)) ) |
d3774cdf |
#endif |
8bc93d7f |
static socklen_t |
81d882d5 |
link_socket_read_udp_posix_recvmsg(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from)
{
struct iovec iov;
uint8_t pktinfo_buf[PKTINFO_BUF_SIZE];
struct msghdr mesg;
socklen_t fromlen = sizeof(from->dest.addr);
iov.iov_base = BPTR(buf);
iov.iov_len = buf_forward_capacity_total(buf);
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
mesg.msg_name = &from->dest.addr;
mesg.msg_namelen = fromlen;
mesg.msg_control = pktinfo_buf;
mesg.msg_controllen = sizeof pktinfo_buf;
buf->len = recvmsg(sock->sd, &mesg, 0);
if (buf->len >= 0)
{
struct cmsghdr *cmsg;
fromlen = mesg.msg_namelen;
cmsg = CMSG_FIRSTHDR(&mesg);
if (cmsg != NULL
&& CMSG_NXTHDR(&mesg, cmsg) == NULL |
7efa60d9 |
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) |
81d882d5 |
&& cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO
&& cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_pktinfo)) ) |
d3774cdf |
#elif defined(IP_RECVDSTADDR) |
81d882d5 |
&& cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_RECVDSTADDR
&& cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_addr)) )
#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ |
d3774cdf |
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif |
81d882d5 |
{ |
7efa60d9 |
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) |
81d882d5 |
struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA(cmsg);
from->pi.in4.ipi_ifindex = pkti->ipi_ifindex;
from->pi.in4.ipi_spec_dst = pkti->ipi_spec_dst; |
d3774cdf |
#elif defined(IP_RECVDSTADDR) |
81d882d5 |
from->pi.in4 = *(struct in_addr *) CMSG_DATA(cmsg);
#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ |
d3774cdf |
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif |
81d882d5 |
}
else if (cmsg != NULL
&& CMSG_NXTHDR(&mesg, cmsg) == NULL
&& cmsg->cmsg_level == IPPROTO_IPV6
&& cmsg->cmsg_type == IPV6_PKTINFO
&& cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in6_pktinfo)) )
{
struct in6_pktinfo *pkti6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
from->pi.in6.ipi6_ifindex = pkti6->ipi6_ifindex;
from->pi.in6.ipi6_addr = pkti6->ipi6_addr;
}
else if (cmsg != NULL)
{
msg(M_WARN, "CMSG received that cannot be parsed (cmsg_level=%d, cmsg_type=%d, cmsg=len=%d)", (int)cmsg->cmsg_level, (int)cmsg->cmsg_type, (int)cmsg->cmsg_len );
}
}
return fromlen; |
8bc93d7f |
} |
81d882d5 |
#endif /* if ENABLE_IP_PKTINFO */ |
8bc93d7f |
|
6fbf66fa |
int |
81d882d5 |
link_socket_read_udp_posix(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *from) |
6fbf66fa |
{ |
81d882d5 |
socklen_t fromlen = sizeof(from->dest.addr);
socklen_t expectedlen = af_addr_size(sock->info.af);
addr_zero_host(&from->dest); |
8bc93d7f |
#if ENABLE_IP_PKTINFO |
81d882d5 |
/* Both PROTO_UDPv4 and PROTO_UDPv6 */
if (sock->info.proto == PROTO_UDP && sock->sockflags & SF_USE_IP_PKTINFO)
{
fromlen = link_socket_read_udp_posix_recvmsg(sock, buf, from);
}
else |
8bc93d7f |
#endif |
81d882d5 |
buf->len = recvfrom(sock->sd, BPTR(buf), buf_forward_capacity(buf), 0,
&from->dest.addr.sa, &fromlen);
/* FIXME: won't do anything when sock->info.af == AF_UNSPEC */
if (buf->len >= 0 && expectedlen && fromlen != expectedlen)
{
bad_address_length(fromlen, expectedlen);
}
return buf->len; |
6fbf66fa |
}
|
81d882d5 |
#endif /* ifndef _WIN32 */ |
6fbf66fa |
/*
* Socket Write Routines
*/
int |
81d882d5 |
link_socket_write_tcp(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to)
{
packet_size_type len = BLEN(buf);
dmsg(D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset);
ASSERT(len <= sock->stream_buf.maxlen);
len = htonps(len);
ASSERT(buf_write_prepend(buf, &len, sizeof(len))); |
445b192a |
#ifdef _WIN32 |
81d882d5 |
return link_socket_write_win32(sock, buf, to); |
6fbf66fa |
#else |
81d882d5 |
return link_socket_write_tcp_posix(sock, buf, to); |
6fbf66fa |
#endif
}
|
8bc93d7f |
#if ENABLE_IP_PKTINFO
|
23d61c56 |
size_t |
81d882d5 |
link_socket_write_udp_posix_sendmsg(struct link_socket *sock,
struct buffer *buf,
struct link_socket_actual *to)
{
struct iovec iov;
struct msghdr mesg;
struct cmsghdr *cmsg;
uint8_t pktinfo_buf[PKTINFO_BUF_SIZE];
iov.iov_base = BPTR(buf);
iov.iov_len = BLEN(buf);
mesg.msg_iov = &iov;
mesg.msg_iovlen = 1;
switch (to->dest.addr.sa.sa_family)
{
case AF_INET:
{
mesg.msg_name = &to->dest.addr.sa;
mesg.msg_namelen = sizeof(struct sockaddr_in);
mesg.msg_control = pktinfo_buf;
mesg.msg_flags = 0; |
7efa60d9 |
#if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) |
81d882d5 |
mesg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
cmsg = CMSG_FIRSTHDR(&mesg);
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmsg->cmsg_level = SOL_IP;
cmsg->cmsg_type = IP_PKTINFO;
{
struct in_pktinfo *pkti;
pkti = (struct in_pktinfo *) CMSG_DATA(cmsg);
pkti->ipi_ifindex = to->pi.in4.ipi_ifindex;
pkti->ipi_spec_dst = to->pi.in4.ipi_spec_dst;
pkti->ipi_addr.s_addr = 0;
} |
d3774cdf |
#elif defined(IP_RECVDSTADDR) |
81d882d5 |
ASSERT( CMSG_SPACE(sizeof(struct in_addr)) <= sizeof(pktinfo_buf) );
mesg.msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
cmsg = CMSG_FIRSTHDR(&mesg);
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_RECVDSTADDR;
*(struct in_addr *) CMSG_DATA(cmsg) = to->pi.in4;
#else /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */ |
d3774cdf |
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h) |
81d882d5 |
#endif /* if defined(HAVE_IN_PKTINFO) && defined(HAVE_IPI_SPEC_DST) */
break;
}
case AF_INET6:
{
struct in6_pktinfo *pkti6;
mesg.msg_name = &to->dest.addr.sa;
mesg.msg_namelen = sizeof(struct sockaddr_in6);
ASSERT( CMSG_SPACE(sizeof(struct in6_pktinfo)) <= sizeof(pktinfo_buf) );
mesg.msg_control = pktinfo_buf;
mesg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
mesg.msg_flags = 0;
cmsg = CMSG_FIRSTHDR(&mesg);
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
pkti6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);
pkti6->ipi6_ifindex = to->pi.in6.ipi6_ifindex;
pkti6->ipi6_addr = to->pi.in6.ipi6_addr;
break;
}
default: ASSERT(0);
}
return sendmsg(sock->sd, &mesg, 0); |
8bc93d7f |
}
|
81d882d5 |
#endif /* if ENABLE_IP_PKTINFO */ |
8bc93d7f |
|
6fbf66fa |
/*
* Win32 overlapped socket I/O functions.
*/
|
445b192a |
#ifdef _WIN32 |
6fbf66fa |
int |
81d882d5 |
socket_recv_queue(struct link_socket *sock, int maxsize)
{
if (sock->reads.iostate == IOSTATE_INITIAL)
{
WSABUF wsabuf[1];
int status;
/* reset buf to its initial state */
if (proto_is_udp(sock->info.proto))
{
sock->reads.buf = sock->reads.buf_init;
}
else if (proto_is_tcp(sock->info.proto))
{
stream_buf_get_next(&sock->stream_buf, &sock->reads.buf);
}
else
{
ASSERT(0);
}
/* Win32 docs say it's okay to allocate the wsabuf on the stack */
wsabuf[0].buf = BPTR(&sock->reads.buf);
wsabuf[0].len = maxsize ? maxsize : BLEN(&sock->reads.buf);
/* check for buffer overflow */
ASSERT(wsabuf[0].len <= BLEN(&sock->reads.buf));
/* the overlapped read will signal this event on I/O completion */
ASSERT(ResetEvent(sock->reads.overlapped.hEvent));
sock->reads.flags = 0;
if (proto_is_udp(sock->info.proto))
{
sock->reads.addr_defined = true;
sock->reads.addrlen = sizeof(sock->reads.addr6);
status = WSARecvFrom(
sock->sd,
wsabuf,
1,
&sock->reads.size,
&sock->reads.flags,
(struct sockaddr *) &sock->reads.addr,
&sock->reads.addrlen,
&sock->reads.overlapped,
NULL);
}
else if (proto_is_tcp(sock->info.proto))
{
sock->reads.addr_defined = false;
status = WSARecv(
sock->sd,
wsabuf,
1,
&sock->reads.size,
&sock->reads.flags,
&sock->reads.overlapped,
NULL);
}
else
{
status = 0;
ASSERT(0);
}
if (!status) /* operation completed immediately? */
{
/* FIXME: won't do anything when sock->info.af == AF_UNSPEC */
int af_len = af_addr_size(sock->info.af);
if (sock->reads.addr_defined && af_len && sock->reads.addrlen != af_len)
{
bad_address_length(sock->reads.addrlen, af_len);
}
sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
/* since we got an immediate return, we must signal the event object ourselves */
ASSERT(SetEvent(sock->reads.overlapped.hEvent));
sock->reads.status = 0;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Receive immediate return [%d,%d]",
(int) wsabuf[0].len,
(int) sock->reads.size);
}
else
{
status = WSAGetLastError();
if (status == WSA_IO_PENDING) /* operation queued? */
{
sock->reads.iostate = IOSTATE_QUEUED;
sock->reads.status = status;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Receive queued [%d]",
(int) wsabuf[0].len);
}
else /* error occurred */
{
struct gc_arena gc = gc_new();
ASSERT(SetEvent(sock->reads.overlapped.hEvent));
sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
sock->reads.status = status;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Receive error [%d]: %s",
(int) wsabuf[0].len,
strerror_win32(status, &gc));
gc_free(&gc);
}
}
}
return sock->reads.iostate; |
6fbf66fa |
}
int |
81d882d5 |
socket_send_queue(struct link_socket *sock, struct buffer *buf, const struct link_socket_actual *to)
{
if (sock->writes.iostate == IOSTATE_INITIAL)
{
WSABUF wsabuf[1];
int status;
/* make a private copy of buf */
sock->writes.buf = sock->writes.buf_init;
sock->writes.buf.len = 0;
ASSERT(buf_copy(&sock->writes.buf, buf));
/* Win32 docs say it's okay to allocate the wsabuf on the stack */
wsabuf[0].buf = BPTR(&sock->writes.buf);
wsabuf[0].len = BLEN(&sock->writes.buf);
/* the overlapped write will signal this event on I/O completion */
ASSERT(ResetEvent(sock->writes.overlapped.hEvent));
sock->writes.flags = 0;
if (proto_is_udp(sock->info.proto))
{
/* set destination address for UDP writes */
sock->writes.addr_defined = true;
if (to->dest.addr.sa.sa_family == AF_INET6)
{
sock->writes.addr6 = to->dest.addr.in6;
sock->writes.addrlen = sizeof(sock->writes.addr6);
}
else
{
sock->writes.addr = to->dest.addr.in4;
sock->writes.addrlen = sizeof(sock->writes.addr);
}
status = WSASendTo(
sock->sd,
wsabuf,
1,
&sock->writes.size,
sock->writes.flags,
(struct sockaddr *) &sock->writes.addr,
sock->writes.addrlen,
&sock->writes.overlapped,
NULL);
}
else if (proto_is_tcp(sock->info.proto))
{
/* destination address for TCP writes was established on connection initiation */
sock->writes.addr_defined = false;
status = WSASend(
sock->sd,
wsabuf,
1,
&sock->writes.size,
sock->writes.flags,
&sock->writes.overlapped,
NULL);
}
else
{
status = 0;
ASSERT(0);
}
if (!status) /* operation completed immediately? */
{
sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
/* since we got an immediate return, we must signal the event object ourselves */
ASSERT(SetEvent(sock->writes.overlapped.hEvent));
sock->writes.status = 0;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Send immediate return [%d,%d]",
(int) wsabuf[0].len,
(int) sock->writes.size);
}
else
{
status = WSAGetLastError();
if (status == WSA_IO_PENDING) /* operation queued? */
{
sock->writes.iostate = IOSTATE_QUEUED;
sock->writes.status = status;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Send queued [%d]",
(int) wsabuf[0].len);
}
else /* error occurred */
{
struct gc_arena gc = gc_new();
ASSERT(SetEvent(sock->writes.overlapped.hEvent));
sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
sock->writes.status = status;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Send error [%d]: %s",
(int) wsabuf[0].len,
strerror_win32(status, &gc));
gc_free(&gc);
}
}
}
return sock->writes.iostate; |
6fbf66fa |
}
int |
81d882d5 |
socket_finalize(SOCKET s,
struct overlapped_io *io,
struct buffer *buf,
struct link_socket_actual *from)
{
int ret = -1;
BOOL status;
switch (io->iostate)
{
case IOSTATE_QUEUED:
status = WSAGetOverlappedResult(
s,
&io->overlapped,
&io->size,
FALSE,
&io->flags
);
if (status)
{
/* successful return for a queued operation */
if (buf)
{
*buf = io->buf;
}
ret = io->size;
io->iostate = IOSTATE_INITIAL;
ASSERT(ResetEvent(io->overlapped.hEvent));
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Completion success [%d]", ret);
}
else
{
/* error during a queued operation */
ret = -1;
if (WSAGetLastError() != WSA_IO_INCOMPLETE)
{
/* if no error (i.e. just not finished yet), then DON'T execute this code */
io->iostate = IOSTATE_INITIAL;
ASSERT(ResetEvent(io->overlapped.hEvent));
msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Socket Completion error");
}
}
break;
case IOSTATE_IMMEDIATE_RETURN:
io->iostate = IOSTATE_INITIAL;
ASSERT(ResetEvent(io->overlapped.hEvent));
if (io->status)
{
/* error return for a non-queued operation */
WSASetLastError(io->status);
ret = -1;
msg(D_WIN32_IO | M_ERRNO, "WIN32 I/O: Socket Completion non-queued error");
}
else
{
/* successful return for a non-queued operation */
if (buf)
{
*buf = io->buf;
}
ret = io->size;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Completion non-queued success [%d]", ret);
}
break;
case IOSTATE_INITIAL: /* were we called without proper queueing? */
WSASetLastError(WSAEINVAL);
ret = -1;
dmsg(D_WIN32_IO, "WIN32 I/O: Socket Completion BAD STATE");
break;
default:
ASSERT(0);
}
/* return from address if requested */
if (from)
{
if (ret >= 0 && io->addr_defined)
{
/* TODO(jjo): streamline this mess */
/* in this func we dont have relevant info about the PF_ of this
* endpoint, as link_socket_actual will be zero for the 1st received packet
*
* Test for inets PF_ possible sizes
*/
switch (io->addrlen)
{
case sizeof(struct sockaddr_in):
case sizeof(struct sockaddr_in6):
/* TODO(jjo): for some reason (?) I'm getting 24,28 for AF_INET6
* under _WIN32*/
case sizeof(struct sockaddr_in6)-4:
break;
default:
bad_address_length(io->addrlen, af_addr_size(io->addr.sin_family));
}
switch (io->addr.sin_family)
{
case AF_INET:
from->dest.addr.in4 = io->addr;
break;
case AF_INET6:
from->dest.addr.in6 = io->addr6;
break;
}
}
else
{
CLEAR(from->dest.addr);
}
}
if (buf)
{
buf->len = ret;
}
return ret; |
6fbf66fa |
}
|
445b192a |
#endif /* _WIN32 */ |
6fbf66fa |
/*
* Socket event notification
*/
unsigned int |
81d882d5 |
socket_set(struct link_socket *s,
struct event_set *es,
unsigned int rwflags,
void *arg,
unsigned int *persistent)
{
if (s)
{
if ((rwflags & EVENT_READ) && !stream_buf_read_setup(s))
{
ASSERT(!persistent);
rwflags &= ~EVENT_READ;
}
|
445b192a |
#ifdef _WIN32 |
81d882d5 |
if (rwflags & EVENT_READ)
{
socket_recv_queue(s, 0);
} |
6fbf66fa |
#endif
|
81d882d5 |
/* if persistent is defined, call event_ctl only if rwflags has changed since last call */
if (!persistent || *persistent != rwflags)
{
event_ctl(es, socket_event_handle(s), rwflags, arg);
if (persistent)
{
*persistent = rwflags;
}
} |
6fbf66fa |
|
81d882d5 |
s->rwflags_debug = rwflags; |
6fbf66fa |
} |
81d882d5 |
return rwflags; |
6fbf66fa |
} |
bb564a59 |
void |
81d882d5 |
sd_close(socket_descriptor_t *sd) |
bb564a59 |
{ |
81d882d5 |
if (sd && socket_defined(*sd)) |
bb564a59 |
{ |
81d882d5 |
openvpn_close_socket(*sd);
*sd = SOCKET_UNDEFINED; |
bb564a59 |
}
}
#if UNIX_SOCK_SUPPORT
/*
* code for unix domain sockets
*/
const char * |
81d882d5 |
sockaddr_unix_name(const struct sockaddr_un *local, const char *null) |
bb564a59 |
{ |
81d882d5 |
if (local && local->sun_family == PF_UNIX)
{
return local->sun_path;
}
else
{
return null;
} |
bb564a59 |
}
socket_descriptor_t |
81d882d5 |
create_socket_unix(void) |
bb564a59 |
{ |
81d882d5 |
socket_descriptor_t sd; |
bb564a59 |
|
81d882d5 |
if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
{
msg(M_ERR, "Cannot create unix domain socket");
} |
e35a7883 |
|
81d882d5 |
/* set socket file descriptor to not pass across execs, so that
* scripts don't have access to it */
set_cloexec(sd); |
e35a7883 |
|
81d882d5 |
return sd; |
bb564a59 |
}
void |
81d882d5 |
socket_bind_unix(socket_descriptor_t sd,
struct sockaddr_un *local,
const char *prefix) |
bb564a59 |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
bb564a59 |
#ifdef HAVE_UMASK |
81d882d5 |
const mode_t orig_umask = umask(0); |
bb564a59 |
#endif
|
81d882d5 |
if (bind(sd, (struct sockaddr *) local, sizeof(struct sockaddr_un))) |
bb564a59 |
{ |
56b396dc |
msg(M_FATAL | M_ERRNO,
"%s: Socket bind[%d] failed on unix domain socket %s", |
81d882d5 |
prefix,
(int)sd, |
56b396dc |
sockaddr_unix_name(local, "NULL")); |
bb564a59 |
}
#ifdef HAVE_UMASK |
81d882d5 |
umask(orig_umask); |
bb564a59 |
#endif
|
81d882d5 |
gc_free(&gc); |
bb564a59 |
}
socket_descriptor_t |
81d882d5 |
socket_accept_unix(socket_descriptor_t sd,
struct sockaddr_un *remote) |
bb564a59 |
{ |
81d882d5 |
socklen_t remote_len = sizeof(struct sockaddr_un);
socket_descriptor_t ret; |
bb564a59 |
|
81d882d5 |
CLEAR(*remote);
ret = accept(sd, (struct sockaddr *) remote, &remote_len);
if (ret >= 0) |
e35a7883 |
{ |
81d882d5 |
/* set socket file descriptor to not pass across execs, so that
* scripts don't have access to it */
set_cloexec(ret); |
e35a7883 |
} |
81d882d5 |
return ret; |
bb564a59 |
}
|
86f5c7c9 |
int |
81d882d5 |
socket_connect_unix(socket_descriptor_t sd,
struct sockaddr_un *remote) |
86f5c7c9 |
{ |
81d882d5 |
int status = connect(sd, (struct sockaddr *) remote, sizeof(struct sockaddr_un));
if (status)
{
status = openvpn_errno();
}
return status; |
86f5c7c9 |
}
|
bb564a59 |
void |
81d882d5 |
sockaddr_unix_init(struct sockaddr_un *local, const char *path) |
bb564a59 |
{ |
81d882d5 |
local->sun_family = PF_UNIX;
strncpynt(local->sun_path, path, sizeof(local->sun_path)); |
bb564a59 |
}
void |
81d882d5 |
socket_delete_unix(const struct sockaddr_un *local) |
bb564a59 |
{ |
81d882d5 |
const char *name = sockaddr_unix_name(local, NULL); |
bb564a59 |
#ifdef HAVE_UNLINK |
81d882d5 |
if (name && strlen(name))
{
unlink(name);
} |
bb564a59 |
#endif
}
bool |
81d882d5 |
unix_socket_get_peer_uid_gid(const socket_descriptor_t sd, int *uid, int *gid) |
bb564a59 |
{
#ifdef HAVE_GETPEEREID |
81d882d5 |
uid_t u;
gid_t g;
if (getpeereid(sd, &u, &g) == -1)
{
return false;
}
if (uid)
{
*uid = u;
}
if (gid)
{
*gid = g;
}
return true; |
bb564a59 |
#elif defined(SO_PEERCRED) |
81d882d5 |
struct ucred peercred;
socklen_t so_len = sizeof(peercred);
if (getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) == -1)
{
return false;
}
if (uid)
{
*uid = peercred.uid;
}
if (gid)
{
*gid = peercred.gid;
}
return true;
#else /* ifdef HAVE_GETPEEREID */ |
bb564a59 |
return false; |
81d882d5 |
#endif /* ifdef HAVE_GETPEEREID */ |
bb564a59 |
}
|
81d882d5 |
#endif /* if UNIX_SOCK_SUPPORT */ |