ps.c
6add6b2f
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
d7fa38f2
  *  Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net>
6add6b2f
  *
  *  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
  */
 
 #include "syshead.h"
 
 #if PORT_SHARE
 
 #include "event.h"
 #include "socket.h"
 #include "fdmisc.h"
6ee8f6f8
 #include "crypto.h"
6add6b2f
 #include "ps.h"
 
 #include "memdbg.h"
 
 struct port_share *port_share = NULL; /* GLOBAL */
 
 /* size of i/o buffers */
 #define PROXY_CONNECTION_BUFFER_SIZE 1500
 
 /* Command codes for foreground -> background communication */
 #define COMMAND_REDIRECT 10
 #define COMMAND_EXIT     11
 
 /* Response codes for background -> foreground communication */
 #define RESPONSE_INIT_SUCCEEDED   20
 #define RESPONSE_INIT_FAILED      21
 
154adc7a
 /*
  * Return values for proxy_connection_io functions
  */
 
 #define IOSTAT_EAGAIN_ON_READ   0 /* recv returned EAGAIN */
 #define IOSTAT_EAGAIN_ON_WRITE  1 /* send returned EAGAIN */
 #define IOSTAT_READ_ERROR       2 /* the other end of our read socket (pc) was closed */
 #define IOSTAT_WRITE_ERROR      3 /* the other end of our write socket (pc->counterpart) was closed */
 #define IOSTAT_GOOD             4 /* nothing to report */
 
16eda097
 /*
  * A foreign (non-OpenVPN) connection we are proxying,
  * usually HTTPS
  */
6add6b2f
 struct proxy_connection {
   bool defined;
   struct proxy_connection *next;
   struct proxy_connection *counterpart;
   struct buffer buf;
   bool buffer_initial;
   int rwflags;
   int sd;
 };
 
 #if 0
 static const char *
 headc (const struct buffer *buf)
 {
   static char foo[16];
   strncpy (foo, BSTR(buf), 15);
   foo[15] = 0;
   return foo;
 }
 #endif
 
e92cee68
 static inline void
6add6b2f
 close_socket_if_defined (const socket_descriptor_t sd)
 {
   if (socket_defined (sd))
     openvpn_close_socket (sd);
 }
 
 /*
  * Close most of parent's fds.
  * Keep stdin/stdout/stderr, plus one
  * other fd which is presumed to be
  * our pipe back to parent.
  * Admittedly, a bit of a kludge,
  * but posix doesn't give us a kind
  * of FD_CLOEXEC which will stop
  * fds from crossing a fork().
  */
 static void
 close_fds_except (int keep)
 {
   socket_descriptor_t i;
   closelog ();
   for (i = 3; i <= 100; ++i)
     {
       if (i != keep)
 	openvpn_close_socket (i);
     }
 }
 
 /*
  * Usually we ignore signals, because our parent will
  * deal with them.
  */
 static void
 set_signals (void)
 {
   signal (SIGTERM, SIG_DFL);
 
   signal (SIGINT, SIG_IGN);
   signal (SIGHUP, SIG_IGN);
   signal (SIGUSR1, SIG_IGN);
   signal (SIGUSR2, SIG_IGN);
   signal (SIGPIPE, SIG_IGN);
 }
 
 /*
  * Socket read/write functions.
  */
 
 static int
 recv_control (const socket_descriptor_t fd)
 {
   unsigned char c;
   const ssize_t size = read (fd, &c, sizeof (c));
   if (size == sizeof (c))
     return c;
   else
     {
       return -1;
     }
 }
 
 static int
 send_control (const socket_descriptor_t fd, int code)
 {
   unsigned char c = (unsigned char) code;
   const ssize_t size = write (fd, &c, sizeof (c));
   if (size == sizeof (c))
     return (int) size;
   else
     return -1;
 }
 
d3ad4570
 static int
 cmsg_size ()
 {
   return CMSG_SPACE(sizeof(socket_descriptor_t));
 }
 
