diff options
-rw-r--r-- | error.c | 29 | ||||
-rw-r--r-- | event.c | 4 | ||||
-rw-r--r-- | init.c | 4 | ||||
-rw-r--r-- | openvpn.8 | 12 | ||||
-rw-r--r-- | options.c | 6 | ||||
-rw-r--r-- | options.h | 1 | ||||
-rw-r--r-- | ps.c | 104 | ||||
-rw-r--r-- | ps.h | 4 |
8 files changed, 131 insertions, 33 deletions
@@ -353,7 +353,7 @@ void x_msg (const unsigned int flags, const char *format, ...) } if (flags & M_FATAL) - msg (M_INFO, "Exiting"); + msg (M_INFO, "Exiting due to fatal error"); mutex_unlock_static (L_MSG); @@ -690,35 +690,38 @@ msg_thread_uninit (void) void openvpn_exit (const int status) { - void tun_abort(); + if (!forked) + { + void tun_abort(); #ifdef ENABLE_PLUGIN - void plugin_abort (void); + void plugin_abort (void); #endif - tun_abort(); + tun_abort(); #ifdef WIN32 - uninit_win32 (); + uninit_win32 (); #endif - close_syslog (); + close_syslog (); #ifdef ENABLE_PLUGIN - plugin_abort (); + plugin_abort (); #endif #if PORT_SHARE - if (port_share) - port_share_abort (port_share); + if (port_share) + port_share_abort (port_share); #endif #ifdef ABORT_ON_ERROR - if (status == OPENVPN_EXIT_STATUS_ERROR) - abort (); + if (status == OPENVPN_EXIT_STATUS_ERROR) + abort (); #endif - if (status == OPENVPN_EXIT_STATUS_GOOD) - perf_output_results (); + if (status == OPENVPN_EXIT_STATUS_GOOD) + perf_output_results (); + } exit (status); } @@ -522,10 +522,10 @@ ep_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg) if (errno == ENOENT) { if (epoll_ctl (eps->epfd, EPOLL_CTL_ADD, event, &ev) < 0) - msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed"); + msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed, sd=%d", (int)event); } else - msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed"); + msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed, sd=%d", (int)event); } } @@ -520,7 +520,9 @@ init_port_share (struct context *c) if (!port_share && (c->options.port_share_host && c->options.port_share_port)) { port_share = port_share_open (c->options.port_share_host, - c->options.port_share_port); + c->options.port_share_port, + MAX_RW_SIZE_LINK (&c->c2.frame), + c->options.port_share_journal_dir); if (port_share == NULL) msg (M_FATAL, "Fatal error: Port sharing failed"); } @@ -3240,7 +3240,7 @@ disable the remapping feature. Don't use this option unless you know what you are doing! .\"********************************************************* .TP -.B --port-share host port +.B --port-share host port [dir] When run in TCP server mode, share the OpenVPN port with another application, such as an HTTPS server. If OpenVPN senses a connection to its port which is using a non-OpenVPN @@ -3250,6 +3250,16 @@ Currently only designed to work with HTTP/HTTPS, though it would be theoretically possible to extend to other protocols such as ssh. +.B dir +specifies an optional directory where a temporary file with name N +containing content C will be dynamically generated for each proxy +connection, where N is the source IP:port of the client connection +and C is the source IP:port of the connection to the proxy +receiver. This directory can be used as a dictionary by +the proxy receiver to determine the origin of the connection. +Each generated file will be automatically deleted when the proxied +connection is torn down. + Not implemented on Windows. .\"********************************************************* .SS Client Mode @@ -427,8 +427,9 @@ static const char usage_message[] = "--max-clients n : Allow a maximum of n simultaneously connected clients.\n" "--max-routes-per-client n : Allow a maximum of n internal routes per client.\n" #if PORT_SHARE - "--port-share host port : When run in TCP mode, proxy incoming HTTPS sessions\n" - " to a web server at host:port.\n" + "--port-share host port [dir] : When run in TCP mode, proxy incoming HTTPS\n" + " sessions to a web server at host:port. dir specifies an\n" + " optional directory to write origin IP:port data.\n" #endif #endif "\n" @@ -5101,6 +5102,7 @@ add_option (struct options *options, options->port_share_host = p[1]; options->port_share_port = port; + options->port_share_journal_dir = p[3]; } #endif else if (streq (p[0], "client-to-client")) @@ -430,6 +430,7 @@ struct options #if PORT_SHARE char *port_share_host; int port_share_port; + const char *port_share_journal_dir; #endif #endif @@ -69,6 +69,7 @@ struct proxy_connection { bool buffer_initial; int rwflags; int sd; + char *jfn; }; #if 0 @@ -226,7 +227,7 @@ port_share_sendmsg (const socket_descriptor_t sd, status = sendmsg (sd, &mesg, MSG_NOSIGNAL); if (status == -1) - msg (M_WARN, "PORT SHARE: sendmsg failed (unable to communicate with background process)"); + msg (M_WARN|M_ERRNO_SOCK, "PORT SHARE: sendmsg failed (unable to communicate with background process)"); close_socket_if_defined (sd_null[0]); close_socket_if_defined (sd_null[1]); @@ -273,6 +274,12 @@ proxy_entry_mark_for_close (struct proxy_connection *pc, struct event_set *es) 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); } @@ -309,6 +316,48 @@ proxy_list_housekeeping (struct proxy_connection **list) } /* + * Record IP/port of client in filesystem, so that server receiving + * the proxy can determine true client origin. + */ +static void +journal_add (const char *journal_dir, struct proxy_connection *pc, struct proxy_connection *cp) +{ + struct gc_arena gc = gc_new (); + struct openvpn_sockaddr from, to; + socklen_t slen, dlen; + int fnlen; + char *jfn; + int fd; + + slen = sizeof(from.sa); + dlen = sizeof(to.sa); + if (!getpeername (pc->sd, (struct sockaddr *) &from.sa, &slen) + && !getsockname (cp->sd, (struct sockaddr *) &to.sa, &dlen)) + { + const char *f = print_sockaddr_ex (&from, ":", PS_SHOW_PORT, &gc); + const char *t = print_sockaddr_ex (&to, ":", PS_SHOW_PORT, &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 = open (jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); + if (fd != -1) + { + write(fd, f, strlen(f)); + 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); +} + +/* * Cleanup function, on proxy process exit. */ static void @@ -361,7 +410,8 @@ proxy_entry_new (struct proxy_connection **list, const in_addr_t server_addr, const int server_port, const socket_descriptor_t sd_client, - struct buffer *initial_data) + struct buffer *initial_data, + const char *journal_dir) { struct openvpn_sockaddr osaddr; socket_descriptor_t sd_server; @@ -371,7 +421,11 @@ proxy_entry_new (struct proxy_connection **list, /* connect to port share server */ sock_addr_set (&osaddr, server_addr, server_port); - sd_server = create_socket_tcp (); + if ((sd_server = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + msg (M_WARN|M_ERRNO_SOCK, "PORT SHARE PROXY: cannot create socket"); + return false; + } status = openvpn_connect (sd_server, &osaddr, 5, NULL); if (status) { @@ -408,6 +462,10 @@ proxy_entry_new (struct proxy_connection **list, /* 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); @@ -429,9 +487,14 @@ 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 server_port, + const int max_initial_buf, + const char *journal_dir) { - struct buffer buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE); + /* 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]; @@ -467,7 +530,7 @@ control_message_from_parent (const socket_descriptor_t sd_control, || h->cmsg_level != SOL_SOCKET || h->cmsg_type != SCM_RIGHTS ) { - ret = false; + msg (M_WARN, "PORT SHARE PROXY: received unknown message"); } else { @@ -482,7 +545,8 @@ control_message_from_parent (const socket_descriptor_t sd_control, server_addr, server_port, received_fd, - &buf)) + &buf, + journal_dir)) { CLEAR (buf); /* we gave the buffer to proxy_entry_new */ } @@ -517,6 +581,7 @@ proxy_connection_io_recv (struct proxy_connection *pc) { 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; } return IOSTAT_GOOD; @@ -544,7 +609,7 @@ proxy_connection_io_send (struct proxy_connection *pc, int *bytes_sent) } else { - /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status);*/ + dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status); pc->buf.len = 0; pc->buf.offset = 0; } @@ -627,6 +692,8 @@ proxy_connection_io_dispatch (struct proxy_connection *pc, int rwflags_pc = pc->rwflags; int rwflags_cp = cp->rwflags; + ASSERT(pc->defined && cp->defined && cp->counterpart == pc); + if (rwflags & EVENT_READ) { const int status = proxy_connection_io_xfer (pc, max_transfer_per_iteration); @@ -653,7 +720,11 @@ proxy_connection_io_dispatch (struct proxy_connection *pc, * This is the main function for the port share proxy background process. */ static void -port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descriptor_t sd_control) +port_share_proxy (const in_addr_t hostaddr, + const int port, + const socket_descriptor_t sd_control, + const int max_initial_buf, + const char *journal_dir) { if (send_control (sd_control, RESPONSE_INIT_SUCCEEDED) >= 0) { @@ -687,7 +758,7 @@ port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descrip 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)) + if (!control_message_from_parent (sd_control, &list, es, hostaddr, port, max_initial_buf, journal_dir)) goto done; } else @@ -713,7 +784,7 @@ port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descrip proxy_list_close (&list); event_free (es); } - msg (D_PS_PROXY, "PORT SHARE PROXY: proxy exiting"); + msg (M_INFO, "PORT SHARE PROXY: proxy exiting"); } /* @@ -721,7 +792,10 @@ port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descrip * share proxy. */ struct port_share * -port_share_open (const char *host, const int port) +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]; @@ -766,6 +840,10 @@ port_share_open (const char *host, const int port) /* don't let future subprocesses inherit child socket */ set_cloexec (fd[0]); + /* note that this will cause possible EAGAIN when writing to + control socket if proxy process is backlogged */ + set_nonblock (fd[0]); + /* wait for background child process to initialize */ status = recv_control (fd[0]); if (status == RESPONSE_INIT_SUCCEEDED) @@ -796,7 +874,7 @@ port_share_open (const char *host, const int port) prng_init (NULL, 0); /* execute the event loop */ - port_share_proxy (hostaddr, port, fd[1]); + port_share_proxy (hostaddr, port, fd[1], max_initial_buf, journal_dir); openvpn_close_socket (fd[1]); @@ -44,7 +44,9 @@ struct port_share { extern struct port_share *port_share; struct port_share *port_share_open (const char *host, - const int port); + const int port, + const int max_initial_buf, + const char *journal_dir); void port_share_close (struct port_share *ps); void port_share_abort (struct port_share *ps); |