src/openvpn/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.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN 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.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6add6b2f
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6add6b2f
 #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 {
81d882d5
     bool defined;
     struct proxy_connection *next;
     struct proxy_connection *counterpart;
     struct buffer buf;
     bool buffer_initial;
     int rwflags;
     int sd;
     char *jfn;
6add6b2f
 };
 
 #if 0
 static const char *
81d882d5
 headc(const struct buffer *buf)
6add6b2f
 {
81d882d5
     static char foo[16];
     strncpy(foo, BSTR(buf), 15);
     foo[15] = 0;
     return foo;
6add6b2f
 }
 #endif
 
e92cee68
 static inline void
81d882d5
 close_socket_if_defined(const socket_descriptor_t sd)
6add6b2f
 {
81d882d5
     if (socket_defined(sd))
     {
         openvpn_close_socket(sd);
     }
6add6b2f
 }
 
 /*
  * 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
81d882d5
 close_fds_except(int keep)
6add6b2f
 {
81d882d5
     socket_descriptor_t i;
     closelog();
     for (i = 3; i <= 100; ++i)
6add6b2f
     {
81d882d5
         if (i != keep)
         {
             openvpn_close_socket(i);
         }
6add6b2f
     }
 }
 
 /*
  * Usually we ignore signals, because our parent will
  * deal with them.
  */
 static void
81d882d5
 set_signals(void)
6add6b2f
 {
81d882d5
     signal(SIGTERM, SIG_DFL);
6add6b2f
 
81d882d5
     signal(SIGINT, SIG_IGN);
     signal(SIGHUP, SIG_IGN);
     signal(SIGUSR1, SIG_IGN);
     signal(SIGUSR2, SIG_IGN);
     signal(SIGPIPE, SIG_IGN);
6add6b2f
 }
 
 /*
  * Socket read/write functions.
  */
 
 static int
81d882d5
 recv_control(const socket_descriptor_t fd)
6add6b2f
 {
81d882d5
     unsigned char c;
     const ssize_t size = read(fd, &c, sizeof(c));
     if (size == sizeof(c))
     {
         return c;
     }
     else
6add6b2f
     {
81d882d5
         return -1;
6add6b2f
     }
 }
 
 static int
81d882d5
 send_control(const socket_descriptor_t fd, int code)
6add6b2f
 {
81d882d5
     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;
     }
6add6b2f
 }
 
d3ad4570
 static int
e2a0cad4
 cmsg_size(void)
d3ad4570
 {
81d882d5
     return CMSG_SPACE(sizeof(socket_descriptor_t));
d3ad4570
 }
 
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
81d882d5
 port_share_sendmsg(const socket_descriptor_t sd,
                    const char command,
                    const struct buffer *head,
                    const socket_descriptor_t sd_send)
6add6b2f
 {
81d882d5
     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",
              (int)sd_send,
              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;
 
         mesg.msg_controllen = cmsg_size();
         mesg.msg_control = (char *) malloc(mesg.msg_controllen);
         check_malloc_return(mesg.msg_control);
         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))
         {
             memcpy(CMSG_DATA(h), &sd_send, sizeof(sd_send));
         }
         else
         {
             socketpair(PF_UNIX, SOCK_DGRAM, 0, sd_null);
             memcpy(CMSG_DATA(h), &sd_null[0], sizeof(sd_null[0]));
         }
 
         status = sendmsg(sd, &mesg, MSG_NOSIGNAL);
         if (status == -1)
         {
             msg(M_WARN|M_ERRNO, "PORT SHARE: sendmsg failed -- unable to communicate with background process (%d,%d,%d,%d)",
                 sd, sd_send, sd_null[0], sd_null[1]
                 );
         }
 
         close_socket_if_defined(sd_null[0]);
         close_socket_if_defined(sd_null[1]);
         free(mesg.msg_control);
6add6b2f
     }
 }
 
93cb134d
 static void
81d882d5
 proxy_entry_close_sd(struct proxy_connection *pc, struct event_set *es)
93cb134d
 {
81d882d5
     if (pc->defined && socket_defined(pc->sd))
93cb134d
     {
81d882d5
         dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: delete sd=%d", (int)pc->sd);
         if (es)
         {
             event_del(es, pc->sd);
         }
         openvpn_close_socket(pc->sd);
         pc->sd = SOCKET_UNDEFINED;
93cb134d
     }
 }
 