e92cee68
 /*
  * Send a command (char), data (head), and a file descriptor (sd_send) to a local process
  * over unix socket sd.  Unfortunately, there's no portable way to send file descriptors
  * to other processes, so this code, as well as its analog (control_message_from_parent below),
  * is Linux-specific. This function runs in the context of the main process and is used to
  * send commands, data, and file descriptors to the background process.
  */
6add6b2f
 static void
 port_share_sendmsg (const socket_descriptor_t sd,
 		    const char command,
 		    const struct buffer *head,
 		    const socket_descriptor_t sd_send)
 {
   if (socket_defined (sd))
     {
       struct msghdr mesg;
       struct cmsghdr* h;
       struct iovec iov[2];
       socket_descriptor_t sd_null[2] = { SOCKET_UNDEFINED, SOCKET_UNDEFINED };
       char cmd;
       ssize_t status;
 
       dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: sendmsg sd=%d len=%d",
16eda097
 	    (int)sd_send,
6add6b2f
 	    head ? BLEN(head) : -1);
 
       CLEAR (mesg);
 
       cmd = command;
 
       iov[0].iov_base = &cmd;
       iov[0].iov_len = sizeof (cmd);
       mesg.msg_iovlen = 1;
 
       if (head)
 	{
 	  iov[1].iov_base = BPTR (head);
 	  iov[1].iov_len = BLEN (head);
 	  mesg.msg_iovlen = 2;
 	}
 
       mesg.msg_iov = iov;
 
d3ad4570
       mesg.msg_controllen = cmsg_size ();
       mesg.msg_control = (char *) malloc (mesg.msg_controllen);
       check_malloc_return (mesg.msg_control);
6add6b2f
       mesg.msg_flags = 0;
 
       h = CMSG_FIRSTHDR(&mesg);
       h->cmsg_level = SOL_SOCKET;
       h->cmsg_type = SCM_RIGHTS;
       h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t));
 
       if (socket_defined (sd_send))
 	{
 	  *((socket_descriptor_t*)CMSG_DATA(h)) = sd_send;
 	}
       else
 	{
 	  socketpair (PF_UNIX, SOCK_DGRAM, 0, sd_null);
 	  *((socket_descriptor_t*)CMSG_DATA(h)) = sd_null[0];
 	}
 
       status = sendmsg (sd, &mesg, MSG_NOSIGNAL);
       if (status == -1)
 	msg (M_WARN, "PORT SHARE: sendmsg failed (unable to communicate with background process)");
 
       close_socket_if_defined (sd_null[0]);
       close_socket_if_defined (sd_null[1]);
d3ad4570
       free (mesg.msg_control);
6add6b2f
     }
 }
 
 static int
 pc_list_len (struct proxy_connection *pc)
 {
   int count = 0;
   while (pc)
     {
       ++count;
       pc = pc->next;
     }
   return count;
 }
 
93cb134d
 static void
 proxy_entry_close_sd (struct proxy_connection *pc, struct event_set *es)
 {
   if (pc->defined && socket_defined (pc->sd))
     {
16eda097
       dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: delete sd=%d", (int)pc->sd);
93cb134d
       if (es)
 	event_del (es, pc->sd);
       openvpn_close_socket (pc->sd);
       pc->sd = SOCKET_UNDEFINED;
     }
 }
 
e92cee68
 /*
  * Mark a proxy entry and its counterpart for close.
  */
6add6b2f
 static void
 proxy_entry_mark_for_close (struct proxy_connection *pc, struct event_set *es)
 {
   if (pc->defined)
     {
       struct proxy_connection *cp = pc->counterpart;
93cb134d
       proxy_entry_close_sd (pc, es);
6add6b2f
       free_buf (&pc->buf);
       pc->buffer_initial = false;
       pc->rwflags = 0;
       pc->defined = false;
       if (cp && cp->defined && cp->counterpart == pc)
 	proxy_entry_mark_for_close (cp, es);
     }
 }
 
