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); } }
static inline void 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); } }
static void proxy_entry_close_sd (struct proxy_connection *pc, struct event_set *es) { if (pc->defined && socket_defined (pc->sd)) { 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; } }
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; } } }
/* * Called from the main OpenVPN process to enable the port * share proxy. */ struct port_share * port_share_open (const char *host, const int port, const int max_initial_buf, const char *journal_dir) { pid_t pid; socket_descriptor_t fd[2]; in_addr_t hostaddr; struct port_share *ps; ALLOC_OBJ_CLEAR (ps, struct port_share); ps->foreground_fd = -1; ps->background_pid = -1; /* * 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) { /* 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); } } else { /* * Background Process */ /* Ignore most signals (the parent will receive them) */ set_signals (); /* Let msg know that we forked */ msg_forked (); #ifdef ENABLE_MANAGEMENT /* Don't interact with management interface */ management = NULL; #endif /* 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]); /* initialize prng */ prng_init (NULL, 0); /* execute the event loop */ port_share_proxy (hostaddr, port, fd[1], max_initial_buf, journal_dir); openvpn_close_socket (fd[1]); exit (0); return 0; /* NOTREACHED */ } error: port_share_close (ps); return NULL; }
/* * 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. */ 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, const int max_initial_buf, const char *journal_dir) { /* 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; *((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 ) { msg (M_WARN, "PORT SHARE PROXY: received unknown message"); } else { const socket_descriptor_t received_fd = *((socket_descriptor_t*)CMSG_DATA(h)); 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, server_port, 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; }
/* * 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. */ 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, const char *journal_dir) { 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); 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, &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; /* 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; }