/*
 *  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.
 *
 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program (see the file COPYING included with this
 *  distribution); if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif

#include "syshead.h"

#include "socket.h"
#include "fdmisc.h"
#include "misc.h"
#include "gremlin.h"
#include "plugin.h"
#include "ps.h"
#include "manage.h"
#include "misc.h"
#include "manage.h"
#include "openvpn.h"

#include "memdbg.h"

const int proto_overhead[] = { /* indexed by PROTO_x */
  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,
};

/*
 * Convert sockflags/getaddr_flags into getaddr_flags
 */
static unsigned int
sf2gaf(const unsigned int getaddr_flags,
       const unsigned int sockflags)
{
  if (sockflags & SF_HOST_RANDOMIZE)
    return getaddr_flags | GETADDR_RANDOMIZE;
  else
    return getaddr_flags;
}

/*
 * 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
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;
  }
}

static inline bool
streqnull (const char* a, const char* b)
{
  if (a == NULL && b == NULL)
    return true;
  else if (a == NULL || b == NULL)
    return false;
  else
    return streq (a, b);
}

/*
  get_cached_dns_entry return 0 on success and -1
  otherwise. (like getaddrinfo)
 */
static int
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)
{
  struct cached_dns_entry *ph;
  int flags;

  /* Only use flags that are relevant for the structure */
  flags = resolve_flags & GETADDR_CACHE_MASK;

  for (ph = dns_cache; ph ; ph = ph->next)
    {
      if (streqnull (ph->hostname, hostname) &&
	  streqnull (ph->servname, servname) &&
	  ph->ai_family == ai_family &&
	  ph->flags == flags)
	{
	  *ai = ph->ai;
	  return 0;
	}
    }
  return -1;
}


static int
do_preresolve_host (struct context *c,
		    const char *hostname,
		    const char *servname,
		    const int af,
		    const int flags)
{
  struct addrinfo *ai;
  int status;

  if (get_cached_dns_entry(c->c1.dns_cache,
			   hostname,
			   servname,
			   af,
			   flags,
			   &ai) == 0 )
    {
      /* entry already cached, return success */
      return 0;
    }

  status = openvpn_getaddrinfo (flags, hostname, servname,
				c->options.resolve_retry_seconds, NULL,
				af, &ai);
  if (status == 0)
    {
      struct cached_dns_entry *ph;

      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;

      if (!c->c1.dns_cache)
	c->c1.dns_cache = ph;
      else
	{
	  struct cached_dns_entry *prev = c->c1.dns_cache;
	  while (prev->next)
	    prev = prev->next;
	  prev->next = ph;
	}

      gc_addspecial (ai, &gc_freeaddrinfo_callback, &c->gc);

    }
  return status;
}

void
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;

	}

    }
    return;

 err:
  throw_signal_soft (SIGHUP, "Preresolving failed");
}

/*
 * Translate IPv4/IPv6 addr or hostname into struct addrinfo
 * If resolve error, try again for resolve_retry_seconds seconds.
 */
int
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)
{
  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;

  ASSERT(res);

  ASSERT (hostname || servname);
  ASSERT (!(flags & GETADDR_HOST_ORDER));

  if (hostname && (flags & GETADDR_RANDOMIZE))
    hostname = hostname_randomize(hostname, &gc);

  if(hostname)
    print_hostname = hostname;
  else
    print_hostname = "undefined";

  if(servname)
    print_servname = servname;
  else
    print_servname = "";

  if (flags & GETADDR_MSG_VIRT_OUT)
    msglevel |= M_MSG_VIRT_OUT;

  if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
      && !signal_received)
    signal_received = &sigrec;

  /* try numeric ipv6 addr first */
  CLEAR(hints);
  hints.ai_family = ai_family;
  hints.ai_flags = AI_NUMERICHOST;

  if(flags & GETADDR_PASSIVE)
      hints.ai_flags |= AI_PASSIVE;

  if(flags & GETADDR_DATAGRAM)
      hints.ai_socktype = SOCK_DGRAM;
  else
      hints.ai_socktype = SOCK_STREAM;

  status = getaddrinfo(hostname, servname, &hints, res);

  if (status != 0) /* parse as numeric address failed? */
    {
      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;

      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.)";

      if (!(flags & GETADDR_RESOLVE) || status == EAI_FAIL)
        {
          msg (msglevel, "RESOLVE: Cannot parse IP address: %s:%s (%s)",
               print_hostname,print_servname, gai_strerror(status));
          goto done;
        }

#ifdef ENABLE_MANAGEMENT
      if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
        {
          if (management)
            management_set_state (management,
                                  OPENVPN_STATE_RESOLVE,
                                  NULL,
                                  (in_addr_t)0,
                                  (in_addr_t)0);
        }
#endif

      /*
       * Resolve hostname
       */
      while (true)
        {
#ifndef WIN32
	  res_init ();
#endif
          /* 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);

          if (signal_received)
            {
              get_signal (signal_received);
              if (*signal_received) /* were we interrupted by a signal? */
                {
                  if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
                    {
                      msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
                      *signal_received = 0;
                    }
                  else
                    {
		      /* turn success into failure (interrupted syscall) */
                      if (0 == status) {
                          ASSERT(res);
                          freeaddrinfo(*res);
                          *res = NULL;
                          status = EAI_AGAIN;	/* = temporary failure */
                          errno = EINTR;
                      }
                      goto done;
                    }
                }
            }

          /* success? */
          if (0 == status)
            break;

          /* resolve lookup failed, should we
             continue or fail? */
          level = msglevel;
          if (resolve_retries > 0)
            level = D_RESOLVE_ERRORS;

          msg (level,
               fmt,
               print_hostname,
               print_servname,
               gai_strerror(status));

          if (--resolve_retries <= 0)
            goto done;

          openvpn_sleep (fail_wait_interval);
        }

      ASSERT(res);

      /* hostname resolve succeeded */

      /*
       * Do not choose an IP Addresse by random or change the order *
       * of IP addresses, doing so will break RFC 3484 address selection *
       */
    }
  else
    {
      /* IP address parse succeeded */
    }

 done:
  if (signal_received && *signal_received)
    {
      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");
    }

  gc_free (&gc);
  return status;
}

/*
 * We do our own inet_aton because the glibc function
 * isn't very good about error checking.
 */
int
openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr)
{
  unsigned int a, b, c, d;

  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
    return OIA_HOSTNAME; /* probably a hostname */
}