e92cee68
 /*
  * Run through the proxy entry list and delete all entries marked
  * for close.
  */
6add6b2f
 static void
 proxy_list_housekeeping (struct proxy_connection **list)
 {
   if (list)
     {
       struct proxy_connection *prev = NULL;
       struct proxy_connection *pc = *list;
 
       while (pc)
 	{
 	  struct proxy_connection *next = pc->next;
 	  if (!pc->defined)
 	    {
 	      free (pc);
 	      if (prev)
 		prev->next = next;
 	      else
 		*list = next;
 	    }
 	  else
 	    prev = pc;
 	  pc = next;
 	}
     }
 }
 
e92cee68
 /*
  * Cleanup function, on proxy process exit.
  */
6add6b2f
 static void
 proxy_list_close (struct proxy_connection **list)
 {
   if (list)
     {
       struct proxy_connection *pc = *list;
       while (pc)
 	{
 	  proxy_entry_mark_for_close (pc, NULL);
 	  pc = pc->next;
 	}
       proxy_list_housekeeping (list);
     }
 }
 
 static void
 sock_addr_set (struct openvpn_sockaddr *osaddr,
 	       const in_addr_t addr,
 	       const int port)
 {
   CLEAR (*osaddr);
   osaddr->sa.sin_family = AF_INET;
   osaddr->sa.sin_addr.s_addr = htonl (addr);
   osaddr->sa.sin_port = htons (port);
 }
 
 static inline void
 proxy_connection_io_requeue (struct proxy_connection *pc, const int rwflags_new, struct event_set *es)
 {
93cb134d
   if (socket_defined (pc->sd) && pc->rwflags != rwflags_new)
6add6b2f
     {
16eda097
       /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: requeue[%d] rwflags=%d", (int)pc->sd, rwflags_new);*/
6add6b2f
       event_ctl (es, pc->sd, rwflags_new, (void*)pc);
       pc->rwflags = rwflags_new;
     }
 }
 
e92cee68
 /*
  * Create a new pair of proxy_connection entries, one for each
  * socket file descriptor involved in the proxy.  We are given
  * the client fd, and we should derive our own server fd by connecting
  * to the server given by server_addr/server_port.  Return true
  * on success and false on failure to connect to server.
  */
6add6b2f
 static bool
 proxy_entry_new (struct proxy_connection **list,
 		 struct event_set *es,
 		 const in_addr_t server_addr,
 		 const int server_port,
 		 const socket_descriptor_t sd_client,
 		 struct buffer *initial_data)
 {
   struct openvpn_sockaddr osaddr;
   socket_descriptor_t sd_server;
   int status;
   struct proxy_connection *pc;
   struct proxy_connection *cp;
 
   /* connect to port share server */
   sock_addr_set (&osaddr, server_addr, server_port);
   sd_server = create_socket_tcp ();
   status = openvpn_connect (sd_server, &osaddr, 5, NULL);
   if (status)
     {
       msg (M_WARN, "PORT SHARE PROXY: connect to port-share server failed");
       openvpn_close_socket (sd_server);
       return false;
     }
   dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: connect to port-share server succeeded");
 
   set_nonblock (sd_client);
   set_nonblock (sd_server);
 
   /* allocate 2 new proxy_connection objects */
   ALLOC_OBJ_CLEAR (pc, struct proxy_connection);
   ALLOC_OBJ_CLEAR (cp, struct proxy_connection);
 
   /* client object */
   pc->defined = true;
   pc->next = cp;
   pc->counterpart = cp;
   pc->buf = *initial_data;
   pc->buffer_initial = true;
   pc->rwflags = EVENT_UNDEF;
   pc->sd = sd_client;
 
   /* server object */
   cp->defined = true;
   cp->next = *list;
   cp->counterpart = pc;
   cp->buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
   cp->buffer_initial = false;
   cp->rwflags = EVENT_UNDEF;
   cp->sd = sd_server;
 
   /* add to list */
   *list = pc;
   
16eda097
   dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: NEW CONNECTION [c=%d s=%d]", (int)sd_client, (int)sd_server);
6add6b2f
 
   /* set initial i/o states */
   proxy_connection_io_requeue (pc, EVENT_READ, es);
   proxy_connection_io_requeue (cp, EVENT_READ|EVENT_WRITE, es);
   
   return true;
 }
 