e92cee68
 /*
  * Mark a proxy entry and its counterpart for close.
  */
6add6b2f
 static void
81d882d5
 proxy_entry_mark_for_close(struct proxy_connection *pc, struct event_set *es)
6add6b2f
 {
81d882d5
     if (pc->defined)
     {
         struct proxy_connection *cp = pc->counterpart;
         proxy_entry_close_sd(pc, es);
         free_buf(&pc->buf);
         pc->buffer_initial = false;
         pc->rwflags = 0;
         pc->defined = false;
         if (pc->jfn)
         {
             unlink(pc->jfn);
             free(pc->jfn);
             pc->jfn = NULL;
         }
         if (cp && cp->defined && cp->counterpart == pc)
         {
             proxy_entry_mark_for_close(cp, es);
         }
6add6b2f
     }
 }
 
e92cee68
 /*
  * Run through the proxy entry list and delete all entries marked
  * for close.
  */
6add6b2f
 static void
81d882d5
 proxy_list_housekeeping(struct proxy_connection **list)
6add6b2f
 {
81d882d5
     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;
         }
6add6b2f
     }
 }
 
e92cee68
 /*
1c5ff772
  * Record IP/port of client in filesystem, so that server receiving
  * the proxy can determine true client origin.
  */
 static void
81d882d5
 journal_add(const char *journal_dir, struct proxy_connection *pc, struct proxy_connection *cp)
1c5ff772
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct openvpn_sockaddr from, to;
     socklen_t slen, dlen;
     int fnlen;
     char *jfn;
     int fd;
 
     slen = sizeof(from.addr.sa);
     dlen = sizeof(to.addr.sa);
     if (!getpeername(pc->sd, (struct sockaddr *) &from.addr.sa, &slen)
         && !getsockname(cp->sd, (struct sockaddr *) &to.addr.sa, &dlen))
     {
         const char *f = print_openvpn_sockaddr(&from, &gc);
         const char *t = print_openvpn_sockaddr(&to, &gc);
         fnlen =  strlen(journal_dir) + strlen(t) + 2;
         jfn = (char *) malloc(fnlen);
         check_malloc_return(jfn);
         openvpn_snprintf(jfn, fnlen, "%s/%s", journal_dir, t);
         dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: client origin %s -> %s", jfn, f);
         fd = platform_open(jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP);
         if (fd != -1)
         {
             if (write(fd, f, strlen(f)) != strlen(f))
             {
                 msg(M_WARN, "PORT SHARE: writing to journal file (%s) failed", jfn);
             }
             close(fd);
             cp->jfn = jfn;
         }
         else
         {
             msg(M_WARN|M_ERRNO, "PORT SHARE: unable to write journal file in %s", jfn);
             free(jfn);
         }
     }
     gc_free(&gc);
1c5ff772
 }
 
 /*
e92cee68
  * Cleanup function, on proxy process exit.
  */
6add6b2f
 static void
81d882d5
 proxy_list_close(struct proxy_connection **list)
6add6b2f
 {
81d882d5
     if (list)
6add6b2f
     {
81d882d5
         struct proxy_connection *pc = *list;
         while (pc)
         {
             proxy_entry_mark_for_close(pc, NULL);
             pc = pc->next;
         }
         proxy_list_housekeeping(list);
6add6b2f
     }
 }
 
 static inline void
81d882d5
 proxy_connection_io_requeue(struct proxy_connection *pc, const int rwflags_new, struct event_set *es)
6add6b2f
 {
81d882d5
     if (socket_defined(pc->sd) && pc->rwflags != rwflags_new)
6add6b2f
     {
81d882d5
         /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: requeue[%d] rwflags=%d", (int)pc->sd, rwflags_new);*/
         event_ctl(es, pc->sd, rwflags_new, (void *)pc);
         pc->rwflags = rwflags_new;
6add6b2f
     }
 }
 
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
81d882d5
 proxy_entry_new(struct proxy_connection **list,
                 struct event_set *es,
                 const struct sockaddr_in server_addr,
                 const socket_descriptor_t sd_client,
                 struct buffer *initial_data,
                 const char *journal_dir)