bool
ip_addr_dotted_quad_safe (const char *dotted_quad)
{
  /* verify non-NULL */
  if (!dotted_quad)
    return false;

  /* 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;

    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;
  }
}

bool
ipv6_addr_safe (const char *ipv6_text_addr)
{
  /* verify non-NULL */
  if (!ipv6_text_addr)
    return false;

  /* verify length is within limits */
  if (strlen (ipv6_text_addr) > INET6_ADDRSTRLEN )
    return false;

  /* verify that string will convert to IPv6 address */
  {
    struct in6_addr a6;
    return inet_pton( AF_INET6, ipv6_text_addr, &a6 ) == 1;
  }
}

static bool
dns_addr_safe (const char *addr)
{
  if (addr)
    {
      const size_t len = strlen (addr);
      return len > 0 && len <= 255 && string_class (addr, CC_ALNUM|CC_DASH|CC_DOT, 0);
    }
  else
    return false;
}

bool
ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn)
{
  if (ip_addr_dotted_quad_safe (addr))
    return true;
  else if (allow_fqdn)
    return dns_addr_safe (addr);
  else
    return false;
}

bool
mac_addr_safe (const char *mac_addr)
{
  /* verify non-NULL */
  if (!mac_addr)
    return false;

  /* 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;
      }
  }

  /* error-checking is left to script invoked in lladdr.c */
  return true;
}

static int
socket_get_sndbuf (int sd)
{
#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF)
  int val;
  socklen_t len;

  len = sizeof (val);
  if (getsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &val, &len) == 0
      && len == sizeof (val))
    return val;
#endif
  return 0;
}

static void
socket_set_sndbuf (int sd, int size)
{
#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF)
  if (setsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof (size)) != 0)
    {
      msg (M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size);
    }
#endif
}

static int
socket_get_rcvbuf (int sd)
{
#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF)
  int val;
  socklen_t len;

  len = sizeof (val);
  if (getsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &val, &len) == 0
      && len == sizeof (val))
    return val;
#endif
  return 0;
}

static bool
socket_set_rcvbuf (int sd, int size)
{
#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF)
  if (setsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof (size)) != 0)
    {
      msg (M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size);
      return false;
    }
  return true;
#endif
}

static void
socket_set_buffers (int fd, const struct socket_buffer_size *sbs)
{
  if (sbs)
    {
      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);

      if (sbs->rcvbuf)
	socket_set_rcvbuf (fd, sbs->rcvbuf);
       
      msg (D_OSBUF, "Socket Buffers: R=[%d->%d] S=[%d->%d]",
	   rcvbuf_old,
	   socket_get_rcvbuf (fd),
	   sndbuf_old,
	   socket_get_sndbuf (fd));
    }
}

/*
 * Set other socket options
 */

static bool
socket_set_tcp_nodelay (int sd, int state)
{
#if defined(WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY))
  if (setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof (state)) != 0)
    {
      msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state);
      return false;
    }
  else
    {
      dmsg (D_OSBUF, "Socket flags: TCP_NODELAY=%d succeeded", state);
      return true;
    }
#else
  msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed (No kernel support)", state);
  return false;
#endif
}

static inline void
socket_set_mark (int sd, int mark)
{
#if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK
  if (mark && setsockopt (sd, SOL_SOCKET, SO_MARK, (void *) &mark, sizeof (mark)) != 0)
    msg (M_WARN, "NOTE: setsockopt SO_MARK=%d failed", mark);
#endif
}

static bool
socket_set_flags (int sd, unsigned int sockflags)
{
  if (sockflags & SF_TCP_NODELAY)
    return socket_set_tcp_nodelay (sd, 1);
  else
    return true;
}

bool
link_socket_update_flags (struct link_socket *ls, unsigned int sockflags)
{
  if (ls && socket_defined (ls->sd))
    return socket_set_flags (ls->sd, ls->sockflags = sockflags);
  else
    return false;
}

void
link_socket_update_buffer_sizes (struct link_socket *ls, int rcvbuf, int sndbuf)
{
  if (ls && socket_defined (ls->sd))
    {
      ls->socket_buffer_sizes.sndbuf = sndbuf;
      ls->socket_buffer_sizes.rcvbuf = rcvbuf;
      socket_set_buffers (ls->sd, &ls->socket_buffer_sizes);
    }
}

/*
 * SOCKET INITALIZATION CODE.
 * Create a TCP/UDP socket
 */

socket_descriptor_t
create_socket_tcp (struct addrinfo* addrinfo)
{
  socket_descriptor_t sd;

  ASSERT (addrinfo);
  ASSERT (addrinfo->ai_socktype == SOCK_STREAM);

  if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0)
    msg (M_ERR, "Cannot create TCP socket");

#ifndef WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */
  /* 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");
  }
#endif

  return sd;
}

static socket_descriptor_t
create_socket_udp (struct addrinfo* addrinfo, const unsigned int flags)
{
  socket_descriptor_t sd;

  ASSERT (addrinfo);
  ASSERT (addrinfo->ai_socktype == SOCK_DGRAM);

  if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0)
    msg (M_ERR, "UDP: Cannot create UDP/UDP6 socket");
#if ENABLE_IP_PKTINFO
  else if (flags & SF_USE_IP_PKTINFO)
    {
      int pad = 1;
      if(addrinfo->ai_family == AF_INET)
        {
#ifdef IP_PKTINFO
          if (setsockopt (sd, SOL_IP, IP_PKTINFO,
                          (void*)&pad, sizeof(pad)) < 0)
            msg(M_ERR, "UDP: failed setsockopt for IP_PKTINFO");
#elif defined(IP_RECVDSTADDR)
          if (setsockopt (sd, IPPROTO_IP, IP_RECVDSTADDR,
                          (void*)&pad, sizeof(pad)) < 0)
            msg(M_ERR, "UDP: failed setsockopt for IP_RECVDSTADDR");
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif
        }
      else if (addrinfo->ai_family == AF_INET6 )
        {
#ifndef IPV6_RECVPKTINFO /* Some older Darwin platforms require this */
          if (setsockopt (sd, IPPROTO_IPV6, IPV6_PKTINFO,
                          (void*)&pad, sizeof(pad)) < 0)
#else
          if (setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                          (void*)&pad, sizeof(pad)) < 0)
#endif
            msg(M_ERR, "UDP: failed setsockopt for IPV6_RECVPKTINFO");
        }
    }
#endif
  return sd;
}