e92cee68
 /*
  * This function runs in the context of the background proxy process.
  * Receive a control message from the parent (sent by the port_share_sendmsg
  * function above) and act on it.  Return false if the proxy process should
  * exit, true otherwise.
  */
6add6b2f
 static bool
 control_message_from_parent (const socket_descriptor_t sd_control,
 			     struct proxy_connection **list,
 			     struct event_set *es,
 			     const in_addr_t server_addr,
 			     const int server_port)
 {
   struct buffer buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
   struct msghdr mesg;
   struct cmsghdr* h;
   struct iovec iov[2];
   char command = 0;
   ssize_t status;
   int ret = true;
 
   CLEAR (mesg);
 
   iov[0].iov_base = &command;
   iov[0].iov_len = sizeof (command);
   iov[1].iov_base = BPTR (&buf);
   iov[1].iov_len = BCAP (&buf);
   mesg.msg_iov = iov;
   mesg.msg_iovlen = 2;
 
d3ad4570
   mesg.msg_controllen = cmsg_size ();
   mesg.msg_control = (char *) malloc (mesg.msg_controllen);
   check_malloc_return (mesg.msg_control);
6add6b2f
   mesg.msg_flags = 0;
 
   h = CMSG_FIRSTHDR(&mesg);
   h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t));
   h->cmsg_level = SOL_SOCKET;
   h->cmsg_type = SCM_RIGHTS;
   *((socket_descriptor_t*)CMSG_DATA(h)) = SOCKET_UNDEFINED;
 
   status = recvmsg (sd_control, &mesg, MSG_NOSIGNAL);
   if (status != -1)
     {
       if (   h == NULL
 	  || h->cmsg_len    != CMSG_LEN(sizeof(socket_descriptor_t))
 	  || h->cmsg_level  != SOL_SOCKET
 	  || h->cmsg_type   != SCM_RIGHTS )
 	{
 	  ret = false;
 	}
       else
 	{
 	  const socket_descriptor_t received_fd = *((socket_descriptor_t*)CMSG_DATA(h));
16eda097
 	  dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED sd=%d", (int)received_fd);
6add6b2f
 
 	  if (status >= 2 && command == COMMAND_REDIRECT)
 	    {
 	      buf.len = status - 1;
 	      if (proxy_entry_new (list,
 				   es,
 				   server_addr,
 				   server_port,
 				   received_fd,
 				   &buf))
 		{
 		  CLEAR (buf); /* we gave the buffer to proxy_entry_new */
 		}
 	      else
 		{
 		  openvpn_close_socket (received_fd);
 		}
 	    }
 	  else if (status >= 1 && command == COMMAND_EXIT)
 	    {
 	      dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED COMMAND_EXIT");
 	      openvpn_close_socket (received_fd); /* null socket */
 	      ret = false;
 	    }
 	}
     }
d3ad4570
   free (mesg.msg_control);
6add6b2f
   free_buf (&buf);
   return ret;
 }
 
93cb134d
 static int
 proxy_connection_io_recv (struct proxy_connection *pc)
 {
   /* recv data from socket */
154adc7a
   const int status = recv (pc->sd, BPTR(&pc->buf), BCAP(&pc->buf), MSG_NOSIGNAL);
   if (status < 0)
93cb134d
     {
       return (errno == EAGAIN) ? IOSTAT_EAGAIN_ON_READ : IOSTAT_READ_ERROR;
     }
   else
     {
       if (!status)
 	return IOSTAT_READ_ERROR;
       pc->buf.len = status;
     }
   return IOSTAT_GOOD;
 }
 
 static int
