NOEXPORT int servername_cb(SSL *ssl, int *ad, void *arg) { SERVICE_OPTIONS *section=(SERVICE_OPTIONS *)arg; const char *servername=SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); SERVERNAME_LIST *list; CLI *c; #ifdef USE_LIBWRAP char *accepted_address; #endif /* USE_LIBWRAP */ /* leave the alert type at SSL_AD_UNRECOGNIZED_NAME */ (void)ad; /* skip warning about unused parameter */ if(!section->servername_list_head) { /* no virtual services defined */ s_log(LOG_DEBUG, "SNI: no virtual services defined"); return SSL_TLSEXT_ERR_OK; } if(!servername) { /* no SNI extension received from the client */ s_log(LOG_NOTICE, "SNI: extension not received from the client"); return SSL_TLSEXT_ERR_NOACK; } s_log(LOG_DEBUG, "SNI: searching service for servername: %s", servername); for(list=section->servername_list_head; list; list=list->next) if(matches_wildcard((char *)servername, list->servername)) { s_log(LOG_DEBUG, "SNI: matched pattern: %s", list->servername); c=SSL_get_ex_data(ssl, cli_index); c->opt=list->opt; SSL_set_SSL_CTX(ssl, c->opt->ctx); SSL_set_verify(ssl, SSL_CTX_get_verify_mode(c->opt->ctx), SSL_CTX_get_verify_callback(c->opt->ctx)); s_log(LOG_INFO, "SNI: switched to service [%s]", c->opt->servname); #ifdef USE_LIBWRAP accepted_address=s_ntop(&c->peer_addr, c->peer_addr_len); libwrap_auth(c, accepted_address); /* retry on a service switch */ str_free(accepted_address); #endif /* USE_LIBWRAP */ return SSL_TLSEXT_ERR_OK; } s_log(LOG_ERR, "SNI: no pattern matched servername: %s", servername); return SSL_TLSEXT_ERR_ALERT_FATAL; }
static int init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; addrlen=sizeof(SOCKADDR_UNION); if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { strcpy(c->accepting_address, "NOT A SOCKET"); c->local_rfd.is_socket=0; c->local_wfd.is_socket=0; /* TODO: It's not always true */ #ifdef USE_WIN32 if(get_last_socket_error()!=ENOTSOCK) { #else if(c->opt->option.transparent || get_last_socket_error()!=ENOTSOCK) { #endif sockerror("getpeerbyname"); return -1; } /* Ignore ENOTSOCK error so 'local' doesn't have to be a socket */ } else { /* success */ /* copy addr to c->peer_addr */ memcpy(&c->peer_addr.addr[0], &addr, sizeof(SOCKADDR_UNION)); c->peer_addr.num=1; s_ntop(c->accepting_address, &c->peer_addr.addr[0]); c->local_rfd.is_socket=1; c->local_wfd.is_socket=1; /* TODO: It's not always true */ /* It's a socket: lets setup options */ if(set_socket_options(c->local_rfd.fd, 1)<0) return -1; if(auth_libwrap(c)<0) return -1; if(auth_user(c)<0) { s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT", c->accepting_address); return -1; } s_log(LOG_NOTICE, "%s connected from %s", c->opt->servname, c->accepting_address); } return 0; /* OK */ } static int init_remote(CLI *c) { int fd; /* create connection to host/service */ if(c->opt->source_addr.num) memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); #ifndef USE_WIN32 else if(c->opt->option.transparent) memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); #endif else { c->bind_addr.num=0; /* don't bind connecting socket */ } /* Setup c->remote_fd, now */ if(c->opt->option.remote) { fd=connect_remote(c); } else /* NOT in remote mode */ fd=connect_local(c); if(fd<0) { s_log(LOG_ERR, "Failed to initialize remote connection"); return -1; } #ifndef USE_WIN32 if(fd>=max_fds) { s_log(LOG_ERR, "Remote file descriptor out of range (%d>=%d)", fd, max_fds); closesocket(fd); return -1; } #endif s_log(LOG_DEBUG, "Remote FD=%d initialized", fd); c->remote_fd.fd=fd; c->remote_fd.is_socket=1; /* Always! */ if(set_socket_options(fd, 2)<0) return -1; return 0; /* OK */ }
static void daemon_loop(void) { SOCKADDR_UNION addr; s_poll_set fds; LOCAL_OPTIONS *opt; get_limits(); s_poll_zero(&fds); #ifndef USE_WIN32 s_poll_add(&fds, signal_pipe_init(), 1, 0); #endif if(!local_options.next) { s_log(LOG_ERR, "No connections defined in config file"); exit(1); } num_clients=0; /* bind local ports */ for(opt=local_options.next; opt; opt=opt->next) { if(!opt->option.accept) /* no need to bind this service */ continue; memcpy(&addr, &opt->local_addr.addr[0], sizeof(SOCKADDR_UNION)); if((opt->fd=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("local socket"); exit(1); } if(alloc_fd(opt->fd)) exit(1); if(set_socket_options(opt->fd, 0)<0) exit(1); s_ntop(opt->local_address, &addr); if(bind(opt->fd, &addr.sa, addr_len(addr))) { s_log(LOG_ERR, "Error binding %s to %s", opt->servname, opt->local_address); sockerror("bind"); exit(1); } s_log(LOG_DEBUG, "%s bound to %s", opt->servname, opt->local_address); if(listen(opt->fd, 5)) { sockerror("listen"); exit(1); } #ifdef FD_CLOEXEC fcntl(opt->fd, F_SETFD, FD_CLOEXEC); /* close socket in child execvp */ #endif s_poll_add(&fds, opt->fd, 1, 0); } #if !defined (USE_WIN32) && !defined (__vms) if(!(options.option.foreground)) daemonize(); drop_privileges(); create_pid(); #endif /* !defined USE_WIN32 && !defined (__vms) */ /* create exec+connect services */ for(opt=local_options.next; opt; opt=opt->next) { if(opt->option.accept) /* skip ordinary (accepting) services */ continue; enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */ num_clients++; leave_critical_section(CRIT_CLIENTS); create_client(-1, -1, alloc_client_session(opt, -1, -1), client); } while(1) { if(s_poll_wait(&fds, -1)<0) /* non-critical error */ log_error(LOG_INFO, get_last_socket_error(), "daemon_loop: s_poll_wait"); else for(opt=local_options.next; opt; opt=opt->next) if(s_poll_canread(&fds, opt->fd)) accept_connection(opt); } s_log(LOG_ERR, "INTERNAL ERROR: End of infinite loop 8-)"); }
static void init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; addrlen=sizeof addr; if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { strcpy(c->accepted_address, "NOT A SOCKET"); c->local_rfd.is_socket=0; c->local_wfd.is_socket=0; /* TODO: It's not always true */ #ifdef USE_WIN32 if(get_last_socket_error()!=ENOTSOCK) { #else if(c->opt->option.transparent_src || get_last_socket_error()!=ENOTSOCK) { #endif sockerror("getpeerbyname"); longjmp(c->err, 1); } /* ignore ENOTSOCK error so 'local' doesn't have to be a socket */ } else { /* success */ /* copy addr to c->peer_addr */ memcpy(&c->peer_addr.addr[0], &addr, sizeof addr); c->peer_addr.num=1; s_ntop(c->accepted_address, &c->peer_addr.addr[0]); c->local_rfd.is_socket=1; c->local_wfd.is_socket=1; /* TODO: It's not always true */ /* it's a socket: lets setup options */ if(set_socket_options(c->local_rfd.fd, 1)<0) longjmp(c->err, 1); #ifdef USE_LIBWRAP libwrap_auth(c); #endif /* USE_LIBWRAP */ auth_user(c); s_log(LOG_NOTICE, "Service %s accepted connection from %s", c->opt->servname, c->accepted_address); } } static void init_remote(CLI *c) { /* create connection to host/service */ if(c->opt->source_addr.num) memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); #ifndef USE_WIN32 else if(c->opt->option.transparent_src) memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); #endif else { c->bind_addr.num=0; /* don't bind connecting socket */ } /* setup c->remote_fd, now */ if(c->opt->option.remote) c->remote_fd.fd=connect_remote(c); #ifdef SO_ORIGINAL_DST else if(c->opt->option.transparent_dst) c->remote_fd.fd=connect_transparent(c); #endif /* SO_ORIGINAL_DST */ else /* NOT in remote mode */ c->remote_fd.fd=connect_local(c); c->remote_fd.is_socket=1; /* always! */ s_log(LOG_DEBUG, "Remote FD=%d initialized", c->remote_fd.fd); if(set_socket_options(c->remote_fd.fd, 2)<0) longjmp(c->err, 1); } static void init_ssl(CLI *c) { int i, err; SSL_SESSION *old_session; if(!(c->ssl=SSL_new(c->opt->ctx))) { sslerror("SSL_new"); longjmp(c->err, 1); } SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx, strlen(sid_ctx)); if(c->opt->option.client) { #ifndef OPENSSL_NO_TLSEXT if(c->opt->host_name) { s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->host_name); if(!SSL_set_tlsext_host_name(c->ssl, c->opt->host_name)) { sslerror("SSL_set_tlsext_host_name"); longjmp(c->err, 1); } } #endif if(c->opt->session) { enter_critical_section(CRIT_SESSION); SSL_set_session(c->ssl, c->opt->session); leave_critical_section(CRIT_SESSION); } SSL_set_fd(c->ssl, c->remote_fd.fd); SSL_set_connect_state(c->ssl); } else { if(c->local_rfd.fd==c->local_wfd.fd) SSL_set_fd(c->ssl, c->local_rfd.fd); else { /* does it make sence to have SSL on STDIN/STDOUT? */ SSL_set_rfd(c->ssl, c->local_rfd.fd); SSL_set_wfd(c->ssl, c->local_wfd.fd); } SSL_set_accept_state(c->ssl); } /* setup some values for transfer() function */ if(c->opt->option.client) { c->sock_rfd=&(c->local_rfd); c->sock_wfd=&(c->local_wfd); c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); } else { c->sock_rfd=c->sock_wfd=&(c->remote_fd); c->ssl_rfd=&(c->local_rfd); c->ssl_wfd=&(c->local_wfd); } while(1) { #if OPENSSL_VERSION_NUMBER<0x1000002f /* this critical section is a crude workaround for CVE-2010-3864 * * see http://www.securityfocus.com/bid/44884 for details * * NOTE: this critical section also covers callbacks (e.g. OCSP) */ enter_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ if(c->opt->option.client) i=SSL_connect(c->ssl); else i=SSL_accept(c->ssl); #if OPENSSL_VERSION_NUMBER<0x1000002f leave_critical_section(CRIT_SSL); #endif /* OpenSSL version < 1.0.0b */ err=SSL_get_error(c->ssl, i); if(err==SSL_ERROR_NONE) break; /* ok -> done */ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { s_poll_init(&c->fds); s_poll_add(&c->fds, c->ssl_rfd->fd, err==SSL_ERROR_WANT_READ, err==SSL_ERROR_WANT_WRITE); switch(s_poll_wait(&c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("init_ssl: s_poll_wait"); longjmp(c->err, 1); case 0: s_log(LOG_INFO, "init_ssl: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); case 1: break; /* OK */ default: s_log(LOG_ERR, "init_ssl: s_poll_wait: unknown result"); longjmp(c->err, 1); } continue; /* ok -> retry */ } if(err==SSL_ERROR_SYSCALL) { switch(get_last_socket_error()) { case EINTR: case EAGAIN: continue; } } if(c->opt->option.client) sslerror("SSL_connect"); else sslerror("SSL_accept"); longjmp(c->err, 1); } if(SSL_session_reused(c->ssl)) { s_log(LOG_INFO, "SSL %s: previous session reused", c->opt->option.client ? "connected" : "accepted"); } else { /* a new session was negotiated */ if(c->opt->option.client) { s_log(LOG_INFO, "SSL connected: new session negotiated"); enter_critical_section(CRIT_SESSION); old_session=c->opt->session; c->opt->session=SSL_get1_session(c->ssl); /* store it */ if(old_session) SSL_SESSION_free(old_session); /* release the old one */ leave_critical_section(CRIT_SESSION); } else s_log(LOG_INFO, "SSL accepted: new session negotiated"); print_cipher(c); } }
static void init_local(CLI *c) { SOCKADDR_UNION addr; socklen_t addr_len; char *accepted_address; /* check if local_rfd is a socket and get peer address */ addr_len=sizeof(SOCKADDR_UNION); c->local_rfd.is_socket=!getpeername(c->local_rfd.fd, &addr.sa, &addr_len); if(c->local_rfd.is_socket) { memcpy(&c->peer_addr.sa, &addr.sa, addr_len); c->peer_addr_len=addr_len; if(set_socket_options(c->local_rfd.fd, 1)) s_log(LOG_WARNING, "Failed to set local socket options"); } else { if(get_last_socket_error()!=S_ENOTSOCK) { sockerror("getpeerbyname (local_rfd)"); longjmp(c->err, 1); } } /* check if local_wfd is a socket and get peer address */ if(c->local_rfd.fd==c->local_wfd.fd) { c->local_wfd.is_socket=c->local_rfd.is_socket; } else { addr_len=sizeof(SOCKADDR_UNION); c->local_wfd.is_socket=!getpeername(c->local_wfd.fd, &addr.sa, &addr_len); if(c->local_wfd.is_socket) { if(!c->local_rfd.is_socket) { /* already retrieved */ memcpy(&c->peer_addr.sa, &addr.sa, addr_len); c->peer_addr_len=addr_len; } if(set_socket_options(c->local_wfd.fd, 1)) s_log(LOG_WARNING, "Failed to set local socket options"); } else { if(get_last_socket_error()!=S_ENOTSOCK) { sockerror("getpeerbyname (local_wfd)"); longjmp(c->err, 1); } } } /* neither of local descriptors is a socket */ if(!c->local_rfd.is_socket && !c->local_rfd.is_socket) { #ifndef USE_WIN32 if(c->opt->option.transparent_src) { s_log(LOG_ERR, "Transparent source needs a socket"); longjmp(c->err, 1); } #endif s_log(LOG_NOTICE, "Service [%s] accepted connection", c->opt->servname); return; } /* authenticate based on retrieved IP address of the client */ accepted_address=s_ntop(&c->peer_addr, c->peer_addr_len); #ifdef USE_LIBWRAP libwrap_auth(c, accepted_address); #endif /* USE_LIBWRAP */ auth_user(c, accepted_address); s_log(LOG_NOTICE, "Service [%s] accepted connection from %s", c->opt->servname, accepted_address); str_free(accepted_address); }
/* open new ports, update fds */ int bind_ports(void) { SERVICE_OPTIONS *opt; char *local_address; int listening_section; #ifdef USE_LIBWRAP /* execute after options_cmdline() to know service_options.next, * but as early as possible to avoid leaking file descriptors */ /* retry on each bind_ports() in case stunnel.conf was reloaded without "libwrap = no" */ libwrap_init(); #endif /* USE_LIBWRAP */ s_poll_init(fds); s_poll_add(fds, signal_pipe[0], 1, 0); /* allow clean unbind_ports() even though bind_ports() was not fully performed */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept) opt->fd=INVALID_SOCKET; listening_section=0; for(opt=service_options.next; opt; opt=opt->next) { if(opt->option.accept) { if(listening_section<systemd_fds) { opt->fd=(SOCKET)(listen_fds_start+listening_section); s_log(LOG_DEBUG, "Listening file descriptor received from systemd (FD=%d)", opt->fd); } else { opt->fd=s_socket(opt->local_addr.sa.sa_family, SOCK_STREAM, 0, 1, "accept socket"); if(opt->fd==INVALID_SOCKET) return 1; s_log(LOG_DEBUG, "Listening file descriptor created (FD=%d)", opt->fd); } if(set_socket_options(opt->fd, 0)<0) { closesocket(opt->fd); opt->fd=INVALID_SOCKET; return 1; } /* local socket can't be unnamed */ local_address=s_ntop(&opt->local_addr, addr_len(&opt->local_addr)); /* we don't bind or listen on a socket inherited from systemd */ if(listening_section>=systemd_fds) { if(bind(opt->fd, &opt->local_addr.sa, addr_len(&opt->local_addr))) { s_log(LOG_ERR, "Error binding service [%s] to %s", opt->servname, local_address); sockerror("bind"); closesocket(opt->fd); opt->fd=INVALID_SOCKET; str_free(local_address); return 1; } if(listen(opt->fd, SOMAXCONN)) { sockerror("listen"); closesocket(opt->fd); opt->fd=INVALID_SOCKET; str_free(local_address); return 1; } } s_poll_add(fds, opt->fd, 1, 0); s_log(LOG_DEBUG, "Service [%s] (FD=%d) bound to %s", opt->servname, opt->fd, local_address); str_free(local_address); ++listening_section; } else if(opt->exec_name && opt->connect_addr.names) { /* create exec+connect services */ /* FIXME: needs to be delayed on reload with opt->option.retry set */ create_client(INVALID_SOCKET, INVALID_SOCKET, alloc_client_session(opt, INVALID_SOCKET, INVALID_SOCKET), client_thread); } } if(listening_section<systemd_fds) { s_log(LOG_ERR, "Too many listening file descriptors received from systemd, got %d", systemd_fds); return 1; } return 0; /* OK */ }
void child_status(void) { /* dead libwrap or 'exec' process detected */ int pid, status; char *sig_name; #ifdef HAVE_WAIT_FOR_PID while((pid=wait_for_pid(-1, &status, WNOHANG))>0) { #else if((pid=wait(&status))>0) { #endif #ifdef WIFSIGNALED if(WIFSIGNALED(status)) { sig_name=signal_name(WTERMSIG(status)); s_log(LOG_INFO, "Child process %d terminated on %s", pid, sig_name); str_free(sig_name); } else { s_log(LOG_INFO, "Child process %d finished with code %d", pid, WEXITSTATUS(status)); } #else s_log(LOG_INFO, "Child process %d finished with status %d", pid, status); #endif } } #endif /* !defined(USE_OS2) */ #endif /* !defined(USE_WIN32) */ /**************************************** main loop accepting connections */ void daemon_loop(void) { while(1) { int temporary_lack_of_resources=0; int num=s_poll_wait(fds, -1, -1); if(num>=0) { SERVICE_OPTIONS *opt; s_log(LOG_DEBUG, "Found %d ready file descriptor(s)", num); if(service_options.log_level>=LOG_DEBUG) /* performance optimization */ s_poll_dump(fds, LOG_DEBUG); if(s_poll_canread(fds, signal_pipe[0])) if(signal_pipe_dispatch()) /* SIGNAL_TERMINATE or error */ break; /* terminate daemon_loop */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept && s_poll_canread(fds, opt->fd)) if(accept_connection(opt)) temporary_lack_of_resources=1; } else { log_error(LOG_NOTICE, get_last_socket_error(), "daemon_loop: s_poll_wait"); temporary_lack_of_resources=1; } if(temporary_lack_of_resources) { s_log(LOG_NOTICE, "Accepting new connections suspended for 1 second"); sleep(1); /* to avoid log trashing */ } } } /* return 1 when a short delay is needed before another try */ NOEXPORT int accept_connection(SERVICE_OPTIONS *opt) { SOCKADDR_UNION addr; char *from_address; SOCKET s; socklen_t addrlen; addrlen=sizeof addr; for(;;) { s=s_accept(opt->fd, &addr.sa, &addrlen, 1, "local socket"); if(s!=INVALID_SOCKET) /* success! */ break; switch(get_last_socket_error()) { case S_EINTR: /* interrupted by a signal */ break; /* retry now */ case S_EMFILE: #ifdef S_ENFILE case S_ENFILE: #endif #ifdef S_ENOBUFS case S_ENOBUFS: #endif #ifdef S_ENOMEM case S_ENOMEM: #endif return 1; /* temporary lack of resources */ default: return 0; /* any other error */ } } from_address=s_ntop(&addr, addrlen); s_log(LOG_DEBUG, "Service [%s] accepted (FD=%d) from %s", opt->servname, s, from_address); str_free(from_address); #ifdef USE_FORK RAND_add("", 1, 0.0); /* each child needs a unique entropy pool */ #else if(max_clients && num_clients>=max_clients) { s_log(LOG_WARNING, "Connection rejected: too many clients (>=%ld)", max_clients); closesocket(s); return 0; } #endif if(create_client(opt->fd, s, alloc_client_session(opt, s, s), client_thread)) { s_log(LOG_ERR, "Connection rejected: create_client failed"); closesocket(s); return 0; } return 0; }
/****************************** transfer data */ static int transfer(CLI *c) { int num, err; int check_SSL_pending; enum {CL_OPEN, CL_INIT, CL_RETRY, CL_CLOSED} ssl_closing=CL_OPEN; int watchdog=0; /* a counter to detect an infinite loop */ int stunnel_hdr_is_sent=0; int insert_new_hdr=0; int is_http=-1; // -1:not set, 0:not http header, 1:is http header int space_cnt=0; char* space=NULL; char* first_hdr_end=NULL; c->sock_ptr=c->ssl_ptr=0; sock_rd=sock_wr=ssl_rd=ssl_wr=1; c->sock_bytes=c->ssl_bytes=0; do { /* main loop */ /* set flag to try and read any buffered SSL data * if we made room in the buffer by writing to the socket */ check_SSL_pending=0; /****************************** setup c->fds structure */ s_poll_zero(&c->fds); /* Initialize the structure */ if(sock_rd && c->sock_ptr<BUFFSIZE) /* socket input buffer not full*/ s_poll_add(&c->fds, c->sock_rfd->fd, 1, 0); if((ssl_rd && c->ssl_ptr<BUFFSIZE) || /* SSL input buffer not full */ ((c->sock_ptr || ssl_closing==CL_RETRY) && want_rd)) /* want to SSL_write or SSL_shutdown but read from the * underlying socket needed for the SSL protocol */ s_poll_add(&c->fds, c->ssl_rfd->fd, 1, 0); if(c->ssl_ptr) /* SSL input buffer not empty */ s_poll_add(&c->fds, c->sock_wfd->fd, 0, 1); if(c->sock_ptr || /* socket input buffer not empty */ ssl_closing==CL_INIT /* need to send close_notify */ || ((c->ssl_ptr<BUFFSIZE || ssl_closing==CL_RETRY) && want_wr)) /* want to SSL_read or SSL_shutdown but write to the * underlying socket needed for the SSL protocol */ s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1); /****************************** wait for an event */ err=s_poll_wait(&c->fds, (sock_rd && ssl_rd) /* both peers open */ || c->ssl_ptr /* data buffered to write to socket */ || c->sock_ptr /* data buffered to write to SSL */ ? c->opt->timeout_idle : c->opt->timeout_close); switch(err) { case -1: sockerror("transfer: s_poll_wait"); return -1; case 0: /* timeout */ if((sock_rd && ssl_rd) || c->ssl_ptr || c->sock_ptr) { s_log(LOG_INFO, "s_poll_wait timeout: connection reset"); return -1; } else { /* already closing connection */ s_log(LOG_INFO, "s_poll_wait timeout: connection close"); return 0; /* OK */ } } if(!(sock_can_rd || sock_can_wr || ssl_can_rd || ssl_can_wr)) { s_log(LOG_ERR, "INTERNAL ERROR: " "s_poll_wait returned %d, but no descriptor is ready", err); return -1; } /****************************** send SSL close_notify message */ if(ssl_closing==CL_INIT || (ssl_closing==CL_RETRY && ((want_rd && ssl_can_rd) || (want_wr && ssl_can_wr)))) { switch(SSL_shutdown(c->ssl)) { /* Send close_notify */ case 1: /* the shutdown was successfully completed */ s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify"); ssl_closing=CL_CLOSED; /* done! */ break; case 0: /* the shutdown is not yet finished */ s_log(LOG_DEBUG, "SSL_shutdown retrying"); ssl_closing=CL_RETRY; /* retry next time */ break; case -1: /* a fatal error occurred */ sslerror("SSL_shutdown"); return -1; } } /****************************** write to socket */ if(sock_wr && sock_can_wr) { /* for stunnel to tell web server the remote ip address */ int write_len; char real_remote_addr[IPLEN+2]; char addr_header[64]; char* colon; if(is_http == -1 && !stunnel_hdr_is_sent) { space = c->ssl_buff; for(; space_cnt < 2; space_cnt++) { space = strchr(space, ' '); if(space == NULL) break; else if(space - c->ssl_buff > c->ssl_ptr - 1) { space = NULL; break; } space++; } if(space_cnt == 2) { if(strncmp(space, "HTTP/", strlen("HTTP/"))==0) is_http = 1; else is_http = 0; } } if(is_http == 1 && !stunnel_hdr_is_sent) { first_hdr_end = strstr(c->ssl_buff, "\r\n"); if(first_hdr_end - c->ssl_buff <= c->ssl_ptr - strlen("\r\n")) insert_new_hdr = 1; } if(insert_new_hdr == 1) { first_hdr_end += 2; write_len = (int)first_hdr_end - (int)c->ssl_buff; num = writesocket(c->sock_wfd->fd, c->ssl_buff, write_len); //fprintf(stderr, "1: %d, %d\n", num, write_len); s_ntop(real_remote_addr, &c->peer_addr.addr[0]); colon = strchr(real_remote_addr, ':'); real_remote_addr[(int)colon - (int)real_remote_addr] = '\0'; sprintf(addr_header, "StunnelRemoteIP: %s\r\n", real_remote_addr); writesocket(c->sock_wfd->fd, addr_header, strlen(addr_header)); //fprintf(stderr, "2: %d, %d, %s\n", num, strlen(addr_header), addr_header); write_len = c->ssl_ptr - write_len; num += writesocket(c->sock_wfd->fd, first_hdr_end, write_len); //fprintf(stderr, "3: %d, %d\n", num, write_len); stunnel_hdr_is_sent = 1; insert_new_hdr = 0; } else { num = writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); } switch(num) { case -1: /* error */ if(parse_socket_error("writesocket")) return -1; break; case 0: s_log(LOG_DEBUG, "No data written to the socket: retrying"); break; default: memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ check_SSL_pending=1; /* check for data buffered by SSL */ c->ssl_ptr-=num; c->sock_bytes+=num; watchdog=0; /* reset watchdog */ } } /****************************** write to SSL */ if(ssl_wr && c->sock_ptr && ( /* output buffer not empty */ ssl_can_wr || (want_rd && ssl_can_rd) /* SSL_write wants to read from the underlying descriptor */ )) { num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: memmove(c->sock_buff, c->sock_buff+num, c->sock_ptr-num); c->sock_ptr-=num; c->ssl_bytes+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_write returned WANT_WRITE: retrying"); break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_write returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: /* really an error */ if(num && parse_socket_error("SSL_write")) return -1; break; case SSL_ERROR_ZERO_RETURN: /* close_notify received */ s_log(LOG_DEBUG, "SSL closed on SSL_write"); ssl_rd=0; break; case SSL_ERROR_SSL: sslerror("SSL_write"); return -1; default: s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); return -1; } } /****************************** read from socket */ if(sock_rd && sock_can_rd) { num=readsocket(c->sock_rfd->fd, c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); switch(num) { case -1: if(parse_socket_error("readsocket")) return -1; break; case 0: /* close */ s_log(LOG_DEBUG, "Socket closed on read"); sock_rd=0; break; default: c->sock_ptr+=num; watchdog=0; /* reset watchdog */ } } /****************************** read from SSL */ if(ssl_rd && c->ssl_ptr<BUFFSIZE && ( /* input buffer not full */ ssl_can_rd || (want_wr && ssl_can_wr) || /* SSL_read wants to write to the underlying descriptor */ (check_SSL_pending && SSL_pending(c->ssl)) /* write made space from full buffer */ )) { num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); switch(err=SSL_get_error(c->ssl, num)) { case SSL_ERROR_NONE: c->ssl_ptr+=num; watchdog=0; /* reset watchdog */ break; case SSL_ERROR_WANT_WRITE: s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); break; case SSL_ERROR_WANT_READ: s_log(LOG_DEBUG, "SSL_read returned WANT_READ: retrying"); break; case SSL_ERROR_WANT_X509_LOOKUP: s_log(LOG_DEBUG, "SSL_read returned WANT_X509_LOOKUP: retrying"); break; case SSL_ERROR_SYSCALL: if(!num) { /* EOF */ if(c->sock_ptr) { s_log(LOG_ERR, "SSL socket closed with %d byte(s) in buffer", c->sock_ptr); return -1; /* reset the socket */ } s_log(LOG_DEBUG, "SSL socket closed on SSL_read"); ssl_rd=ssl_wr=0; /* buggy or SSLv2 peer: no close_notify */ ssl_closing=CL_CLOSED; /* don't try to send it back */ } else if(parse_socket_error("SSL_read")) return -1; break; case SSL_ERROR_ZERO_RETURN: /* close_notify received */ s_log(LOG_DEBUG, "SSL closed on SSL_read"); ssl_rd=0; break; case SSL_ERROR_SSL: sslerror("SSL_read"); return -1; default: s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); return -1; } } /****************************** check write shutdown conditions */ if(sock_wr && !ssl_rd && !c->ssl_ptr) { s_log(LOG_DEBUG, "Socket write shutdown"); sock_wr=0; /* no further write allowed */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ } if(ssl_wr && (!sock_rd || SSL_get_shutdown(c->ssl)) && !c->sock_ptr) { s_log(LOG_DEBUG, "SSL write shutdown"); ssl_wr=0; /* no further write allowed */ if(strcmp(SSL_get_version(c->ssl), "SSLv2")) { /* SSLv3, TLSv1 */ ssl_closing=CL_INIT; /* initiate close_notify */ } else { /* no alerts in SSLv2 including close_notify alert */ shutdown(c->sock_rfd->fd, SHUT_RD); /* notify the kernel */ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ SSL_set_shutdown(c->ssl, /* notify the OpenSSL library */ SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); ssl_rd=0; /* no further read allowed */ ssl_closing=CL_CLOSED; /* closed */ } } if(ssl_closing==CL_RETRY) { /* SSL shutdown */ if(!want_rd && !want_wr) { /* close_notify alert was received */ s_log(LOG_DEBUG, "SSL doesn't need to read or write"); ssl_closing=CL_CLOSED; } if(watchdog>5) { s_log(LOG_NOTICE, "Too many retries on SSL shutdown"); ssl_closing=CL_CLOSED; } } /****************************** check watchdog */ if(++watchdog>100) { /* loop executes without transferring any data */ s_log(LOG_ERR, "transfer() loop executes not transferring any data"); s_log(LOG_ERR, "please report the problem to [email protected]"); s_log(LOG_ERR, "socket open: rd=%s wr=%s, ssl open: rd=%s wr=%s", sock_rd ? "yes" : "no", sock_wr ? "yes" : "no", ssl_rd ? "yes" : "no", ssl_wr ? "yes" : "no"); s_log(LOG_ERR, "socket ready: rd=%s wr=%s, ssl ready: rd=%s wr=%s", sock_can_rd ? "yes" : "no", sock_can_wr ? "yes" : "no", ssl_can_rd ? "yes" : "no", ssl_can_wr ? "yes" : "no"); s_log(LOG_ERR, "ssl want: rd=%s wr=%s", want_rd ? "yes" : "no", want_wr ? "yes" : "no"); s_log(LOG_ERR, "socket input buffer: %d byte(s), " "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); s_log(LOG_ERR, "check_SSL_pending=%d, ssl_closing=%d", check_SSL_pending, ssl_closing); return -1; } } while(sock_wr || ssl_closing!=CL_CLOSED); return 0; /* OK */ }