static void bind_local (struct link_socket *sock, const sa_family_t ai_family)
{
    /* bind to local address/port */
    if (sock->bind_local)
      {
        if (sock->socks_proxy && sock->info.proto == PROTO_UDP)
            socket_bind (sock->ctrl_sd, sock->info.lsa->bind_local,
			 ai_family, "SOCKS", false);
        else
            socket_bind (sock->sd, sock->info.lsa->bind_local,
			 ai_family,
                         "TCP/UDP", sock->info.bind_ipv6_only);
      }
}

static void
create_socket (struct link_socket* sock, struct addrinfo* addr)
{
  if (addr->ai_protocol == IPPROTO_UDP || addr->ai_socktype == SOCK_DGRAM)
    {
      sock->sd = create_socket_udp (addr, sock->sockflags);
      sock->sockflags |= SF_GETADDRINFO_DGRAM;

      /* 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);
	}
    }
  else if (addr->ai_protocol == IPPROTO_TCP || addr->ai_socktype == SOCK_STREAM)
    {
      sock->sd = create_socket_tcp (addr);
    }
  else
    {
      ASSERT (0);
    }
    /* set socket buffers based on --sndbuf and --rcvbuf options */
    socket_set_buffers (sock->sd, &sock->socket_buffer_sizes);

    /* set socket to --mark packets with given value */
    socket_set_mark (sock->sd, sock->mark);

    bind_local (sock, addr->ai_family);
}

#ifdef TARGET_ANDROID
static void protect_fd_nonlocal (int fd, const struct sockaddr* addr)
{
  /* 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;
  }

  msg(D_SOCKET_DEBUG, "Protecting socket fd %d", fd);
  management->connection.fdtosend = fd;
  management_android_control (management, "PROTECTFD", __func__);
}
#endif

/*
 * Functions used for establishing a TCP stream connection.
 */
static void
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");
    }

  /* set socket to non-blocking mode */
  if (do_set_nonblock)
    set_nonblock (sd);

  gc_free (&gc);
}

socket_descriptor_t
socket_do_accept (socket_descriptor_t sd,
		  struct link_socket_actual *act,
		  const bool nowait)
{
  /* 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;

  CLEAR (*act);

#ifdef HAVE_GETPEERNAME
  if (nowait)
    {
      new_sd = getpeername (sd, &act->dest.addr.sa, &remote_len);

      if (!socket_defined (new_sd))
	msg (D_LINK_ERRORS | M_ERRNO, "TCP: getpeername() failed");
      else
	new_sd = sd;
    }
#else
  if (nowait)
    msg (M_WARN, "TCP: this OS does not provide the getpeername() function");
#endif
  else
    {
      new_sd = accept (sd, &act->dest.addr.sa, &remote_len);
    }

#if 0 /* For debugging only, test the effect of accept() failures */
 {
   static int foo = 0;
   ++foo;
   if (foo & 1)
     new_sd = -1;
 }
#endif

  if (!socket_defined (new_sd))
    {
      msg (D_LINK_ERRORS | M_ERRNO, "TCP: accept(%d) failed", sd);
    }
  /* only valid if we have remote_len_af!=0 */
  else if (remote_len_af && remote_len != remote_len_af)
    {
      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;
    }
  return new_sd;
}

static void
tcp_connection_established (const struct link_socket_actual *act)
{
  struct gc_arena gc = gc_new ();
  msg (M_INFO, "TCP connection established with %s", 
       print_link_socket_actual (act, &gc));
  gc_free (&gc);
}

static int
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;
  int new_sd = SOCKET_UNDEFINED;

  CLEAR (*act);
  socket_do_listen (sd, local, do_listen, true);

  while (true)
    {
      int status;
      fd_set reads;
      struct timeval tv;

      FD_ZERO (&reads);
      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)
	{
	  openvpn_sleep (1);
	  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,
                                    remote_verify.addr.sa.sa_family, &ai);

          if(ai && !addrlist_match(&remote_verify, ai))
            {
              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);
            }
	  else
            {
              if(ai)
                  freeaddrinfo(ai);
              break;
            }
	}
      openvpn_sleep (1);
    }

  if (!nowait && openvpn_close_socket (sd))
    msg (M_ERR, "TCP: close socket failed (sd)");

  tcp_connection_established (act);

  gc_free (&gc);
  return new_sd;
}

/* older mingw versions and WinXP do not have this define,
 * but Vista and up support the functionality - just define it here
 */
#ifdef WIN32
# ifndef IPV6_V6ONLY
#  define IPV6_V6ONLY 27
# endif
#endif
void
socket_bind (socket_descriptor_t sd,
             struct addrinfo *local,
             int ai_family,
	     const char *prefix,
             bool ipv6only)
{
  struct gc_arena gc = gc_new ();

  /* 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?
   */

  struct addrinfo* cur;

  ASSERT(local);


  /* 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)
      msg (M_FATAL, "%s: Socket bind failed: Addr to bind has no %s record",
           prefix, addr_family_name(ai_family));

  if (ai_family == AF_INET6)
    {
      int v6only = ipv6only ? 1: 0;	/* setsockopt must have an "int" */

      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);
	}
    }
  if (bind (sd, cur->ai_addr, cur->ai_addrlen))
    {
      const int errnum = openvpn_errno ();
      msg (M_FATAL, "%s: Socket bind failed on local address %s: %s",
	   prefix,
           print_sockaddr_ex (local->ai_addr, ":", PS_SHOW_PORT, &gc),
           strerror_ts (errnum, &gc));
    }
  gc_free (&gc);
}

int
openvpn_connect (socket_descriptor_t sd,
		 const struct sockaddr *remote,
		 int connect_timeout,
		 volatile int *signal_received)
{
  int status = 0;

#ifdef TARGET_ANDROID
  protect_fd_nonlocal(sd, remote);
#endif

#ifdef CONNECT_NONBLOCK
  set_nonblock (sd);
  status = connect (sd, remote, af_addr_size(remote->sa_family));
  if (status)
    status = openvpn_errno ();
  if (
#ifdef WIN32
    status == WSAEWOULDBLOCK
#else
    status == EINPROGRESS
#endif
  )
    {
      while (true)
	{
	  fd_set writes;
	  struct timeval tv;

	  FD_ZERO (&writes);
	  FD_SET (sd, &writes);
	  tv.tv_sec = 0;
	  tv.tv_usec = 0;

	  status = select (sd + 1, NULL, &writes, NULL, &tv);

	  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)
		{
		  status = ETIMEDOUT;
		  break;
		}
	      openvpn_sleep (1);
	      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;
	  }
	}
    }
#else
  status = connect (sd, remote, af_addr_size(remote->sa_family));
  if (status)
    status = openvpn_errno ();
#endif

  return status;
}