6add6b2f
 {
81d882d5
     socket_descriptor_t sd_server;
     int status;
     struct proxy_connection *pc;
     struct proxy_connection *cp;
 
     /* connect to port share server */
     if ((sd_server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
     {
         msg(M_WARN|M_ERRNO, "PORT SHARE PROXY: cannot create socket");
         return false;
     }
     status = openvpn_connect(sd_server,(const struct sockaddr *)  &server_addr, 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;
 
     /* add journal entry */
     if (journal_dir)
     {
         journal_add(journal_dir, pc, cp);
     }
 
     dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: NEW CONNECTION [c=%d s=%d]", (int)sd_client, (int)sd_server);
 
     /* 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;
6add6b2f
 }
 
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
81d882d5
 control_message_from_parent(const socket_descriptor_t sd_control,
                             struct proxy_connection **list,
                             struct event_set *es,
                             const struct sockaddr_in server_addr,
                             const int max_initial_buf,
                             const char *journal_dir)
6add6b2f
 {
81d882d5
     /* this buffer needs to be large enough to handle the largest buffer
      * that might be returned by the link_socket_read call in read_incoming_link. */
     struct buffer buf = alloc_buf(max_initial_buf);
 
     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;
 
     mesg.msg_controllen = cmsg_size();
     mesg.msg_control = (char *) malloc(mesg.msg_controllen);
     check_malloc_return(mesg.msg_control);
     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;
     static const socket_descriptor_t socket_undefined = SOCKET_UNDEFINED;
     memcpy(CMSG_DATA(h), &socket_undefined, sizeof(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)
         {
             msg(M_WARN, "PORT SHARE PROXY: received unknown message");
         }
         else
         {
             socket_descriptor_t received_fd;
             memcpy(&received_fd, CMSG_DATA(h), sizeof(received_fd));
             dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED sd=%d", (int)received_fd);
 
             if (status >= 2 && command == COMMAND_REDIRECT)
             {
                 buf.len = status - 1;
                 if (proxy_entry_new(list,
                                     es,
                                     server_addr,
                                     received_fd,
                                     &buf,
                                     journal_dir))
                 {
                     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;
             }
         }
     }
     free(mesg.msg_control);
     free_buf(&buf);
     return ret;
6add6b2f
 }
 
93cb134d
 static int
81d882d5
 proxy_connection_io_recv(struct proxy_connection *pc)
93cb134d
 {
81d882d5
     /* recv data from socket */
     const int status = recv(pc->sd, BPTR(&pc->buf), BCAP(&pc->buf), MSG_NOSIGNAL);
     if (status < 0)
93cb134d
     {
81d882d5
         return (errno == EAGAIN) ? IOSTAT_EAGAIN_ON_READ : IOSTAT_READ_ERROR;
93cb134d
     }
81d882d5
     else
93cb134d
     {
81d882d5
         if (!status)
         {
             return IOSTAT_READ_ERROR;
         }
         dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: read[%d] %d", (int)pc->sd, status);
         pc->buf.len = status;
93cb134d
     }
81d882d5
     return IOSTAT_GOOD;
93cb134d
 }
 
 static int
81d882d5
 proxy_connection_io_send(struct proxy_connection *pc, int *bytes_sent)
93cb134d
 {
81d882d5
     const socket_descriptor_t sd = pc->counterpart->sd;
     const int status = send(sd, BPTR(&pc->buf), BLEN(&pc->buf), MSG_NOSIGNAL);
154adc7a
 
81d882d5
     if (status < 0)
16eda097
     {
81d882d5
         const int e = errno;
         return (e == EAGAIN) ? IOSTAT_EAGAIN_ON_WRITE : IOSTAT_WRITE_ERROR;
16eda097
     }
81d882d5
     else
16eda097
     {
81d882d5
         *bytes_sent += status;
         if (status != pc->buf.len)
         {
             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;
         }
         else
         {
             dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status);
             pc->buf.len = 0;
             pc->buf.offset = 0;
         }
16eda097
     }
93cb134d
 
81d882d5
     /* realloc send buffer after initial send */
     if (pc->buffer_initial)
16eda097
     {
81d882d5
         free_buf(&pc->buf);
         pc->buf = alloc_buf(PROXY_CONNECTION_BUFFER_SIZE);
         pc->buffer_initial = false;
16eda097
     }
81d882d5
     return IOSTAT_GOOD;
93cb134d
 }
6add6b2f
 
e92cee68
 /*
  * Forward data from pc to pc->counterpart.
  */
93cb134d
 
6add6b2f
 static int
81d882d5
 proxy_connection_io_xfer(struct proxy_connection *pc, const int max_transfer)
6add6b2f
 {
81d882d5
     int transferred = 0;
     while (transferred < max_transfer)
     {
         if (!BLEN(&pc->buf))
         {
             const int status = proxy_connection_io_recv(pc);
             if (status != IOSTAT_GOOD)
             {
                 return status;
             }
         }
 
         if (BLEN(&pc->buf))
         {
             const int status = proxy_connection_io_send(pc, &transferred);
             if (status != IOSTAT_GOOD)
             {
                 return status;
             }
         }
     }
     return IOSTAT_EAGAIN_ON_READ;
6add6b2f
 }
 
e92cee68
 /*
  * Decide how the receipt of an EAGAIN status should affect our next IO queueing.
  */
154adc7a
 static bool
81d882d5
 proxy_connection_io_status(const int status, int *rwflags_pc, int *rwflags_cp)
6add6b2f
 {
81d882d5
     switch (status)
     {
         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:
             return false;
 
         case IOSTAT_WRITE_ERROR:
             return false;
 
         default:
             msg(M_FATAL, "PORT SHARE PROXY: unexpected status=%d", status);
     }
     return false; /* NOTREACHED */
6add6b2f
 }
 
e92cee68
 /*
  * Dispatch function for forwarding data between the two socket fds involved
  * in the proxied connection.
  */
154adc7a
 static int
81d882d5
 proxy_connection_io_dispatch(struct proxy_connection *pc,
                              const int rwflags,
                              struct event_set *es)
6add6b2f
 {
81d882d5
     const int max_transfer_per_iteration = 10000;
     struct proxy_connection *cp = pc->counterpart;
     int rwflags_pc = pc->rwflags;
     int rwflags_cp = cp->rwflags;
6add6b2f
 
81d882d5
     ASSERT(pc->defined && cp->defined && cp->counterpart == pc);
1c5ff772
 
81d882d5
     if (rwflags & EVENT_READ)
6add6b2f
     {
81d882d5
         const int status = proxy_connection_io_xfer(pc, max_transfer_per_iteration);
         if (!proxy_connection_io_status(status, &rwflags_pc, &rwflags_cp))
         {
             goto bad;
         }
6add6b2f
     }
81d882d5
     if (rwflags & EVENT_WRITE)
6add6b2f
     {
81d882d5
         const int status = proxy_connection_io_xfer(cp, max_transfer_per_iteration);
         if (!proxy_connection_io_status(status, &rwflags_cp, &rwflags_pc))
         {
             goto bad;
         }
6add6b2f
     }
81d882d5
     proxy_connection_io_requeue(pc, rwflags_pc, es);
     proxy_connection_io_requeue(cp, rwflags_cp, es);
154adc7a
 
81d882d5
     return true;
154adc7a
 
81d882d5
 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
81d882d5
 port_share_proxy(const struct sockaddr_in hostaddr,
                  const socket_descriptor_t sd_control,
                  const int max_initial_buf,
                  const char *journal_dir)
6add6b2f
 {
81d882d5
     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));
             /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: event_wait returned %d", n_events);*/
             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, max_initial_buf, journal_dir))
                         {
                             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(M_INFO, "PORT SHARE PROXY: proxy exiting");
6add6b2f
 }
 
e92cee68
 /*
  * Called from the main OpenVPN process to enable the port
  * share proxy.
  */
6add6b2f
 struct port_share *
81d882d5
 port_share_open(const char *host,
                 const char *port,
                 const int max_initial_buf,
                 const char *journal_dir)
6add6b2f
 {
81d882d5
     pid_t pid;
     socket_descriptor_t fd[2];
     struct sockaddr_in hostaddr;
     struct port_share *ps;
     int status;
     struct addrinfo *ai;
 
     ALLOC_OBJ_CLEAR(ps, struct port_share);
     ps->foreground_fd = -1;
     ps->background_pid = -1;
 
     /*
      * Get host's IP address
      */
 
     status = openvpn_getaddrinfo(GETADDR_RESOLVE|GETADDR_FATAL,
076fd3e4
                                  host, port,  0, NULL, AF_INET, &ai);
81d882d5
     ASSERT(status==0);
     hostaddr = *((struct sockaddr_in *) ai->ai_addr);
     freeaddrinfo(ai);
 
     /*
      * Make a socket for foreground and background processes
      * to communicate.
      */
     if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
6add6b2f
     {
81d882d5
         msg(M_WARN, "PORT SHARE: socketpair call failed");
         goto error;
6add6b2f
     }
 
81d882d5
     /*
      * Fork off background proxy process.
      */
     pid = fork();
6add6b2f
 
81d882d5
     if (pid)
6add6b2f
     {
81d882d5
         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)
         {
             /* note that this will cause possible EAGAIN when writing to
              * control socket if proxy process is backlogged */
             set_nonblock(fd[0]);
 
             ps->foreground_fd = fd[0];
             return ps;
         }
         else
         {
             msg(M_ERR, "PORT SHARE: unexpected init recv_control status=%d", status);
         }
6add6b2f
     }
81d882d5
     else
6add6b2f
     {
81d882d5
         /*
          * Background Process
          */
6add6b2f
 
81d882d5
         /* Ignore most signals (the parent will receive them) */
         set_signals();
6add6b2f
 
81d882d5
         /* Let msg know that we forked */
         msg_forked();
6add6b2f
 
ca0ed845
 #ifdef ENABLE_MANAGEMENT
81d882d5
         /* Don't interact with management interface */
         management = NULL;
ca0ed845
 #endif
9ed122ef
 
81d882d5
         /* close all parent fds except our socket back to parent */
         close_fds_except(fd[1]);
6add6b2f
 
81d882d5
         /* no blocking on control channel back to parent */
         set_nonblock(fd[1]);
6add6b2f
 
81d882d5
         /* initialize prng */
         prng_init(NULL, 0);
6ee8f6f8
 
81d882d5
         /* execute the event loop */
         port_share_proxy(hostaddr, fd[1], max_initial_buf, journal_dir);
6add6b2f
 
81d882d5
         openvpn_close_socket(fd[1]);
6add6b2f
 
81d882d5
         exit(0);
280150a0
         return NULL; /* NOTREACHED */
6add6b2f
     }
 
81d882d5
 error:
     port_share_close(ps);
     return NULL;
6add6b2f
 }
 
 void