154adc7a
 proxy_connection_io_send (struct proxy_connection *pc, int *bytes_sent)
93cb134d
 {
16eda097
   const socket_descriptor_t sd = pc->counterpart->sd;
   const int status = send (sd, BPTR(&pc->buf), BLEN(&pc->buf), MSG_NOSIGNAL);
154adc7a
 
16eda097
   if (status < 0)
     {
       const int e = errno;
       return (e == EAGAIN) ? IOSTAT_EAGAIN_ON_WRITE : IOSTAT_WRITE_ERROR;
     }
   else
     {
       *bytes_sent += status;
       if (status != pc->buf.len)
93cb134d
 	{
16eda097
 	  dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: partial write[%d], tried=%d got=%d", (int)sd, pc->buf.len, status);
 	  buf_advance (&pc->buf, status);
 	  return IOSTAT_EAGAIN_ON_WRITE;
93cb134d
 	}
       else
 	{
16eda097
 	  /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status);*/
 	  pc->buf.len = 0;
 	  pc->buf.offset = 0;
93cb134d
 	}
16eda097
     }
93cb134d
 
16eda097
   /* realloc send buffer after initial send */
   if (pc->buffer_initial)
     {
       free_buf (&pc->buf);
       pc->buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
       pc->buffer_initial = false;
     }
   return IOSTAT_GOOD;
93cb134d
 }
6add6b2f
 
e92cee68
 /*
  * Forward data from pc to pc->counterpart.
  */
93cb134d
 
6add6b2f
 static int
154adc7a
 proxy_connection_io_xfer (struct proxy_connection *pc, const int max_transfer)
6add6b2f
 {
154adc7a
   int transferred = 0;
   while (transferred < max_transfer)
6add6b2f
     {
154adc7a
       if (!BLEN (&pc->buf))
6add6b2f
 	{
154adc7a
 	  const int status = proxy_connection_io_recv (pc);
 	  if (status != IOSTAT_GOOD)
 	    return status;
6add6b2f
 	}
154adc7a
 
6add6b2f
       if (BLEN (&pc->buf))
 	{
154adc7a
 	  const int status = proxy_connection_io_send (pc, &transferred);
 	  if (status != IOSTAT_GOOD)
 	    return status;
6add6b2f
 	}
93cb134d
     }
154adc7a
   return IOSTAT_EAGAIN_ON_READ;
6add6b2f
 }
 
e92cee68
 /*
  * Decide how the receipt of an EAGAIN status should affect our next IO queueing.
  */
154adc7a
 static bool
6add6b2f
 proxy_connection_io_status (const int status, int *rwflags_pc, int *rwflags_cp)
 {
   switch (status)
     {
93cb134d
     case IOSTAT_EAGAIN_ON_READ:
       *rwflags_pc |= EVENT_READ;
       *rwflags_cp &= ~EVENT_WRITE;
       return true;
     case IOSTAT_EAGAIN_ON_WRITE:
       *rwflags_pc &= ~EVENT_READ;
       *rwflags_cp |= EVENT_WRITE;
       return true;
     case IOSTAT_READ_ERROR:
154adc7a
       return false;
93cb134d
     case IOSTAT_WRITE_ERROR:
154adc7a
       return false;
93cb134d
     default:
154adc7a
       msg (M_FATAL, "PORT SHARE PROXY: unexpected status=%d", status);
6add6b2f
     }
154adc7a
   return false; /* NOTREACHED */
6add6b2f
 }
 
e92cee68
 /*
  * Dispatch function for forwarding data between the two socket fds involved
  * in the proxied connection.
  */
154adc7a
 static int
