/* Only enable a libev ev_io event if the proxied connection still * has both up and down connected */ static void shutdown_proxy(proxystate *ps, SHUTDOWN_REQUESTOR req) { if (ps->want_shutdown || req == SHUTDOWN_HARD) { ev_io_stop(loop, &ps->ev_w_up); ev_io_stop(loop, &ps->ev_r_up); ev_io_stop(loop, &ps->ev_w_handshake); ev_io_stop(loop, &ps->ev_r_handshake); ev_io_stop(loop, &ps->ev_w_down); ev_io_stop(loop, &ps->ev_r_down); close(ps->fd_up); close(ps->fd_down); SSL_set_shutdown(ps->ssl, SSL_SENT_SHUTDOWN); SSL_free(ps->ssl); free(ps); } else { ps->want_shutdown = 1; if (req == SHUTDOWN_DOWN && ringbuffer_is_empty(&ps->ring_up)) shutdown_proxy(ps, SHUTDOWN_HARD); else if (req == SHUTDOWN_UP && ringbuffer_is_empty(&ps->ring_down)) shutdown_proxy(ps, SHUTDOWN_HARD); } }
/* The libev I/O handler during the OpenSSL handshake phase. Basically, just * let OpenSSL do what it likes with the socket and obey its requests for reads * or writes */ static void client_handshake(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; t = SSL_do_handshake(ps->ssl); if (t == 1) { end_handshake(ps); } else { int err = SSL_get_error(ps->ssl, t); if (err == SSL_ERROR_WANT_READ) { ev_io_stop(loop, &ps->ev_w_handshake); ev_io_start(loop, &ps->ev_r_handshake); } else if (err == SSL_ERROR_WANT_WRITE) { ev_io_stop(loop, &ps->ev_r_handshake); ev_io_start(loop, &ps->ev_w_handshake); } else if (err == SSL_ERROR_ZERO_RETURN) { LOG("{client} Connection closed (in handshake)\n"); shutdown_proxy(ps, SHUTDOWN_UP); } else { LOG("{client} Unexpected SSL error (in handshake): %d\n", err); shutdown_proxy(ps, SHUTDOWN_UP); } } }
/* Write some previously-buffered backend data upstream on the * secure socket using OpenSSL */ static void client_write(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; int sz; proxystate *ps = (proxystate *)w->data; assert(!ringbuffer_is_empty(&ps->ring_up)); char * next = ringbuffer_read_next(&ps->ring_up, &sz); t = SSL_write(ps->ssl, next, sz); if (t > 0) { if (t == sz) { ringbuffer_read_pop(&ps->ring_up); safe_enable_io(ps, &ps->ev_r_down); // can be re-enabled b/c we've popped if (ringbuffer_is_empty(&ps->ring_up)) { if (ps->want_shutdown) { shutdown_proxy(ps, SHUTDOWN_HARD); return; } ev_io_stop(loop, &ps->ev_w_up); } } else { ringbuffer_read_skip(&ps->ring_up, t); } } else { int err = SSL_get_error(ps->ssl, t); if (err == SSL_ERROR_WANT_READ) { start_handshake(ps, err); } else if (err == SSL_ERROR_WANT_WRITE) {} /* incomplete SSL data */ else handle_fatal_ssl_error(ps, err); } }
/* Write some data, previously received on the secure upstream socket, * out of the downstream buffer and onto the backend socket */ static void back_write(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; int fd = w->fd; int sz; assert(!ringbuffer_is_empty(&ps->ring_down)); char *next = ringbuffer_read_next(&ps->ring_down, &sz); t = send(fd, next, sz, MSG_NOSIGNAL); if (t > 0) { if (t == sz) { ringbuffer_read_pop(&ps->ring_down); safe_enable_io(ps, &ps->ev_r_up); if (ringbuffer_is_empty(&ps->ring_down)) { if (ps->want_shutdown) { shutdown_proxy(ps, SHUTDOWN_HARD); return; // dealloc'd } ev_io_stop(loop, &ps->ev_w_down); } } else { ringbuffer_read_skip(&ps->ring_down, t); } } else { assert(t == -1); handle_socket_errno(ps); } }
/* Read some data from the backend when libev says data is available-- * write it into the upstream buffer and make sure the write event is * enabled for the upstream socket */ static void back_read(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; if (ps->want_shutdown) { ev_io_stop(loop, &ps->ev_r_down); return; } int fd = w->fd; char * buf = ringbuffer_write_ptr(&ps->ring_up); t = recv(fd, buf, RING_DATA_LEN, 0); if (t > 0) { ringbuffer_write_append(&ps->ring_up, t); if (ringbuffer_is_full(&ps->ring_up)) ev_io_stop(loop, &ps->ev_r_down); safe_enable_io(ps, &ps->ev_w_up); } else if (t == 0) { LOG("{backend} Connection closed\n"); shutdown_proxy(ps, SHUTDOWN_DOWN); } else { assert(t == -1); handle_socket_errno(ps); } }
/* Handle a socket error condition passed to us from OpenSSL */ static void handle_fatal_ssl_error(proxystate *ps, int err) { if (err == SSL_ERROR_ZERO_RETURN) ERR("{client} Connection closed (in data)\n"); else if (err == SSL_ERROR_SYSCALL) if (errno == 0) ERR("{client} Connection closed (in data)\n"); else perror("{client} [errno] "); else ERR("{client} Unexpected SSL_read error: %d\n", err); shutdown_proxy(ps, SHUTDOWN_UP); }
/* Handle various socket errors */ static void handle_socket_errno(proxystate *ps) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) return; if (errno == ECONNRESET) ERR("{backend} Connection reset by peer\n"); else if (errno == ETIMEDOUT) ERR("{backend} Connection to backend timed out\n"); else if (errno == EPIPE) ERR("{backend} Broken pipe to backend (EPIPE)\n"); else perror("{backend} [errno]"); shutdown_proxy(ps, SHUTDOWN_DOWN); }
/* Continue/complete the asynchronous connect() before starting data transmission * between front/backend */ static void handle_connect(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; t = connect(ps->fd_down, backaddr->ai_addr, backaddr->ai_addrlen); if (!t || errno == EISCONN || !errno) { /* INIT */ ev_io_stop(loop, &ps->ev_w_down); ev_io_init(&ps->ev_r_down, back_read, ps->fd_down, EV_READ); ev_io_init(&ps->ev_w_down, back_write, ps->fd_down, EV_WRITE); start_handshake(ps, SSL_ERROR_WANT_READ); /* for client-first handshake */ ev_io_start(loop, &ps->ev_r_down); if (OPTIONS.WRITE_IP_OCTET) { char *ring_pnt = ringbuffer_write_ptr(&ps->ring_down); assert(ps->remote_ip.ss_family == AF_INET || ps->remote_ip.ss_family == AF_INET6); *ring_pnt++ = (unsigned char) ps->remote_ip.ss_family; if (ps->remote_ip.ss_family == AF_INET6) { memcpy(ring_pnt, &((struct sockaddr_in6 *) &ps->remote_ip) ->sin6_addr.s6_addr, 16U); ringbuffer_write_append(&ps->ring_down, 1U + 16U); } else { memcpy(ring_pnt, &((struct sockaddr_in *) &ps->remote_ip) ->sin_addr.s_addr, 4U); ringbuffer_write_append(&ps->ring_down, 1U + 4U); } ev_io_start(loop, &ps->ev_w_down); } } else if (errno == EINPROGRESS || errno == EINTR || errno == EALREADY) { /* do nothing, we'll get phoned home again... */ } else { perror("{backend-connect}"); shutdown_proxy(ps, SHUTDOWN_HARD); } }
/* Continue/complete the asynchronous connect() before starting data transmission * between front/backend */ static void handle_connect(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; char tcp6_address_string[INET6_ADDRSTRLEN]; size_t written = 0; t = connect(ps->fd_down, backaddr->ai_addr, backaddr->ai_addrlen); if (!t || errno == EISCONN || !errno) { /* INIT */ ev_io_stop(loop, &ps->ev_w_down); ev_io_init(&ps->ev_r_down, back_read, ps->fd_down, EV_READ); ev_io_init(&ps->ev_w_down, back_write, ps->fd_down, EV_WRITE); start_handshake(ps, SSL_ERROR_WANT_READ); /* for client-first handshake */ ev_io_start(loop, &ps->ev_r_down); if (OPTIONS.WRITE_PROXY_LINE) { char *ring_pnt = ringbuffer_write_ptr(&ps->ring_down); assert(ps->remote_ip.ss_family == AF_INET || ps->remote_ip.ss_family == AF_INET6); if(ps->remote_ip.ss_family == AF_INET) { struct sockaddr_in* addr = (struct sockaddr_in*)&ps->remote_ip; written = snprintf(ring_pnt, RING_DATA_LEN, tcp_proxy_line, "TCP4", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } else if (ps->remote_ip.ss_family == AF_INET6) { struct sockaddr_in6* addr = (struct sockaddr_in6*)&ps->remote_ip; inet_ntop(AF_INET6,&(addr->sin6_addr),tcp6_address_string,INET6_ADDRSTRLEN); written = snprintf(ring_pnt, RING_DATA_LEN, tcp_proxy_line, "TCP6", tcp6_address_string, ntohs(addr->sin6_port)); } ringbuffer_write_append(&ps->ring_down, written); ev_io_start(loop, &ps->ev_w_down); } else if (OPTIONS.WRITE_IP_OCTET) { char *ring_pnt = ringbuffer_write_ptr(&ps->ring_down); assert(ps->remote_ip.ss_family == AF_INET || ps->remote_ip.ss_family == AF_INET6); *ring_pnt++ = (unsigned char) ps->remote_ip.ss_family; if (ps->remote_ip.ss_family == AF_INET6) { memcpy(ring_pnt, &((struct sockaddr_in6 *) &ps->remote_ip) ->sin6_addr.s6_addr, 16U); ringbuffer_write_append(&ps->ring_down, 1U + 16U); } else { memcpy(ring_pnt, &((struct sockaddr_in *) &ps->remote_ip) ->sin_addr.s_addr, 4U); ringbuffer_write_append(&ps->ring_down, 1U + 4U); } ev_io_start(loop, &ps->ev_w_down); } } else if (errno == EINPROGRESS || errno == EINTR || errno == EALREADY) { /* do nothing, we'll get phoned home again... */ } else { perror("{backend-connect}"); shutdown_proxy(ps, SHUTDOWN_HARD); } }