void set_actual_address (struct link_socket_actual* actual, struct addrinfo* ai)
{
    CLEAR (*actual);
    ASSERT (ai);

    if (ai->ai_family == AF_INET)
        actual->dest.addr.in4 =
        *((struct sockaddr_in*) ai->ai_addr);
    else if (ai->ai_family == AF_INET6)
        actual->dest.addr.in6 =
        *((struct sockaddr_in6*) ai->ai_addr);
    else
        ASSERT(0);

}

void
socket_connect (socket_descriptor_t* sd,
                const struct sockaddr* dest,
                const int connect_timeout,
                struct signal_info* sig_info)
{
  struct gc_arena gc = gc_new ();
  int status;

#ifdef CONNECT_NONBLOCK
  msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]",
       print_sockaddr (dest, &gc));
#else
  msg (M_INFO, "Attempting to establish TCP connection with %s",
       print_sockaddr (dest, &gc));
#endif

#ifdef ENABLE_MANAGEMENT
  if (management)
	management_set_state (management,
			      OPENVPN_STATE_TCP_CONNECT,
			      NULL,
			      (in_addr_t)0,
			      (in_addr_t)0);
#endif

  /* Set the actual address */
  status = openvpn_connect (*sd, dest, connect_timeout, &sig_info->signal_received);

  get_signal (&sig_info->signal_received);
  if (sig_info->signal_received)
	goto done;

  if (status) {

    msg (D_LINK_ERRORS,
         "TCP: connect to %s failed: %s",
         print_sockaddr (dest, &gc),
         strerror_ts (status, &gc));

    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));
  }

 done:
  gc_free (&gc);
}

/* For stream protocols, allocate a buffer to build up packet.
   Called after frame has been finalized. */

static void
socket_frame_init (const struct frame *frame, struct link_socket *sock)
{
#ifdef WIN32
  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;
#endif

  if (link_socket_connection_oriented (sock))
    {
#ifdef WIN32
      stream_buf_init (&sock->stream_buf,
		       &sock->reads.buf_init,
		       sock->sockflags,
		       sock->info.proto);
#else
      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);
#endif
    }
}

/*
 * Adjust frame structure based on a Path MTU value given
 * to us by the OS.
 */
void
frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto)
{
  frame_set_mtu_dynamic (frame, pmtu - datagram_overhead (proto), SET_MTU_UPPER_BOUND);
}

static void
resolve_bind_local (struct link_socket *sock, const sa_family_t af)
{
  struct gc_arena gc = gc_new ();

  /* resolve local address if undefined */
  if (!sock->info.lsa->bind_local)
    {
      int flags = GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL |
	GETADDR_FATAL | GETADDR_PASSIVE;
      int status;

      if(proto_is_dgram(sock->info.proto))
	flags |= GETADDR_DATAGRAM;

      /* 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);

      if (status)
	status = openvpn_getaddrinfo(flags, sock->local_host, sock->local_port, 0,
				   NULL, af, &sock->info.lsa->bind_local);

      if(status !=0) {
	msg (M_FATAL, "getaddrinfo() failed for local \"%s:%s\": %s",
	     sock->local_host, sock->local_port,
	     gai_strerror(status));
      }
    }

  gc_free (&gc);
}

static void
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);
}



struct link_socket *
link_socket_new (void)
{
  struct link_socket *sock;

  ALLOC_OBJ_CLEAR (sock, struct link_socket);
  sock->sd = SOCKET_UNDEFINED;
  sock->ctrl_sd = SOCKET_UNDEFINED;
  return sock;
}

void
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,
#ifdef ENABLE_DEBUG
			 int gremlin,
#endif
			 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 connect_timeout,
			 int mtu_discover_type,
			 int rcvbuf,
			 int sndbuf,
			 int mark,
			 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->connect_timeout = connect_timeout;
  sock->mtu_discover_type = mtu_discover_type;

#ifdef ENABLE_DEBUG
  sock->gremlin = gremlin;
#endif

  sock->socket_buffer_sizes.rcvbuf = rcvbuf;
  sock->socket_buffer_sizes.sndbuf = sndbuf;

  sock->sockflags = sockflags;
  sock->mark = mark;

  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->mode = mode;
  if (mode == LS_MODE_TCP_ACCEPT_FROM)
    {
      ASSERT (accept_from);
      ASSERT (sock->info.proto == PROTO_TCP_SERVER);
      ASSERT (!sock->inetd);
      sock->sd = accept_from->sd;
    }

  /* are we running in HTTP proxy mode? */
  if (sock->http_proxy)
    {
      ASSERT (sock->info.proto == PROTO_TCP_CLIENT);
      ASSERT (!sock->inetd);

      /* the proxy server */
      sock->remote_host = http_proxy->options.server;
      sock->remote_port = http_proxy->options.port;

      /* the OpenVPN server we will use the proxy to connect to */
      sock->proxy_dest_host = remote_host;
      sock->proxy_dest_port = remote_port;
    }
  /* or in Socks proxy mode? */
  else if (sock->socks_proxy)
    {
      ASSERT (!sock->inetd);

      /* the proxy server */
      sock->remote_host = socks_proxy->server;
      sock->remote_port = socks_proxy->port;

      /* the OpenVPN server we will use the proxy to connect to */
      sock->proxy_dest_host = remote_host;
      sock->proxy_dest_port = remote_port;
    }
  else
    {
      sock->remote_host = remote_host;
      sock->remote_port = remote_port;
    }

  /* bind behavior for TCP server vs. client */
  if (sock->info.proto == PROTO_TCP_SERVER)
    {
      if (sock->mode == LS_MODE_TCP_ACCEPT_FROM)
	sock->bind_local = false;
      else
	sock->bind_local = true;
    }

  /* were we started by inetd or xinetd? */
  if (sock->inetd)
    {
      ASSERT (sock->info.proto != PROTO_TCP_CLIENT);
      ASSERT (socket_defined (inetd_socket_descriptor));
      sock->sd = inetd_socket_descriptor;
    }
  else if (mode != LS_MODE_TCP_ACCEPT_FROM)
    {
      if (sock->bind_local) {
	  resolve_bind_local (sock, sock->info.af);
      }
      resolve_remote (sock, 1, NULL, NULL);
    }
}

static
void phase2_inetd (struct link_socket* sock, const struct frame *frame,
		   const char *remote_dynamic, volatile int *signal_received)
{
  bool remote_changed = false;

  if (sock->info.proto == PROTO_TCP_SERVER) {
    /* 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),
	      local_addr.addr.sa.sa_family, sock->sd);
      } else
	msg (M_WARN, "inetd(%s): getsockname(%d) failed, using AF_INET",
	     proto2ascii(sock->info.proto, sock->info.af, false), sock->sd);
    }
#else
    msg (M_WARN, "inetd(%s): this OS does not provide the getsockname() "
	 "function, using AF_INET",
	 proto2ascii(sock->info.proto, false));
#endif
    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);

  }
  ASSERT (!remote_changed);
}

static void
phase2_set_socket_flags (struct link_socket* sock)
{
  /* set misc socket parameters */
  socket_set_flags (sock->sd, sock->sockflags);

  /* set socket to non-blocking mode */
  set_nonblock (sock->sd);

  /* set socket file descriptor to not pass across execs, so that
     scripts don't have access to it */
  set_cloexec (sock->sd);

  if (socket_defined (sock->ctrl_sd))
    set_cloexec (sock->ctrl_sd);

  /* set Path MTU discovery options on the socket */
  set_mtu_discover_type (sock->sd, sock->mtu_discover_type, sock->info.af);

#if EXTENDED_SOCKET_ERROR_CAPABILITY
  /* if the OS supports it, enable extended error passing on the socket */
  set_sock_extended_error_passing (sock->sd);
#endif
}


static void
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);
}