6add6b2f
 proxy_connection_io_dispatch (struct proxy_connection *pc,
 			      const int rwflags,
 			      struct event_set *es)
 {
154adc7a
   const int max_transfer_per_iteration = 10000;
6add6b2f
   struct proxy_connection *cp = pc->counterpart;
   int rwflags_pc = pc->rwflags;
   int rwflags_cp = cp->rwflags;
 
   if (rwflags & EVENT_READ)
     {
154adc7a
       const int status = proxy_connection_io_xfer (pc, max_transfer_per_iteration);
6add6b2f
       if (!proxy_connection_io_status (status, &rwflags_pc, &rwflags_cp))
154adc7a
 	goto bad;
6add6b2f
     }
   if (rwflags & EVENT_WRITE)
     {
154adc7a
       const int status = proxy_connection_io_xfer (cp, max_transfer_per_iteration);
6add6b2f
       if (!proxy_connection_io_status (status, &rwflags_cp, &rwflags_pc))
154adc7a
 	goto bad;
6add6b2f
     }
   proxy_connection_io_requeue (pc, rwflags_pc, es);
   proxy_connection_io_requeue (cp, rwflags_cp, es);
154adc7a
 
   return true;
 
  bad:
   proxy_entry_mark_for_close (pc, es);
   return false;
6add6b2f
 }
 
e92cee68
 /*
  * This is the main function for the port share proxy background process.
  */
6add6b2f
 static void
 port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descriptor_t sd_control)
 {
   if (send_control (sd_control, RESPONSE_INIT_SUCCEEDED) >= 0)
     {
       void *sd_control_marker = (void *)1;
       int maxevents = 256;
       struct event_set *es;
       struct event_set_return esr[64];
       struct proxy_connection *list = NULL;
       time_t last_housekeeping = 0;
 
       msg (D_PS_PROXY, "PORT SHARE PROXY: proxy starting");
 
       es = event_set_init (&maxevents, 0);
       event_ctl (es, sd_control, EVENT_READ, sd_control_marker);
       while (true)
 	{
 	  int n_events;
 	  struct timeval tv;
 	  time_t current;
 
 	  tv.tv_sec = 10;
 	  tv.tv_usec = 0;
 	  n_events = event_wait (es, &tv, esr, SIZE(esr));
154adc7a
 	  /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: event_wait returned %d", n_events);*/
6add6b2f
 	  current = time(NULL);
 	  if (n_events > 0)
 	    {
 	      int i;
 	      for (i = 0; i < n_events; ++i)
 		{
 		  const struct event_set_return *e = &esr[i];
 		  if (e->arg == sd_control_marker)
 		    {
 		      if (!control_message_from_parent (sd_control, &list, es, hostaddr, port))
 			goto done;
 		    }
 		  else
 		    {
 		      struct proxy_connection *pc = (struct proxy_connection *)e->arg;
 		      if (pc->defined)
 			proxy_connection_io_dispatch (pc, e->rwflags, es);
 		    }
 		}
 	    }
 	  else if (n_events < 0)
 	    {
 	      dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: event_wait failed");
 	    }
 	  if (current > last_housekeeping)
 	    {
 	      proxy_list_housekeeping (&list);
 	      last_housekeeping = current;
 	    }
 	}
 
     done:
       proxy_list_close (&list);
       event_free (es);
     }
   msg (D_PS_PROXY, "PORT SHARE PROXY: proxy exiting");
 }
 
e92cee68
 /*
  * Called from the main OpenVPN process to enable the port
  * share proxy.
  */