81d882d5
 port_share_close(struct port_share *ps)
6add6b2f
 {
81d882d5
     if (ps)
6add6b2f
     {
81d882d5
         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);
6add6b2f
     }
 }
 
 void
81d882d5
 port_share_abort(struct port_share *ps)
6add6b2f
 {
81d882d5
     if (ps)
6add6b2f
     {
81d882d5
         /* 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;
         }
6add6b2f
     }
 }
 
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
81d882d5
 is_openvpn_protocol(const struct buffer *buf)
6add6b2f
 {
81d882d5
     const unsigned char *p = (const unsigned char *) BSTR(buf);
     const int len = BLEN(buf);
     if (len >= 3)
6add6b2f
     {
81d882d5
         return p[0] == 0
                && p[1] >= 14
                && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
6add6b2f
     }
81d882d5
     else if (len >= 2)
6add6b2f
     {
81d882d5
         return p[0] == 0 && p[1] >= 14;
     }
     else
     {
         return true;
6add6b2f
     }
 }
 
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
81d882d5
 port_share_redirect(struct port_share *ps, const struct buffer *head, socket_descriptor_t sd)
6add6b2f
 {
81d882d5
     if (ps)
3530e5fb
     {
81d882d5
         port_share_sendmsg(ps->foreground_fd, COMMAND_REDIRECT, head, sd);
3530e5fb
     }
6add6b2f
 }
 
81d882d5
 #endif /* if PORT_SHARE */