static void
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);
    }
}


static void
phase2_tcp_client (struct link_socket *sock, struct signal_info *sig_info)
{
  bool proxy_retry = false;
  do {
    socket_connect (&sock->sd,
                   sock->info.lsa->current_remote->ai_addr,
                   sock->connect_timeout,
                   sig_info);

    if (sig_info->signal_received)
      return;

    if (sock->http_proxy)
      {
	proxy_retry = establish_http_proxy_passthru (sock->http_proxy,
						     sock->sd,
						     sock->proxy_dest_host,
						     sock->proxy_dest_port,
						     &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);
      }

  } while (proxy_retry);

}

static void
phase2_socks_client (struct link_socket *sock, struct signal_info *sig_info)
{
    socket_connect (&sock->ctrl_sd,
		    sock->info.lsa->current_remote->ai_addr,
		    sock->connect_timeout,
		    sig_info);

    if (sig_info->signal_received)
	return;

    establish_socks_proxy_udpassoc (sock->socks_proxy,
				    sock->ctrl_sd,
				    sock->sd,
				    &sock->socks_relay.dest,
				    &sig_info->signal_received);

    if (sig_info->signal_received)
	return;

    sock->remote_host = sock->proxy_dest_host;
    sock->remote_port = sock->proxy_dest_port;

    addr_zero_host(&sock->info.lsa->actual.dest);
    if (sock->info.lsa->remote_list)
      {
	freeaddrinfo(sock->info.lsa->remote_list);
	sock->info.lsa->current_remote = NULL;
	sock->info.lsa->remote_list = NULL;
      }

    resolve_remote (sock, 1, NULL, &sig_info->signal_received);
}

/* finalize socket initialization */
void
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);

  if (sig_info && 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 && 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));

	      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 && 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);
	}
#ifdef TARGET_ANDROID
      if (sock->sd != -1)
	protect_fd_nonlocal (sock->sd, &sock->info.lsa->actual.dest.addr.sa);
#endif
      if (sig_info && sig_info->signal_received)
	goto done;
    }

  phase2_set_socket_flags(sock);
  linksock_print_addr(sock);

 done:
  if (sig_save && sig_info)
    {
      if (!sig_info->signal_received)
	sig_info->signal_received = sig_save;
    }
}

void
link_socket_close (struct link_socket *sock)
{
  if (sock)
    {
#ifdef ENABLE_DEBUG
      const int gremlin = GREMLIN_CONNECTION_FLOOD_LEVEL (sock->gremlin);
#else
      const int gremlin = 0;
#endif

      if (socket_defined (sock->sd))
	{
#ifdef WIN32
	  close_net_event_win32 (&sock->listen_handle, sock->sd, 0);
#endif
	  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;
#ifdef WIN32
	  if (!gremlin)
	    {
	      overlapped_io_close (&sock->reads);
	      overlapped_io_close (&sock->writes);
	    }
#endif
	}

      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;
	}

      stream_buf_close (&sock->stream_buf);
      free_buf (&sock->stream_buf_data);
      if (!gremlin)
	free (sock);
    }
}

/* for stream protocols, allow for packet length prefix */
void
socket_adjust_frame_parameters (struct frame *frame, int proto)
{
  if (link_socket_proto_connection_oriented (proto))
    frame_add_to_extra_frame (frame, sizeof (packet_size_type));
}

void
setenv_trusted (struct env_set *es, const struct link_socket_info *info)
{
  setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
}

static void
ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc)
{
  const char *host = print_sockaddr_ex (&info->lsa->actual.dest.addr.sa, " ", PS_SHOW_PORT , gc);
  if (include_cmd)
    argv_printf (argv, "%sc %s",
		 info->ipchange_command,
		 host);
  else
    argv_printf (argv, "%s", host);

}

void
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)
{
  struct gc_arena gc = gc_new ();
  
  info->lsa->actual = *act; /* Note: skip this line for --force-dest */
  setenv_trusted (es, info);
  info->connection_established = true;

  /* 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);

  /* Process --ipchange plugin */
  if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
    {
      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);
    }

  /* Process --ipchange option */
  if (info->ipchange_command)
    {
      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);
    }

  gc_free (&gc);
}

void
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 */
          for(ai=info->lsa->remote_list->ai_next;ai;ai=ai->ai_next) {
             msg(D_LINK_ERRORS,"or from peer address: %s",
                 print_sockaddr_ex(ai->ai_addr,":",PS_SHOW_PORT, &gc));
          }
      break;
    }
  buf->len = 0;
  gc_free (&gc);
}

void
link_socket_bad_outgoing_addr (void)
{
  dmsg (D_READ_WRITE, "TCP/UDP: No outgoing address to send packet");
}

in_addr_t
link_socket_current_remote (const struct link_socket_info *info)
{
  const struct link_socket_addr *lsa = info->lsa;

/* 
 * This logic supports "redirect-gateway" semantic, which 
 * 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
 *
 * For --remote entries with multiple addresses this
 * only return the actual endpoint we have sucessfully connected to
 */
  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;
}

const struct in6_addr *
link_socket_current_remote_ipv6 (const struct link_socket_info *info)
{
  const struct link_socket_addr *lsa = info->lsa;

/* 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
 */
  if (lsa->actual.dest.addr.sa.sa_family != AF_INET6)
    return NULL;

  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;
}

/*
 * Return a status string describing socket state.
 */