6add6b2f
 struct port_share *
 port_share_open (const char *host, const int port)
 {
   pid_t pid;
   socket_descriptor_t fd[2];
   in_addr_t hostaddr;
   struct port_share *ps;
 
   ALLOC_OBJ_CLEAR (ps, struct port_share);
 
   /*
    * Get host's IP address
    */
   hostaddr = getaddr (GETADDR_RESOLVE|GETADDR_HOST_ORDER|GETADDR_FATAL, host, 0, NULL, NULL);
 
   /*
    * Make a socket for foreground and background processes
    * to communicate.
    */
   if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
     {
       msg (M_WARN, "PORT SHARE: socketpair call failed");
       goto error;
     }
 
   /*
    * Fork off background proxy process.
    */
   pid = fork ();
 
   if (pid)
     {
       int status;
 
       /*
        * Foreground Process
        */
 
       ps->background_pid = pid;
 
       /* close our copy of child's socket */
       openvpn_close_socket (fd[1]);
 
       /* don't let future subprocesses inherit child socket */
       set_cloexec (fd[0]);
 
       /* wait for background child process to initialize */
       status = recv_control (fd[0]);
       if (status == RESPONSE_INIT_SUCCEEDED)
 	{
 	  ps->foreground_fd = fd[0];
 	  return ps;
 	}
     }
   else
     {
       /*
        * Background Process
        */
 
       /* Ignore most signals (the parent will receive them) */
       set_signals ();
 
       /* Let msg know that we forked */
       msg_forked ();
 
       /* close all parent fds except our socket back to parent */
       close_fds_except (fd[1]);
 
       /* no blocking on control channel back to parent */
       set_nonblock (fd[1]);
 
6ee8f6f8
       /* initialize prng */
03bfb228
       prng_init (NULL, 0);
6ee8f6f8
 
6add6b2f
       /* execute the event loop */
       port_share_proxy (hostaddr, port, fd[1]);
 
       openvpn_close_socket (fd[1]);
 
       exit (0);
       return 0; /* NOTREACHED */
     }
 
  error:
   port_share_close (ps);
   return NULL;
 }
 
 void
 port_share_close (struct port_share *ps)
 {
   if (ps)
     {
       if (ps->foreground_fd >= 0)
 	{
 	  /* tell background process to exit */
 	  port_share_sendmsg (ps->foreground_fd, COMMAND_EXIT, NULL, SOCKET_UNDEFINED);
 
 	  /* wait for background process to exit */
 	  dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: waiting for background process to exit");
 	  if (ps->background_pid > 0)
 	    waitpid (ps->background_pid, NULL, 0);
 	  dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: background process exited");
 
 	  openvpn_close_socket (ps->foreground_fd);
 	  ps->foreground_fd = -1;
 	}
 
       free (ps);
     }
 }
 
 void
 port_share_abort (struct port_share *ps)
 {
   if (ps)
     {
       /* tell background process to exit */
       if (ps->foreground_fd >= 0)
 	{
 	  send_control (ps->foreground_fd, COMMAND_EXIT);
 	  openvpn_close_socket (ps->foreground_fd);
 	  ps->foreground_fd = -1;
 	}
     }
 }
 
e92cee68
 /*
  * Given either the first 2 or 3 bytes of an initial client -> server
  * data payload, return true if the protocol is that of an OpenVPN
  * client attempting to connect with an OpenVPN server.
  */
6add6b2f
 bool
 is_openvpn_protocol (const struct buffer *buf)
 {
28549038
   const unsigned char *p = (const unsigned char *) BSTR (buf);
6add6b2f
   const int len = BLEN (buf);
   if (len >= 3)
     {
       return p[0] == 0
 	&& p[1] >= 14
 	&& p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
     }
   else if (len >= 2)
     {
       return p[0] == 0 && p[1] >= 14;
     }
   else
     return true;
 }
 
e92cee68
 /*
  * Called from the foreground process.  Send a message to the background process that it
  * should proxy the TCP client on sd to the host/port defined in the initial port_share_open
  * call.
  */
6add6b2f
 void
 port_share_redirect (struct port_share *ps, const struct buffer *head, socket_descriptor_t sd)
 {
   if (ps)
     port_share_sendmsg (ps->foreground_fd, COMMAND_REDIRECT, head, sd);
 }
 
 #endif