const char *
socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc)
{
  struct buffer out = alloc_buf_gc (64, gc);
  if (s)
    {
      if (rwflags & EVENT_READ)
	{
	  buf_printf (&out, "S%s",
		      (s->rwflags_debug & EVENT_READ) ? "R" : "r");
#ifdef WIN32
	  buf_printf (&out, "%s",
		      overlapped_io_state_ascii (&s->reads));
#endif
	}
      if (rwflags & EVENT_WRITE)
	{
	  buf_printf (&out, "S%s",
		      (s->rwflags_debug & EVENT_WRITE) ? "W" : "w");
#ifdef WIN32
	  buf_printf (&out, "%s",
		      overlapped_io_state_ascii (&s->writes));
#endif
	}
    }
  else
    {
      buf_printf (&out, "S?");
    }
  return BSTR (&out);
}

/*
 * Stream buffer functions, used to packetize a TCP
 * stream connection.
 */

static inline void
stream_buf_reset (struct stream_buf *sb)
{
  dmsg (D_STREAM_DEBUG, "STREAM: RESET");
  sb->residual_fully_formed = false;
  sb->buf = sb->buf_init;
  buf_reset (&sb->next);
  sb->len = -1;
}

void
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;
#if PORT_SHARE
  sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCP_SERVER))
    ? PS_ENABLED
    : PS_DISABLED;
#endif
  stream_buf_reset (sb);

  dmsg (D_STREAM_DEBUG, "STREAM: INIT maxlen=%d", sb->maxlen);
}

static inline void
stream_buf_set_next (struct stream_buf *sb)
{
  /* 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));
}

static inline void
stream_buf_get_final (struct stream_buf *sb, struct buffer *buf)
{
  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;
}

static inline void
stream_buf_get_next (struct stream_buf *sb, struct buffer *buf)
{
  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;
}

bool
stream_buf_read_setup_dowork (struct link_socket* sock)
{
  if (sock->stream_buf.residual.len && !sock->stream_buf.residual_fully_formed)
    {
      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);
    }

  if (!sock->stream_buf.residual_fully_formed)
    stream_buf_set_next (&sock->stream_buf);
  return !sock->stream_buf.residual_fully_formed;
}

bool
stream_buf_added (struct stream_buf *sb,
		  int length_added)
{
  dmsg (D_STREAM_DEBUG, "STREAM: ADD length_added=%d", length_added);
  if (length_added > 0)
    sb->buf.len += length_added;

  /* 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))
    {
      packet_size_type net_size;

#if PORT_SHARE
      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;
	}
#endif

      ASSERT (buf_read (&sb->buf, &net_size, sizeof (net_size)));
      sb->len = ntohps (net_size);

      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;
	}
    }

  /* is our incoming packet fully read? */
  if (sb->len > 0 && sb->buf.len >= sb->len)
    {
      /* 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;
    }
  else
    {
      dmsg (D_STREAM_DEBUG, "STREAM: ADD returned FALSE (have=%d need=%d)", sb->buf.len, sb->len);
      stream_buf_set_next (sb);
      return false;
    }
}

void
stream_buf_close (struct stream_buf* sb)
{
  free_buf (&sb->residual);
}

/*
 * 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
socket_listen_event_handle (struct link_socket *s)
{
#ifdef WIN32
  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
  return s->sd;
#endif
}

/*
 * Format IP addresses in ascii
 */

const char *
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:
      buf_puts (&out, "[AF_INET]");
      salen = sizeof (struct sockaddr_in);
      addr_is_defined = ((struct sockaddr_in*) sa)->sin_addr.s_addr != 0;
      break;
    case AF_INET6:
      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:
      return "[AF_UNSPEC]";
    default:
      ASSERT(0);
    }

  status = getnameinfo(sa, salen, hostaddr, sizeof (hostaddr),
              servname, sizeof(servname), NI_NUMERICHOST | NI_NUMERICSERV);

  if(status!=0) {
      buf_printf(&out,"[nameinfo() err: %s]",gai_strerror(status));
      return BSTR(&out);
  }

  if (!(flags & PS_DONT_SHOW_ADDR))
    {
      if (addr_is_defined)
        buf_puts (&out, hostaddr);
      else
        buf_puts (&out, "[undef]");
    }

  if ((flags & PS_SHOW_PORT) || (flags & PS_SHOW_PORT_IF_DEFINED))
    {
      if (separator)
        buf_puts (&out, separator);

      buf_puts (&out, servname);
    }

  return BSTR (&out);
}

const char *
print_link_socket_actual (const struct link_socket_actual *act, struct gc_arena *gc)
{
  return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc);
}

#ifndef IF_NAMESIZE
#define IF_NAMESIZE 16
#endif

const char *
print_link_socket_actual_ex (const struct link_socket_actual *act,
			     const char *separator,
			     const unsigned int flags,
			     struct gc_arena *gc)
{
  if (act)
    {
      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));
#if ENABLE_IP_PKTINFO
      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;
#ifdef IP_PKTINFO
		  sa.addr.in4.sin_addr = act->pi.in4.ipi_spec_dst;
		  if_indextoname(act->pi.in4.ipi_ifindex, ifname);
#elif defined(IP_RECVDSTADDR)
		  sa.addr.in4.sin_addr = act->pi.in4;
		  ifname[0]=0;
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif
		  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
      return BSTR (&out);
    }
  else
    return "[NULL]";
}

/*
 * Convert an in_addr_t in host byte order
 * to an ascii dotted quad.
 */
const char *
print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc)
{
  struct in_addr ia;
  struct buffer out = alloc_buf_gc (64, gc);

  if (addr || !(flags & IA_EMPTY_IF_UNDEF))
    {
      CLEAR (ia);
      ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl (addr);

      buf_printf (&out, "%s", inet_ntoa (ia));
    }
  return BSTR (&out);
}

/*
 * Convert an in6_addr in host byte order
 * to an ascii representation of an IPv6 address
 */
const char *
print_in6_addr (struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
{
  struct buffer out = alloc_buf_gc (64, gc);
  char tmp_out_buf[64];		/* inet_ntop wants pointer to buffer */

  if ( memcmp(&a6, &in6addr_any, sizeof(a6)) != 0 || 
       !(flags & IA_EMPTY_IF_UNDEF))
    {
      inet_ntop (AF_INET6, &a6, tmp_out_buf, sizeof(tmp_out_buf)-1);
      buf_printf (&out, "%s", tmp_out_buf );
    }
  return BSTR (&out);
}

#ifndef UINT8_MAX
# define UINT8_MAX 0xff
#endif

/* add some offset to an ipv6 address
 * (add in steps of 8 bits, taking overflow into next round)
 */
struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add )
{
    int i;

    for( i=15; i>=0 && add > 0 ; i-- )
    {
	register int carry;
	register uint32_t h;

	h = (unsigned char) base.s6_addr[i];
	base.s6_addr[i] = (h+add) & UINT8_MAX;

	/* using explicit carry for the 8-bit additions will catch
         * 8-bit and(!) 32-bit overruns nicely
         */
	carry = ((h & 0xff)  + (add & 0xff)) >> 8;
	add = (add>>8) + carry;
    }
    return base;
}

/* set environmental variables for ip/port in *addr */
void
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;
	  ia.s_addr = *(in_addr_t *)&addr->addr.in6.sin6_addr.s6_addr[12] ;
	  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;
    }
}

void
setenv_in_addr_t (struct env_set *es, const char *name_prefix, in_addr_t addr, const unsigned int flags)
{
  if (addr || !(flags & SA_SET_IF_NONZERO))
    {
      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);
    }
}

void
setenv_link_socket_actual (struct env_set *es,
			   const char *name_prefix,
			   const struct link_socket_actual *act,
			   const unsigned int flags)
{
  setenv_sockaddr (es, name_prefix, &act->dest, flags);
}

/*
 * Convert protocol names between index and ascii form.
 */

struct proto_names {
  const char *short_form;
  const char *display_form;
  sa_family_t proto_af;
  int proto;
};

/* Indexed by PROTO_x */
static const struct proto_names proto_names[] = {
  {"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},
};

bool
proto_is_net(int proto)
{
  if (proto < 0 || proto >= PROTO_N)
    ASSERT(0);
    return proto != PROTO_NONE;
}
bool
proto_is_dgram(int proto)
{
    return proto_is_udp(proto);
}

bool
proto_is_udp(int proto)
{
  if (proto < 0 || proto >= PROTO_N)
    ASSERT(0);
  return proto == PROTO_UDP;
}

bool
proto_is_tcp(int proto)
{
  if (proto < 0 || proto >= PROTO_N)
    ASSERT(0);
  return proto == PROTO_TCP_CLIENT || proto == PROTO_TCP_SERVER;
}

int
ascii2proto (const char* proto_name)
{
  int i;
  for (i = 0; i < SIZE (proto_names); ++i)
    if (!strcmp (proto_name, proto_names[i].short_form))
      return proto_names[i].proto;
  return -1;
}

sa_family_t
ascii2af (const char* proto_name)
{
    int i;
    for (i = 0; i < SIZE (proto_names); ++i)
        if (!strcmp (proto_name, proto_names[i].short_form))
            return proto_names[i].proto_af;
    return 0;
}

const char *
proto2ascii (int proto, sa_family_t af, bool display_form)
{
  unsigned int i;
  for (i = 0; i < SIZE (proto_names); ++i)
    {
      if(proto_names[i].proto_af == af && proto_names[i].proto == proto)
        {
          if(display_form)
              return proto_names[i].display_form;
          else
              return proto_names[i].short_form;
        }
    }

  return "[unknown protocol]";
}

const char *
proto2ascii_all (struct gc_arena *gc)
{
  struct buffer out = alloc_buf_gc (256, gc);
  int i;

  for (i = 0; i < SIZE (proto_names); ++i)
    {
      if (i)
	buf_printf(&out, " ");
      buf_printf(&out, "[%s]", proto_names[i].short_form);
    }
  return BSTR (&out);
}

const char *
addr_family_name (int af) 
{
  switch (af)
    {
    case AF_INET:  return "AF_INET";
    case AF_INET6: return "AF_INET6";
    }
  return "AF_UNSPEC";
}

/*
 * Given a local proto, return local proto
 * if !remote, or compatible remote proto
 * if remote.
 *
 * This is used for options compatibility
 * checking.
 *
 * IPv6 and IPv4 protocols are comptabile but OpenVPN
 * has always sent UDPv4, TCPv4 over the wire. Keep these
 * strings for backward compatbility
 */
const char*
proto_remote (int proto, bool remote)
{
  ASSERT (proto >= 0 && proto < PROTO_N);
  if (proto == PROTO_UDP)
	return "UDPv4";

  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";

  ASSERT (0);
  return ""; /* Make the compiler happy */
}

/*
 * Bad incoming address lengths that differ from what
 * we expect are considered to be fatal errors.
 */
void
bad_address_length (int actual, int expected)
{
  msg (M_FATAL, "ERROR: received strange incoming packet with an address length of %d -- we only accept address lengths of %d.",
       actual,
       expected);
}

/*
 * Socket Read Routines
 */

int
link_socket_read_tcp (struct link_socket *sock,
		      struct buffer *buf)
{
  int len = 0;

  if (!sock->stream_buf.residual_fully_formed)
    {
#ifdef WIN32
      len = socket_finalize (sock->sd, &sock->reads, buf, NULL);
#else
      struct buffer frag;
      stream_buf_get_next (&sock->stream_buf, &frag);
      len = recv (sock->sd, BPTR (&frag), BLEN (&frag), MSG_NOSIGNAL);
#endif

      if (!len)
	sock->stream_reset = true;
      if (len <= 0)
	return buf->len = len;
    }

  if (sock->stream_buf.residual_fully_formed
      || stream_buf_added (&sock->stream_buf, len)) /* packet complete? */
    {
      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 */
}

#ifndef WIN32

#if ENABLE_IP_PKTINFO

#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */
struct openvpn_in4_pktinfo
{
  struct cmsghdr cmsghdr;
#ifdef HAVE_IN_PKTINFO
  struct in_pktinfo pi4;
#elif defined(IP_RECVDSTADDR)
  struct in_addr pi4;
#endif
};
struct openvpn_in6_pktinfo
{
  struct cmsghdr cmsghdr;
  struct in6_pktinfo pi6;
};

union openvpn_pktinfo {
	struct openvpn_in4_pktinfo msgpi4;
	struct openvpn_in6_pktinfo msgpi6;
};
#pragma pack()

static socklen_t
link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
				    struct buffer *buf,
				    int maxsize,
				    struct link_socket_actual *from)
{
  struct iovec iov;
  union openvpn_pktinfo opi;
  struct msghdr mesg;
  socklen_t fromlen = sizeof (from->dest.addr);

  iov.iov_base = BPTR (buf);
  iov.iov_len = maxsize;
  mesg.msg_iov = &iov;
  mesg.msg_iovlen = 1;
  mesg.msg_name = &from->dest.addr;
  mesg.msg_namelen = fromlen;
  mesg.msg_control = &opi;
  mesg.msg_controllen = sizeof opi;
  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
#ifdef IP_PKTINFO
	  && cmsg->cmsg_level == SOL_IP 
	  && cmsg->cmsg_type == IP_PKTINFO
#elif defined(IP_RECVDSTADDR)
	  && cmsg->cmsg_level == IPPROTO_IP
	  && cmsg->cmsg_type == IP_RECVDSTADDR
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif
	  && cmsg->cmsg_len >= sizeof (struct openvpn_in4_pktinfo))
	{
#ifdef IP_PKTINFO
	  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;
#elif defined(IP_RECVDSTADDR)
	  from->pi.in4 = *(struct in_addr*) CMSG_DATA (cmsg);
#else
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif
	}
      else if (cmsg != NULL
	  && CMSG_NXTHDR (&mesg, cmsg) == NULL
	  && cmsg->cmsg_level == IPPROTO_IPV6 
	  && cmsg->cmsg_type == IPV6_PKTINFO
	  && cmsg->cmsg_len >= sizeof (struct openvpn_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;
}
#endif

int
link_socket_read_udp_posix (struct link_socket *sock,
			    struct buffer *buf,
			    int maxsize,
			    struct link_socket_actual *from)
{
  socklen_t fromlen = sizeof (from->dest.addr);
  socklen_t expectedlen = af_addr_size(sock->info.af);
  addr_zero_host(&from->dest);
  ASSERT (buf_safe (buf, maxsize));
#if ENABLE_IP_PKTINFO
  /* 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, maxsize, from);
  else
#endif
    buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 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;
}

#endif

/*
 * Socket Write Routines
 */

int
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)));
#ifdef WIN32
  return link_socket_write_win32 (sock, buf, to);
#else
  return link_socket_write_tcp_posix (sock, buf, to);  
#endif
}

#if ENABLE_IP_PKTINFO

size_t
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;
  union openvpn_pktinfo opi;

  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 = &opi;
        mesg.msg_flags = 0;
#ifdef HAVE_IN_PKTINFO
        mesg.msg_controllen = sizeof (struct openvpn_in4_pktinfo);
        cmsg = CMSG_FIRSTHDR (&mesg);
        cmsg->cmsg_len = sizeof (struct openvpn_in4_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;
	}
#elif defined(IP_RECVDSTADDR)
	ASSERT( CMSG_SPACE(sizeof (struct in_addr)) <= sizeof(opi) );
        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
#error ENABLE_IP_PKTINFO is set without IP_PKTINFO xor IP_RECVDSTADDR (fix syshead.h)
#endif
        break;
      }
    case AF_INET6:
      {
        struct in6_pktinfo *pkti6;
        mesg.msg_name = &to->dest.addr.sa;
        mesg.msg_namelen = sizeof (struct sockaddr_in6);
        mesg.msg_control = &opi;
        mesg.msg_controllen = sizeof (struct openvpn_in6_pktinfo);
        mesg.msg_flags = 0;
        cmsg = CMSG_FIRSTHDR (&mesg);
        cmsg->cmsg_len = sizeof (struct openvpn_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);
}

#endif

/*
 * Win32 overlapped socket I/O functions.
 */

#ifdef WIN32

int
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;
}

int
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;
}

int
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;
}

#endif /* WIN32 */

/*
 * Socket event notification
 */

unsigned int
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;
	}
      
#ifdef WIN32
      if (rwflags & EVENT_READ)
	socket_recv_queue (s, 0);
#endif

      /* 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;
	}

      s->rwflags_debug = rwflags;
    }
  return rwflags;
}

void
sd_close (socket_descriptor_t *sd)
{
  if (sd && socket_defined (*sd))
    {
      openvpn_close_socket (*sd);
      *sd = SOCKET_UNDEFINED;
    }
}

#if UNIX_SOCK_SUPPORT

/*
 * code for unix domain sockets
 */

const char *
sockaddr_unix_name (const struct sockaddr_un *local, const char *null)
{
  if (local && local->sun_family == PF_UNIX)
    return local->sun_path;
  else
    return null;
}

socket_descriptor_t
create_socket_unix (void)
{
  socket_descriptor_t sd;

  if ((sd = socket (PF_UNIX, SOCK_STREAM, 0)) < 0)
    msg (M_ERR, "Cannot create unix domain socket");
  return sd;
}

void
socket_bind_unix (socket_descriptor_t sd,
		  struct sockaddr_un *local,
		  const char *prefix)
{
  struct gc_arena gc = gc_new ();

#ifdef HAVE_UMASK
  const mode_t orig_umask = umask (0);
#endif

  if (bind (sd, (struct sockaddr *) local, sizeof (struct sockaddr_un)))
    {
      const int errnum = openvpn_errno ();
      msg (M_FATAL, "%s: Socket bind[%d] failed on unix domain socket %s: %s",
	   prefix,
	   (int)sd,
           sockaddr_unix_name (local, "NULL"),
           strerror_ts (errnum, &gc));
    }

#ifdef HAVE_UMASK
  umask (orig_umask);
#endif

  gc_free (&gc);
}

socket_descriptor_t
socket_accept_unix (socket_descriptor_t sd,
		    struct sockaddr_un *remote)
{
  socklen_t remote_len = sizeof (struct sockaddr_un);
  socket_descriptor_t ret;

  CLEAR (*remote);
  ret = accept (sd, (struct sockaddr *) remote, &remote_len);
  return ret;
}

int
socket_connect_unix (socket_descriptor_t sd,
		     struct sockaddr_un *remote)
{
  int status = connect (sd, (struct sockaddr *) remote, sizeof (struct sockaddr_un));
  if (status)
    status = openvpn_errno ();
  return status;
}

void
sockaddr_unix_init (struct sockaddr_un *local, const char *path)
{
  local->sun_family = PF_UNIX;
  strncpynt (local->sun_path, path, sizeof (local->sun_path));
}

void
socket_delete_unix (const struct sockaddr_un *local)
{
  const char *name = sockaddr_unix_name (local, NULL);
#ifdef HAVE_UNLINK
  if (name && strlen (name))
    unlink (name);
#endif
}

bool
unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int *gid)
{
#ifdef HAVE_GETPEEREID
  uid_t u;
  gid_t g;
  if (getpeereid (sd, &u, &g) == -1) 
    return false;
  if (uid)
    *uid = u;
  if (gid)
    *gid = g;
  return true;
#elif defined(SO_PEERCRED)
  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
  return false;
#endif
}

#endif