Esempio n. 1
0
    /* wait for the result of a non-blocking connect */
static int connect_wait(CLI *c, int fd, int timeout) {
    int error;
    socklen_t optlen;

    s_log(LOG_DEBUG, "connect_wait: waiting %d seconds", timeout);
    s_poll_zero(&c->fds);
    s_poll_add(&c->fds, fd, 1, 1);
    switch(s_poll_wait(&c->fds, timeout)) {
    case -1:
        sockerror("connect_wait: s_poll_wait");
        return -1; /* error */
    case 0:
        s_log(LOG_INFO, "connect_wait: s_poll_wait timeout");
        return -1; /* error */
    default:
        if(s_poll_canread(&c->fds, fd)) {
            /* just connected socket should not be ready for read */
            /* get the resulting error code, now */
            optlen=sizeof(error);
            if(!getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &optlen))
                errno=error;
            if(errno) { /* really an error? */
                sockerror("connect_wait: getsockopt");
                return -1; /* connection failed */
            }
        }
        if(s_poll_canwrite(&c->fds, fd)) {
            s_log(LOG_DEBUG, "connect_wait: connected");
            return 0; /* success */
        }
        s_log(LOG_ERR, "connect_wait: unexpected s_poll_wait result");
        return -1; /* unexpected result */
    }
    return -1; /* should not be possible */
}
Esempio n. 2
0
void daemon_loop(void) {
    SERVICE_OPTIONS *opt;
    int temporary_lack_of_resources;

    while(1) {
        temporary_lack_of_resources=0;
        if(s_poll_wait(fds, -1, -1)>=0) {
            if(s_poll_canread(fds, signal_pipe[0]))
                if(signal_pipe_dispatch()) /* received SIGNAL_TERMINATE */
                    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 */
        }
    }
}
Esempio n. 3
0
void s_write(CLI *c, int fd, const void *buf, int len) {
        /* simulate a blocking write */
    u8 *ptr=(u8 *)buf;
    int num;

    while(len>0) {
        s_poll_init(c->fds);
        s_poll_add(c->fds, fd, 0, 1); /* write */
        switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) {
        case -1:
            sockerror("s_write: s_poll_wait");
            longjmp(c->err, 1); /* error */
        case 0:
            s_log(LOG_INFO, "s_write: s_poll_wait:"
                " TIMEOUTbusy exceeded: sending reset");
            longjmp(c->err, 1); /* timeout */
        case 1:
            break; /* OK */
        default:
            s_log(LOG_ERR, "s_write: s_poll_wait: unknown result");
            longjmp(c->err, 1); /* error */
        }
        num=writesocket(fd, (void *)ptr, len);
        switch(num) {
        case -1: /* error */
            sockerror("writesocket (s_write)");
            longjmp(c->err, 1);
        }
        ptr+=num;
        len-=num;
    }
}
Esempio n. 4
0
void s_read(CLI *c, int fd, void *ptr, int len) {
        /* simulate a blocking read */
    int num;

    while(len>0) {
        s_poll_init(c->fds);
        s_poll_add(c->fds, fd, 1, 0); /* read */
        switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) {
        case -1:
            sockerror("s_read: s_poll_wait");
            longjmp(c->err, 1); /* error */
        case 0:
            s_log(LOG_INFO, "s_read: s_poll_wait:"
                " TIMEOUTbusy exceeded: sending reset");
            longjmp(c->err, 1); /* timeout */
        case 1:
            break; /* OK */
        default:
            s_log(LOG_ERR, "s_read: s_poll_wait: unknown result");
            longjmp(c->err, 1); /* error */
        }
        num=readsocket(fd, ptr, len);
        switch(num) {
        case -1: /* error */
            sockerror("readsocket (s_read)");
            longjmp(c->err, 1);
        case 0: /* EOF */
            s_log(LOG_ERR, "Unexpected socket close (s_read)");
            longjmp(c->err, 1);
        }
        ptr=(u8 *)ptr+num;
        len-=num;
    }
}
Esempio n. 5
0
void read_blocking(CLI *c, int fd, u8 *ptr, int len) {
        /* simulate a blocking read */
    s_poll_set fds;
    int num;

    while(len>0) {
        s_poll_zero(&fds);
        s_poll_add(&fds, fd, 1, 0); /* read */
        switch(s_poll_wait(&fds, c->opt->timeout_busy)) {
        case -1:
            sockerror("read_blocking: s_poll_wait");
            longjmp(c->err, 1); /* error */
        case 0:
            s_log(LOG_INFO, "read_blocking: s_poll_wait timeout");
            longjmp(c->err, 1); /* timeout */
        case 1:
            break; /* OK */
        default:
            s_log(LOG_ERR, "read_blocking: s_poll_wait unknown result");
            longjmp(c->err, 1); /* error */
        }
        num=readsocket(fd, ptr, len);
        switch(num) {
        case -1: /* error */
            sockerror("readsocket (read_blocking)");
            longjmp(c->err, 1);
        case 0: /* EOF */
            s_log(LOG_ERR, "Unexpected socket close (read_blocking)");
            longjmp(c->err, 1);
        }
        ptr+=num;
        len-=num;
    }
}
Esempio n. 6
0
void write_blocking(CLI *c, int fd, u8 *ptr, int len) {
        /* simulate a blocking write */
    s_poll_set fds;
    int num;

    while(len>0) {
        s_poll_zero(&fds);
        s_poll_add(&fds, fd, 0, 1); /* write */
        switch(s_poll_wait(&fds, c->opt->timeout_busy)) {
        case -1:
            sockerror("write_blocking: s_poll_wait");
            longjmp(c->err, 1); /* error */
        case 0:
            s_log(LOG_INFO, "write_blocking: s_poll_wait timeout");
            longjmp(c->err, 1); /* timeout */
        case 1:
            break; /* OK */
        default:
            s_log(LOG_ERR, "write_blocking: s_poll_wait unknown result");
            longjmp(c->err, 1); /* error */
        }
        num=writesocket(fd, ptr, len);
        switch(num) {
        case -1: /* error */
            sockerror("writesocket (write_blocking)");
            longjmp(c->err, 1);
        }
        ptr+=num;
        len-=num;
    }
}
Esempio n. 7
0
char *fd_getline(CLI *c, int fd) {
    char *line, *tmpline;
    int ptr=0, allocated=32;

    line=str_alloc(allocated);
    for(;;) {
        s_poll_init(c->fds);
        s_poll_add(c->fds, fd, 1, 0); /* read */
        switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) {
        case -1:
            sockerror("fd_getline: s_poll_wait");
            str_free(line);
            longjmp(c->err, 1); /* error */
        case 0:
            s_log(LOG_INFO, "fd_getline: s_poll_wait:"
                " TIMEOUTbusy exceeded: sending reset");
            str_free(line);
            longjmp(c->err, 1); /* timeout */
        case 1:
            break; /* OK */
        default:
            s_log(LOG_ERR, "fd_getline: s_poll_wait: Unknown result");
            str_free(line);
            longjmp(c->err, 1); /* error */
        }
        if(allocated<ptr+1) {
            allocated*=2;
            line=str_realloc(line, allocated);
        }
        switch(readsocket(fd, line+ptr, 1)) {
        case -1: /* error */
            sockerror("fd_getline: readsocket");
            str_free(line);
            longjmp(c->err, 1);
        case 0: /* EOF */
            s_log(LOG_ERR, "fd_getline: Unexpected socket close");
            str_free(line);
            longjmp(c->err, 1);
        }
        if(line[ptr]=='\r')
            continue;
        if(line[ptr]=='\n')
            break;
        if(line[ptr]=='\0')
            break;
        if(++ptr>65536) { /* >64KB --> DoS protection */
            s_log(LOG_ERR, "fd_getline: Line too long");
            str_free(line);
            longjmp(c->err, 1);
        }
    }
    line[ptr]='\0';
    tmpline=str_dup(line);
    safestring(tmpline);
    s_log(LOG_DEBUG, " <- %s", tmpline);
    str_free(tmpline);
    return line;
}
Esempio n. 8
0
int s_connect(CLI *c, SOCKADDR_UNION *addr, socklen_t addrlen) {
    int error;
    char *dst;

    dst=s_ntop(addr, addrlen);
    s_log(LOG_INFO, "s_connect: connecting %s", dst);

    if(!connect(c->fd, &addr->sa, addrlen)) {
        s_log(LOG_NOTICE, "s_connect: connected %s", dst);
        str_free(dst);
        return 0; /* no error -> success (on some OSes over the loopback) */
    }
    error=get_last_socket_error();
    if(error!=S_EINPROGRESS && error!=S_EWOULDBLOCK) {
        s_log(LOG_ERR, "s_connect: connect %s: %s (%d)",
            dst, s_strerror(error), error);
        str_free(dst);
        return -1;
    }

    s_log(LOG_DEBUG, "s_connect: s_poll_wait %s: waiting %d seconds",
        dst, c->opt->timeout_connect);
    s_poll_init(c->fds);
    s_poll_add(c->fds, c->fd, 1, 1);
    switch(s_poll_wait(c->fds, c->opt->timeout_connect, 0)) {
    case -1:
        error=get_last_socket_error();
        s_log(LOG_ERR, "s_connect: s_poll_wait %s: %s (%d)",
            dst, s_strerror(error), error);
        str_free(dst);
        return -1;
    case 0:
        s_log(LOG_ERR, "s_connect: s_poll_wait %s:"
            " TIMEOUTconnect exceeded", dst);
        str_free(dst);
        return -1;
    default:
        error=get_socket_error(c->fd);
        if(error) {
            s_log(LOG_ERR, "s_connect: connect %s: %s (%d)",
               dst, s_strerror(error), error);
            str_free(dst);
            return -1;
        }
        if(s_poll_canwrite(c->fds, c->fd)) {
            s_log(LOG_NOTICE, "s_connect: connected %s", dst);
            str_free(dst);
            return 0; /* success */
        }
        s_log(LOG_ERR, "s_connect: s_poll_wait %s: internal error",
            dst);
        str_free(dst);
        return -1;
    }
    return -1; /* should not be possible */
}
Esempio n. 9
0
static OCSP_RESPONSE *ocsp_get_response(CLI *c, OCSP_REQUEST *req) {
    BIO *bio=NULL;
    OCSP_REQ_CTX *req_ctx=NULL;
    OCSP_RESPONSE *resp=NULL;
    int err;

    /* connect specified OCSP server (responder) */
    c->fd=s_socket(c->opt->ocsp_addr.sa.sa_family, SOCK_STREAM, 0,
        1, "OCSP: socket (auth_user)");
    if(c->fd<0)
        goto cleanup;
    if(connect_blocking(c, &c->opt->ocsp_addr, addr_len(&c->opt->ocsp_addr)))
        goto cleanup;
    bio=BIO_new_fd(c->fd, BIO_NOCLOSE);
    if(!bio)
        goto cleanup;
    s_log(LOG_DEBUG, "OCSP: server connected");

    /* OCSP protocol communication loop */
    req_ctx=OCSP_sendreq_new(bio, c->opt->ocsp_path, req, -1);
    if(!req_ctx) {
        sslerror("OCSP: OCSP_sendreq_new");
        goto cleanup;
    }
    while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) {
        s_poll_init(c->fds);
        s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio));
        err=s_poll_wait(c->fds, c->opt->timeout_busy, 0);
        if(err==-1)
            sockerror("OCSP: s_poll_wait");
        if(err==0)
            s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded");
        if(err<=0)
            goto cleanup;
    }
    /* s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx); */
    /* http://www.mail-archive.com/[email protected]/msg61691.html */
    if(!resp) {
        if(ERR_peek_error())
            sslerror("OCSP: OCSP_sendreq_nbio");
        else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */
            s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error");
    }

cleanup:
    if(req_ctx)
        OCSP_REQ_CTX_free(req_ctx);
    if(bio)
        BIO_free_all(bio);
    if(c->fd>=0) {
        closesocket(c->fd);
        c->fd=-1; /* avoid double close on cleanup */
    }
    return resp;
}
Esempio n. 10
0
static int RFC2487(CLI *c, int fd) {
    s_poll_zero(&c->fds);
    s_poll_add(&c->fds, fd, 1, 0);

    switch(s_poll_wait(&c->fds, 0)) {
    case 0: /* fd not ready to read */
        s_log(LOG_DEBUG, "RFC 2487 detected");
        return 1;
    case 1: /* fd ready to read */
        s_log(LOG_DEBUG, "RFC 2487 not detected");
        return 0;
    default: /* -1 */
        sockerror("RFC2487 (s_poll_wait)");
        return -1;
    }
}
Esempio n. 11
0
void fdgetline(CLI *c, int fd, char *line) {
    char logline[STRLEN];
    s_poll_set fds;
    int ptr;

    for(ptr=0;;) {
        s_poll_zero(&fds);
        s_poll_add(&fds, fd, 1, 0); /* read */
        switch(s_poll_wait(&fds, c->opt->timeout_busy)) {
        case -1:
            sockerror("fdgetline: s_poll_wait");
            longjmp(c->err, 1); /* error */
        case 0:
            s_log(LOG_INFO, "fdgetline: s_poll_wait timeout");
            longjmp(c->err, 1); /* timeout */
        case 1:
            break; /* OK */
        default:
            s_log(LOG_ERR, "fdgetline: s_poll_wait unknown result");
            longjmp(c->err, 1); /* error */
        }
        switch(readsocket(fd, line+ptr, 1)) {
        case -1: /* error */
            sockerror("readsocket (fdgetline)");
            longjmp(c->err, 1);
        case 0: /* EOF */
            s_log(LOG_ERR, "Unexpected socket close (fdgetline)");
            longjmp(c->err, 1);
        }
        if(line[ptr]=='\r')
            continue;
        if(line[ptr]=='\n')
            break;
        if(!line[ptr])
            break;
        if(++ptr==STRLEN) {
            s_log(LOG_ERR, "Input line too long");
            longjmp(c->err, 1);
        }
    }
    line[ptr]='\0';
    safecopy(logline, line);
    safestring(logline);
    s_log(LOG_DEBUG, " <- %s", logline);
}
Esempio n. 12
0
void *client(void *arg) {
    CLI *c=arg;

#ifdef DEBUG_STACK_SIZE
    stack_info(1); /* initialize */
#endif
    s_log(LOG_DEBUG, "%s started", c->opt->servname);
    if(alloc_fd(c->local_rfd.fd))
        return NULL;
    if(c->local_wfd.fd!=c->local_rfd.fd)
        if(alloc_fd(c->local_wfd.fd))
            return NULL;
#ifndef USE_WIN32
    if(c->opt->option.remote && c->opt->option.program)
        c->local_rfd.fd=c->local_wfd.fd=connect_local(c);
            /* connect and exec options specified together */
            /* spawn local program instead of stdio */
#endif
    c->remote_fd.fd=-1;
    c->ssl=NULL;
    cleanup(c, do_client(c));
#ifdef USE_FORK
    if(!c->opt->option.remote) /* 'exec' specified */
        child_status(); /* null SIGCHLD handler was used */
#else
    enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */
    s_log(LOG_DEBUG, "%s finished (%d left)", c->opt->servname,
        --num_clients);
    leave_critical_section(CRIT_CLIENTS);
#endif
    free(c);
#ifdef DEBUG_STACK_SIZE
    stack_info(0); /* display computed value */
#endif
#ifdef USE_WIN32
    _endthread();
#endif
#ifdef USE_UCONTEXT
    s_log(LOG_DEBUG, "Context %ld closed", ready_head->id);
    s_poll_wait(NULL, 0); /* wait on poll() */
    s_log(LOG_ERR, "INTERNAL ERROR: failed to drop context");
#endif
    return NULL;
}
Esempio n. 13
0
NOEXPORT void smtp_server(CLI *c) {
    char *line;

    s_poll_init(c->fds);
    s_poll_add(c->fds, c->local_rfd.fd, 1, 0);
    switch(s_poll_wait(c->fds, 0, 200)) { /* wait up to 200ms */
    case 0: /* fd not ready to read */
        s_log(LOG_DEBUG, "RFC 2487 detected");
        break;
    case 1: /* fd ready to read */
        s_log(LOG_DEBUG, "RFC 2487 not detected");
        return; /* return if RFC 2487 is not used */
    default: /* -1 */
        sockerror("RFC2487 (s_poll_wait)");
        longjmp(c->err, 1);
    }

    line=fd_getline(c, c->remote_fd.fd);
    if(!is_prefix(line, "220")) {
        s_log(LOG_ERR, "Unknown server welcome");
        str_free(line);
        longjmp(c->err, 1);
    }
    fd_printf(c, c->local_wfd.fd, "%s + stunnel", line);
    str_free(line);
    line=fd_getline(c, c->local_rfd.fd);
    if(!is_prefix(line, "EHLO ")) {
        s_log(LOG_ERR, "Unknown client EHLO");
        str_free(line);
        longjmp(c->err, 1);
    }
    fd_printf(c, c->local_wfd.fd, "250-%s Welcome", line);
    fd_putline(c, c->local_wfd.fd, "250 STARTTLS");
    str_free(line);
    line=fd_getline(c, c->local_rfd.fd);
    if(!is_prefix(line, "STARTTLS")) {
        s_log(LOG_ERR, "STARTTLS expected");
        str_free(line);
        longjmp(c->err, 1);
    }
    fd_putline(c, c->local_wfd.fd, "220 Go ahead");
    str_free(line);
}
Esempio n. 14
0
void *client_thread(void *arg) {
    CLI *c=arg;

#ifdef DEBUG_STACK_SIZE
    stack_info(1); /* initialize */
#endif
    client_main(c);
#ifdef DEBUG_STACK_SIZE
    stack_info(0); /* display computed value */
#endif
    str_stats();
    str_cleanup();
    /* s_log() is not allowed after str_cleanup() */
#if defined(USE_WIN32) && !defined(_WIN32_WCE)
    _endthread();
#endif
#ifdef USE_UCONTEXT
    s_poll_wait(NULL, 0, 0); /* wait on poll() */
#endif
    return NULL;
}
Esempio n. 15
0
File: client.c Progetto: l7s/stunnel
void *client(void *arg) {
    CLI *c=arg;

#ifdef DEBUG_STACK_SIZE
    stack_info(1); /* initialize */
#endif
    s_log(LOG_DEBUG, "Service %s started", c->opt->servname);
    if(c->opt->option.remote && c->opt->option.program) {
            /* connect and exec options specified together */
            /* -> spawn a local program instead of stdio */
        while((c->local_rfd.fd=c->local_wfd.fd=connect_local(c))>=0) {
            run_client(c);
            if(!c->opt->option.retry)
                break;
            sleep(1); /* FIXME: not a good idea in ucontext threading */
            str_stats();
            str_cleanup();
        }
    } else
        run_client(c);
    /* str_free() cannot be used here, because corresponding
       calloc() is called from a different thread */
    free(c);
#ifdef DEBUG_STACK_SIZE
    stack_info(0); /* display computed value */
#endif
#ifdef USE_UCONTEXT
    s_log(LOG_DEBUG, "Context %ld closed", ready_head->id);
#endif
    str_stats();
    str_cleanup();
    /* s_log() is not allowed after str_cleanup() */
#if defined(USE_WIN32) && !defined(_WIN32_WCE)
    _endthread();
#endif
#ifdef USE_UCONTEXT
    s_poll_wait(NULL, 0, 0); /* wait on poll() */
#endif
    return NULL;
}
Esempio n. 16
0
File: client.c Progetto: l7s/stunnel
/****************************** transfer data */
static void transfer(CLI *c) {
    int watchdog=0; /* a counter to detect an infinite loop */
    int num, err;
    /* logical channels (not file descriptors!) open for read or write */
    int sock_open_rd=1, sock_open_wr=1, ssl_open_rd=1, ssl_open_wr=1;
    /* awaited conditions on SSL file descriptors */
    int shutdown_wants_read=0, shutdown_wants_write=0;
    int read_wants_read, read_wants_write=0;
    int write_wants_read=0, write_wants_write;
    /* actual conditions on file descriptors */
    int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr;

    c->sock_ptr=c->ssl_ptr=0;

    do { /* main loop of client data transfer */
        /****************************** initialize *_wants_* */
        read_wants_read=
            ssl_open_rd && c->ssl_ptr<BUFFSIZE && !read_wants_write;
        write_wants_write=
            ssl_open_wr && c->sock_ptr && !write_wants_read;

        /****************************** setup c->fds structure */
        s_poll_init(&c->fds); /* initialize the structure */
        /* for plain socket open data strem = open file descriptor */
        /* make sure to add each open socket to receive exceptions! */
        if(sock_open_rd)
            s_poll_add(&c->fds, c->sock_rfd->fd, c->sock_ptr<BUFFSIZE, 0);
        if(sock_open_wr)
            s_poll_add(&c->fds, c->sock_wfd->fd, 0, c->ssl_ptr);
        /* for SSL assume that sockets are open if there any pending requests */
        if(read_wants_read || write_wants_read || shutdown_wants_read)
            s_poll_add(&c->fds, c->ssl_rfd->fd, 1, 0);
        if(read_wants_write || write_wants_write || shutdown_wants_write)
            s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1);

        /****************************** wait for an event */
        err=s_poll_wait(&c->fds,
            (sock_open_rd && ssl_open_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, 0);
        switch(err) {
        case -1:
            sockerror("transfer: s_poll_wait");
            longjmp(c->err, 1);
        case 0: /* timeout */
            if((sock_open_rd && ssl_open_rd) || c->ssl_ptr || c->sock_ptr) {
                s_log(LOG_INFO, "transfer: s_poll_wait:"
                    " TIMEOUTidle exceeded: sending reset");
                longjmp(c->err, 1);
            } else { /* already closing connection */
                s_log(LOG_ERR, "transfer: s_poll_wait:"
                    " TIMEOUTclose exceeded: closing");
                return; /* OK */
            }
        }

        /****************************** check for errors on sockets */
        err=s_poll_error(&c->fds, c->sock_rfd->fd);
        if(err) {
            s_log(LOG_NOTICE,
                "Error detected on socket (read) file descriptor: %s (%d)",
                s_strerror(err), err);
            longjmp(c->err, 1);
        }
        if(c->sock_wfd->fd != c->sock_rfd->fd) { /* performance optimization */
            err=s_poll_error(&c->fds, c->sock_wfd->fd);
            if(err) {
                s_log(LOG_NOTICE,
                    "Error detected on socket write file descriptor: %s (%d)",
                    s_strerror(err), err);
                longjmp(c->err, 1);
            }
        }
        err=s_poll_error(&c->fds, c->ssl_rfd->fd);
        if(err) {
            s_log(LOG_NOTICE,
                "Error detected on SSL (read) file descriptor: %s (%d)",
                s_strerror(err), err);
            longjmp(c->err, 1);
        }
        if(c->ssl_wfd->fd != c->ssl_rfd->fd) { /* performance optimization */
            err=s_poll_error(&c->fds, c->ssl_wfd->fd);
            if(err) {
                s_log(LOG_NOTICE,
                    "Error detected on SSL write file descriptor: %s (%d)",
                    s_strerror(err), err);
                longjmp(c->err, 1);
            }
        }

        /****************************** retrieve results from c->fds */
        sock_can_rd=s_poll_canread(&c->fds, c->sock_rfd->fd);
        sock_can_wr=s_poll_canwrite(&c->fds, c->sock_wfd->fd);
        ssl_can_rd=s_poll_canread(&c->fds, c->ssl_rfd->fd);
        ssl_can_wr=s_poll_canwrite(&c->fds, c->ssl_wfd->fd);

        /****************************** checks for internal failures */
        /* please report any internal errors to stunnel-users mailing list */
        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);
            longjmp(c->err, 1);
        }
        /* these checks should no longer be needed */
        /* I'm going to remove them soon */
        if(!sock_open_rd && sock_can_rd) {
            err=get_socket_error(c->sock_rfd->fd);
            if(err) { /* really an error? */
                s_log(LOG_ERR, "INTERNAL ERROR: "
                    "Closed socket ready to read: %s (%d)",
                    s_strerror(err), err);
                longjmp(c->err, 1);
            }
            if(c->ssl_ptr) { /* anything left to write */
                s_log(LOG_ERR, "INTERNAL ERROR: "
                    "Closed socket ready to read: sending reset");
                longjmp(c->err, 1);
            }
            s_log(LOG_ERR, "INTERNAL ERROR: "
                "Closed socket ready to read: write close");
            sock_open_wr=0; /* no further write allowed */
            shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */
        }

        /****************************** send SSL close_notify message */
        if(shutdown_wants_read || shutdown_wants_write) {
            shutdown_wants_read=shutdown_wants_write=0;
            num=SSL_shutdown(c->ssl); /* send close_notify */
            if(num<0) /* -1 - not completed */
                err=SSL_get_error(c->ssl, num);
            else /* 0 or 1 - success */
                err=SSL_ERROR_NONE;
            switch(err) {
            case SSL_ERROR_NONE: /* the shutdown was successfully completed */
                s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify");
                break;
            case SSL_ERROR_WANT_WRITE:
                s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying");
                shutdown_wants_write=1;
                break;
            case SSL_ERROR_WANT_READ:
                s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying");
                shutdown_wants_read=1;
                break;
            case SSL_ERROR_SYSCALL: /* socket error */
                parse_socket_error(c, "SSL_shutdown");
                break;
            case SSL_ERROR_SSL: /* SSL error */
                sslerror("SSL_shutdown");
                longjmp(c->err, 1);
            default:
                s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err);
                longjmp(c->err, 1);
            }
        }

        /****************************** read from socket */
        if(sock_open_rd && sock_can_rd) {
            num=readsocket(c->sock_rfd->fd,
                c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr);
            switch(num) {
            case -1:
                parse_socket_error(c, "readsocket");
                break;
            case 0: /* close */
                s_log(LOG_DEBUG, "Socket closed on read");
                sock_open_rd=0;
                break;
            default:
                c->sock_ptr+=num;
                watchdog=0; /* reset watchdog */
            }
        }

        /****************************** write to socket */
        if(sock_open_wr && sock_can_wr) {
            num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr);
            switch(num) {
            case -1: /* error */
                parse_socket_error(c, "writesocket");
                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);
                c->ssl_ptr-=num;
                c->sock_bytes+=num;
                watchdog=0; /* reset watchdog */
            }
        }

        /****************************** update *_wants_* based on new *_ptr */
        /* this update is also required for SSL_pending() to be used */
        read_wants_read=
            ssl_open_rd && c->ssl_ptr<BUFFSIZE && !read_wants_write;
        write_wants_write=
            ssl_open_wr && c->sock_ptr && !write_wants_read;

        /****************************** read from SSL */
        if((read_wants_read && (ssl_can_rd || SSL_pending(c->ssl))) ||
                /* it may be possible to read some pending data after
                 * writesocket() above made some room in c->ssl_buff */
                (read_wants_write && ssl_can_wr)) {
            read_wants_write=0;
            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");
                read_wants_write=1;
                break;
            case SSL_ERROR_WANT_READ: /* nothing unexpected */
                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 on SSL_read "
                                "with %d byte(s) in buffer",
                            c->sock_ptr);
                        longjmp(c->err, 1); /* reset the socket */
                    }
                    s_log(LOG_DEBUG, "SSL socket closed on SSL_read");
                    ssl_open_rd=ssl_open_wr=0; /* buggy peer: no close_notify */
                } else
                    parse_socket_error(c, "SSL_read");
                break;
            case SSL_ERROR_ZERO_RETURN: /* close_notify received */
                s_log(LOG_DEBUG, "SSL closed on SSL_read");
                ssl_open_rd=0;
                if(!strcmp(SSL_get_version(c->ssl), "SSLv2"))
                    ssl_open_wr=0;
                break;
            case SSL_ERROR_SSL:
                sslerror("SSL_read");
                longjmp(c->err, 1);
            default:
                s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err);
                longjmp(c->err, 1);
            }
        }

        /****************************** write to SSL */
        if((write_wants_read && ssl_can_rd) ||
                (write_wants_write && ssl_can_wr)) {
            write_wants_read=0;
            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: /* nothing unexpected */
                break;
            case SSL_ERROR_WANT_READ:
                s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying");
                write_wants_read=1;
                break;
            case SSL_ERROR_WANT_X509_LOOKUP:
                s_log(LOG_DEBUG,
                    "SSL_write returned WANT_X509_LOOKUP: retrying");
                break;
            case SSL_ERROR_SYSCALL: /* socket error */
                if(!num) { /* EOF */
                    if(c->sock_ptr) {
                        s_log(LOG_ERR,
                            "SSL socket closed on SSL_write "
                                "with %d byte(s) in buffer",
                            c->sock_ptr);
                        longjmp(c->err, 1); /* reset the socket */
                    }
                    s_log(LOG_DEBUG, "SSL socket closed on SSL_write");
                    ssl_open_rd=ssl_open_wr=0; /* buggy peer: no close_notify */
                } else
                    parse_socket_error(c, "SSL_write");
                break;
            case SSL_ERROR_ZERO_RETURN: /* close_notify received */
                s_log(LOG_DEBUG, "SSL closed on SSL_write");
                ssl_open_rd=0;
                if(!strcmp(SSL_get_version(c->ssl), "SSLv2"))
                    ssl_open_wr=0;
                break;
            case SSL_ERROR_SSL:
                sslerror("SSL_write");
                longjmp(c->err, 1);
            default:
                s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err);
                longjmp(c->err, 1);
            }
        }

        /****************************** check write shutdown conditions */
        if(sock_open_wr && !ssl_open_rd && !c->ssl_ptr) {
            s_log(LOG_DEBUG, "Sending socket write shutdown");
            sock_open_wr=0; /* no further write allowed */
            shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */
        }
        if(ssl_open_wr && !sock_open_rd && !c->sock_ptr) {
            s_log(LOG_DEBUG, "Sending SSL write shutdown");
            ssl_open_wr=0; /* no further write allowed */
            if(strcmp(SSL_get_version(c->ssl), "SSLv2")) { /* SSLv3, TLSv1 */
                shutdown_wants_write=1; /* 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_open_rd=0; /* no further read allowed */
            }
        }

        /****************************** 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]");
            stunnel_info(LOG_ERR);
            s_log(LOG_ERR, "protocol=%s, SSL_pending=%d",
                SSL_get_version(c->ssl), SSL_pending(c->ssl));
            s_log(LOG_ERR, "sock_open_rd=%s, sock_open_wr=%s, "
                "ssl_open_rd=%s, ssl_open_wr=%s",
                sock_open_rd ? "Y" : "n", sock_open_wr ? "Y" : "n",
                ssl_open_rd ? "Y" : "n", ssl_open_wr ? "Y" : "n");
            s_log(LOG_ERR, "sock_can_rd=%s,  sock_can_wr=%s,  "
                "ssl_can_rd=%s,  ssl_can_wr=%s",
                sock_can_rd ? "Y" : "n", sock_can_wr ? "Y" : "n",
                ssl_can_rd ? "Y" : "n", ssl_can_wr ? "Y" : "n");
            s_log(LOG_ERR, "read_wants_read=%s,     read_wants_write=%s",
                read_wants_read ? "Y" : "n",
                read_wants_write ? "Y" : "n");
            s_log(LOG_ERR, "write_wants_read=%s,    write_wants_write=%s",
                write_wants_read ? "Y" : "n",
                write_wants_write ? "Y" : "n");
            s_log(LOG_ERR, "shutdown_wants_read=%s, shutdown_wants_write=%s",
                shutdown_wants_read ? "Y" : "n",
                shutdown_wants_write ? "Y" : "n");
            s_log(LOG_ERR, "socket input buffer: %d byte(s), "
                "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr);
            longjmp(c->err, 1);
        }

    } while(sock_open_wr || ssl_open_wr ||
        shutdown_wants_read || shutdown_wants_write);
}
Esempio n. 17
0
NOEXPORT OCSP_RESPONSE *ocsp_get_response(CLI *c,
        OCSP_REQUEST *req, char *url) {
    BIO *bio=NULL;
    OCSP_REQ_CTX *req_ctx=NULL;
    OCSP_RESPONSE *resp=NULL;
    int err;
    char *host=NULL, *port=NULL, *path=NULL;
    SOCKADDR_UNION addr;
    int ssl;

    /* parse the OCSP URL */
    if(!OCSP_parse_url(url, &host, &port, &path, &ssl)) {
        s_log(LOG_ERR, "OCSP: Failed to parse the OCSP URL");
        goto cleanup;
    }
    if(ssl) {
        s_log(LOG_ERR, "OCSP: SSL not supported for OCSP"
            " - additional stunnel service needs to be defined");
        goto cleanup;
    }
    memset(&addr, 0, sizeof addr);
    addr.in.sin_family=AF_INET;
    if(!hostport2addr(&addr, host, port)) {
        s_log(LOG_ERR, "OCSP: Failed to resolve the OCSP server address");
        goto cleanup;
    }

    /* connect specified OCSP server (responder) */
    c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "OCSP: socket");
    if(c->fd<0)
        goto cleanup;
    if(s_connect(c, &addr, addr_len(&addr)))
        goto cleanup;
    bio=BIO_new_fd(c->fd, BIO_NOCLOSE);
    if(!bio)
        goto cleanup;
    s_log(LOG_DEBUG, "OCSP: response retrieved");

    /* OCSP protocol communication loop */
    req_ctx=OCSP_sendreq_new(bio, path, req, -1);
    if(!req_ctx) {
        sslerror("OCSP: OCSP_sendreq_new");
        goto cleanup;
    }
    while(OCSP_sendreq_nbio(&resp, req_ctx)==-1) {
        s_poll_init(c->fds);
        s_poll_add(c->fds, c->fd, BIO_should_read(bio), BIO_should_write(bio));
        err=s_poll_wait(c->fds, c->opt->timeout_busy, 0);
        if(err==-1)
            sockerror("OCSP: s_poll_wait");
        if(err==0)
            s_log(LOG_INFO, "OCSP: s_poll_wait: TIMEOUTbusy exceeded");
        if(err<=0)
            goto cleanup;
    }
#if 0
    s_log(LOG_DEBUG, "OCSP: context state: 0x%x", *(int *)req_ctx);
#endif
    /* http://www.mail-archive.com/[email protected]/msg61691.html */
    if(resp) {
        s_log(LOG_DEBUG, "OCSP: request completed");
    } else {
        if(ERR_peek_error())
            sslerror("OCSP: OCSP_sendreq_nbio");
        else /* OpenSSL error: OCSP_sendreq_nbio does not use OCSPerr */
            s_log(LOG_ERR, "OCSP: OCSP_sendreq_nbio: OpenSSL internal error");
    }

cleanup:
    if(req_ctx)
        OCSP_REQ_CTX_free(req_ctx);
    if(bio)
        BIO_free_all(bio);
    if(c->fd>=0) {
        closesocket(c->fd);
        c->fd=-1; /* avoid double close on cleanup */
    }
    if(host)
        OPENSSL_free(host);
    if(port)
        OPENSSL_free(port);
    if(path)
        OPENSSL_free(path);
    return resp;
}
Esempio n. 18
0
/****************************** transfer data */
NOEXPORT void transfer(CLI *c) {
    int watchdog=0; /* a counter to detect an infinite loop */
    int num, err;
    /* logical channels (not file descriptors!) open for read or write */
    int sock_open_rd=1, sock_open_wr=1;
    /* awaited conditions on SSL file descriptors */
    int shutdown_wants_read=0, shutdown_wants_write=0;
    int read_wants_read=0, read_wants_write=0;
    int write_wants_read=0, write_wants_write=0;
    /* actual conditions on file descriptors */
    int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr;

    c->sock_ptr=c->ssl_ptr=0;

    do { /* main loop of client data transfer */
        /****************************** initialize *_wants_* */
        read_wants_read|=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)
            && c->ssl_ptr<BUFFSIZE && !read_wants_write;
        write_wants_write|=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN)
            && c->sock_ptr && !write_wants_read;

        /****************************** setup c->fds structure */
        s_poll_init(c->fds); /* initialize the structure */
        /* for plain socket open data strem = open file descriptor */
        /* make sure to add each open socket to receive exceptions! */
        if(sock_open_rd) /* only poll if the read file descriptor is open */
            s_poll_add(c->fds, c->sock_rfd->fd, c->sock_ptr<BUFFSIZE, 0);
        if(sock_open_wr) /* only poll if the write file descriptor is open */
            s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr);
        /* poll SSL file descriptors unless SSL shutdown was completed */
        if(SSL_get_shutdown(c->ssl)!=
                (SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN)) {
            s_poll_add(c->fds, c->ssl_rfd->fd, 
                read_wants_read || write_wants_read || shutdown_wants_read, 0);
            s_poll_add(c->fds, c->ssl_wfd->fd, 0,
                read_wants_write || write_wants_write || shutdown_wants_write);
        }

        /****************************** wait for an event */
        err=s_poll_wait(c->fds,
            (sock_open_rd && /* both peers open */
                !(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)) ||
            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, 0);
        switch(err) {
        case -1:
            sockerror("transfer: s_poll_wait");
            longjmp(c->err, 1);
        case 0: /* timeout */
            if((sock_open_rd &&
                    !(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)) ||
                    c->ssl_ptr || c->sock_ptr) {
                s_log(LOG_INFO, "transfer: s_poll_wait:"
                    " TIMEOUTidle exceeded: sending reset");
                longjmp(c->err, 1);
            } else { /* already closing connection */
                s_log(LOG_ERR, "transfer: s_poll_wait:"
                    " TIMEOUTclose exceeded: closing");
                return; /* OK */
            }
        }

        /****************************** check for errors on sockets */
        err=s_poll_error(c->fds, c->sock_rfd->fd);
        if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) {
            s_log(LOG_NOTICE, "Read socket error: %s (%d)",
                s_strerror(err), err);
            longjmp(c->err, 1);
        }
        if(c->sock_wfd->fd!=c->sock_rfd->fd) { /* performance optimization */
            err=s_poll_error(c->fds, c->sock_wfd->fd);
            if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) {
                s_log(LOG_NOTICE, "Write socket error: %s (%d)",
                    s_strerror(err), err);
                longjmp(c->err, 1);
            }
        }
        err=s_poll_error(c->fds, c->ssl_rfd->fd);
        if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) {
            s_log(LOG_NOTICE, "SSL socket error: %s (%d)",
                s_strerror(err), err);
            longjmp(c->err, 1);
        }
        if(c->ssl_wfd->fd!=c->ssl_rfd->fd) { /* performance optimization */
            err=s_poll_error(c->fds, c->ssl_wfd->fd);
            if(err && err!=S_EWOULDBLOCK && err!=S_EAGAIN) {
                s_log(LOG_NOTICE, "SSL socket error: %s (%d)",
                    s_strerror(err), err);
                longjmp(c->err, 1);
            }
        }

        /****************************** check for hangup conditions */
        if(s_poll_hup(c->fds, c->sock_rfd->fd)) {
            s_log(LOG_INFO, "Read socket closed (hangup)");
            sock_open_rd=0;
        }
        if(s_poll_hup(c->fds, c->sock_wfd->fd)) {
            if(c->ssl_ptr) {
                s_log(LOG_ERR,
                    "Write socket closed (hangup) with %d unsent byte(s)",
                    c->ssl_ptr);
                longjmp(c->err, 1); /* reset the socket */
            }
            s_log(LOG_INFO, "Write socket closed (hangup)");
            sock_open_wr=0;
        }
        if(s_poll_hup(c->fds, c->ssl_rfd->fd) ||
                s_poll_hup(c->fds, c->ssl_wfd->fd)) {
            /* hangup -> buggy (e.g. Microsoft) peer:
             * SSL socket closed without close_notify alert */
            if(c->sock_ptr || write_wants_write) {
                s_log(LOG_ERR,
                    "SSL socket closed (hangup) with %d unsent byte(s)",
                    c->sock_ptr);
                longjmp(c->err, 1); /* reset the socket */
            }
            s_log(LOG_INFO, "SSL socket closed (hangup)");
            SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
        }

        /****************************** retrieve results from c->fds */
        sock_can_rd=s_poll_canread(c->fds, c->sock_rfd->fd);
        sock_can_wr=s_poll_canwrite(c->fds, c->sock_wfd->fd);
        ssl_can_rd=s_poll_canread(c->fds, c->ssl_rfd->fd);
        ssl_can_wr=s_poll_canwrite(c->fds, c->ssl_wfd->fd);

        /****************************** checks for internal failures */
        /* please report any internal errors to stunnel-users mailing list */
        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);
            longjmp(c->err, 1);
        }

        if(c->reneg_state==RENEG_DETECTED && !c->opt->option.renegotiation) {
            s_log(LOG_ERR, "Aborting due to renegotiation request");
            longjmp(c->err, 1);
        }

        /****************************** send SSL close_notify alert */
        if(shutdown_wants_read || shutdown_wants_write) {
            num=SSL_shutdown(c->ssl); /* send close_notify alert */
            if(num<0) /* -1 - not completed */
                err=SSL_get_error(c->ssl, num);
            else /* 0 or 1 - success */
                err=SSL_ERROR_NONE;
            switch(err) {
            case SSL_ERROR_NONE: /* the shutdown was successfully completed */
                s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify alert");
                shutdown_wants_read=shutdown_wants_write=0;
                break;
            case SSL_ERROR_SYSCALL: /* socket error */
                if(parse_socket_error(c, "SSL_shutdown"))
                    break; /* a non-critical error: retry */
                SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
                shutdown_wants_read=shutdown_wants_write=0;
                break;
            case SSL_ERROR_WANT_WRITE:
                s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying");
                shutdown_wants_read=0;
                shutdown_wants_write=1;
                break;
            case SSL_ERROR_WANT_READ:
                s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying");
                shutdown_wants_read=1;
                shutdown_wants_write=0;
                break;
            case SSL_ERROR_SSL: /* SSL error */
                sslerror("SSL_shutdown");
                longjmp(c->err, 1);
            default:
                s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err);
                longjmp(c->err, 1);
            }
        }

        /****************************** read from socket */
        if(sock_open_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(c, "readsocket"))
                    break; /* a non-critical error: retry */
                sock_open_rd=sock_open_wr=0;
                break;
            case 0: /* close */
                s_log(LOG_INFO, "Read socket closed (readsocket)");
                sock_open_rd=0;
                break;
            default:
                c->sock_ptr+=num;
                watchdog=0; /* reset watchdog */
            }
        }

        /****************************** write to socket */
        if(sock_open_wr && sock_can_wr) {
            num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr);
            switch(num) {
            case -1: /* error */
                if(parse_socket_error(c, "writesocket"))
                    break; /* a non-critical error: retry */
                sock_open_rd=sock_open_wr=0;
                break;
            default:
                memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num);
                c->ssl_ptr-=num;
                c->sock_bytes+=num;
                watchdog=0; /* reset watchdog */
            }
        }

        /****************************** update *_wants_* based on new *_ptr */
        /* this update is also required for SSL_pending() to be used */
        read_wants_read|=!(SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN)
            && c->ssl_ptr<BUFFSIZE && !read_wants_write;
        write_wants_write|=!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN)
            && c->sock_ptr && !write_wants_read;

        /****************************** read from SSL */
        if((read_wants_read && (ssl_can_rd || SSL_pending(c->ssl))) ||
                /* it may be possible to read some pending data after
                 * writesocket() above made some room in c->ssl_buff */
                (read_wants_write && ssl_can_wr)) {
            read_wants_read=0;
            read_wants_write=0;
            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:
                if(num==0)
                    s_log(LOG_DEBUG, "SSL_read returned 0");
                c->ssl_ptr+=num;
                watchdog=0; /* reset watchdog */
                break;
            case SSL_ERROR_WANT_WRITE:
                s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying");
                read_wants_write=1;
                break;
            case SSL_ERROR_WANT_READ: /* is it possible? */
                s_log(LOG_DEBUG, "SSL_read returned WANT_READ: retrying");
                read_wants_read=1;
                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 && parse_socket_error(c, "SSL_read"))
                    break; /* a non-critical error: retry */
                /* EOF -> buggy (e.g. Microsoft) peer:
                 * SSL socket closed without close_notify alert */
                if(c->sock_ptr || write_wants_write) {
                    s_log(LOG_ERR,
                        "SSL socket closed (SSL_read) with %d unsent byte(s)",
                        c->sock_ptr);
                    longjmp(c->err, 1); /* reset the socket */
                }
                s_log(LOG_INFO, "SSL socket closed (SSL_read)");
                SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
                break;
            case SSL_ERROR_ZERO_RETURN: /* close_notify alert received */
                s_log(LOG_INFO, "SSL closed (SSL_read)");
                if(SSL_version(c->ssl)==SSL2_VERSION)
                    SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
                break;
            case SSL_ERROR_SSL:
                sslerror("SSL_read");
                longjmp(c->err, 1);
            default:
                s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err);
                longjmp(c->err, 1);
            }
        }

        /****************************** write to SSL */
        if((write_wants_read && ssl_can_rd) ||
                (write_wants_write && ssl_can_wr)) {
            write_wants_read=0;
            write_wants_write=0;
            num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr);
            switch(err=SSL_get_error(c->ssl, num)) {
            case SSL_ERROR_NONE:
                if(num==0)
                    s_log(LOG_DEBUG, "SSL_write returned 0");
                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: /* buffered data? */
                s_log(LOG_DEBUG, "SSL_write returned WANT_WRITE: retrying");
                write_wants_write=1;
                break;
            case SSL_ERROR_WANT_READ:
                s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying");
                write_wants_read=1;
                break;
            case SSL_ERROR_WANT_X509_LOOKUP:
                s_log(LOG_DEBUG,
                    "SSL_write returned WANT_X509_LOOKUP: retrying");
                break;
            case SSL_ERROR_SYSCALL: /* socket error */
                if(num && parse_socket_error(c, "SSL_write"))
                    break; /* a non-critical error: retry */
                /* EOF -> buggy (e.g. Microsoft) peer:
                 * SSL socket closed without close_notify alert */
                if(c->sock_ptr) { /* TODO: what about buffered data? */
                    s_log(LOG_ERR,
                        "SSL socket closed (SSL_write) with %d unsent byte(s)",
                        c->sock_ptr);
                    longjmp(c->err, 1); /* reset the socket */
                }
                s_log(LOG_INFO, "SSL socket closed (SSL_write)");
                SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
                break;
            case SSL_ERROR_ZERO_RETURN: /* close_notify alert received */
                s_log(LOG_INFO, "SSL closed (SSL_write)");
                if(SSL_version(c->ssl)==SSL2_VERSION)
                    SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
                break;
            case SSL_ERROR_SSL:
                sslerror("SSL_write");
                longjmp(c->err, 1);
            default:
                s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err);
                longjmp(c->err, 1);
            }
        }

        /****************************** check write shutdown conditions */
        if(sock_open_wr && SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN &&
                !c->ssl_ptr) {
            sock_open_wr=0; /* no further write allowed */
            if(!c->sock_wfd->is_socket) {
                s_log(LOG_DEBUG, "Closing the file descriptor");
                sock_open_rd=0; /* file descriptor is ready to be closed */
            } else if(!shutdown(c->sock_wfd->fd, SHUT_WR)) { /* send TCP FIN */
                s_log(LOG_DEBUG, "Sent socket write shutdown");
            } else {
                s_log(LOG_DEBUG, "Failed to send socket write shutdown");
                sock_open_rd=0; /* file descriptor is ready to be closed */
            }
        }
        if(!(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) && !sock_open_rd &&
                !c->sock_ptr && !write_wants_write) {
            if(SSL_version(c->ssl)!=SSL2_VERSION) {
                s_log(LOG_DEBUG, "Sending close_notify alert");
                shutdown_wants_write=1;
            } else { /* no alerts in SSLv2, including the close_notify alert */
                s_log(LOG_DEBUG, "Closing SSLv2 socket");
                if(c->ssl_rfd->is_socket)
                    shutdown(c->ssl_rfd->fd, SHUT_RD); /* notify the kernel */
                if(c->ssl_wfd->is_socket)
                    shutdown(c->ssl_wfd->fd, SHUT_WR); /* send TCP FIN */
                /* notify the OpenSSL library */
                SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
            }
        }

        /****************************** 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]");
            stunnel_info(LOG_ERR);
            s_log(LOG_ERR, "protocol=%s, SSL_pending=%d",
                SSL_get_version(c->ssl), SSL_pending(c->ssl));
            s_log(LOG_ERR, "sock_open_rd=%s, sock_open_wr=%s",
                sock_open_rd ? "Y" : "n", sock_open_wr ? "Y" : "n");
            s_log(LOG_ERR, "SSL_RECEIVED_SHUTDOWN=%s, SSL_SENT_SHUTDOWN=%s",
                SSL_get_shutdown(c->ssl)&SSL_RECEIVED_SHUTDOWN ? "Y" : "n",
                SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN ? "Y" : "n");
            s_log(LOG_ERR, "sock_can_rd=%s, sock_can_wr=%s",
                sock_can_rd ? "Y" : "n", sock_can_wr ? "Y" : "n");
            s_log(LOG_ERR, "ssl_can_rd=%s, ssl_can_wr=%s",
                ssl_can_rd ? "Y" : "n", ssl_can_wr ? "Y" : "n");
            s_log(LOG_ERR, "read_wants_read=%s, read_wants_write=%s",
                read_wants_read ? "Y" : "n", read_wants_write ? "Y" : "n");
            s_log(LOG_ERR, "write_wants_read=%s, write_wants_write=%s",
                write_wants_read ? "Y" : "n", write_wants_write ? "Y" : "n");
            s_log(LOG_ERR, "shutdown_wants_read=%s, shutdown_wants_write=%s",
                shutdown_wants_read ? "Y" : "n",
                shutdown_wants_write ? "Y" : "n");
            s_log(LOG_ERR, "socket input buffer: %d byte(s), "
                "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr);
            longjmp(c->err, 1);
        }

    } while(sock_open_wr || !(SSL_get_shutdown(c->ssl)&SSL_SENT_SHUTDOWN) ||
        shutdown_wants_read || shutdown_wants_write);
}
Esempio n. 19
0
static int init_ssl(CLI *c) {
    int i, err;
    SSL_SESSION *old_session;

    if(!(c->ssl=SSL_new(ctx))) {
        sslerror("SSL_new");
        return -1;
    }
#if SSLEAY_VERSION_NUMBER >= 0x0922
    SSL_set_session_id_context(c->ssl, sid_ctx, strlen(sid_ctx));
#endif
    if(options.option.client) {
        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(options.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(options.option.client)
            i=SSL_connect(c->ssl);
        else
            i=SSL_accept(c->ssl);
        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_zero(&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)) {
            case -1:
                sockerror("init_ssl: s_poll_wait");
                return -1; /* error */
            case 0:
                s_log(LOG_INFO, "init_ssl: s_poll_wait timeout");
                return -1; /* timeout */
            case 1:
                break; /* OK */
            default:
                s_log(LOG_ERR, "init_ssl: s_poll_wait unknown result");
                return -1; /* error */
            }
            continue; /* ok -> retry */
        }
        if(err==SSL_ERROR_SYSCALL) {
            switch(get_last_socket_error()) {
            case EINTR:
            case EAGAIN:
                continue;
            }
        }
        if(options.option.client)
            sslerror("SSL_connect");
        else
            sslerror("SSL_accept");
        return -1;
    }
    if(SSL_session_reused(c->ssl)) {
        s_log(LOG_INFO, "SSL %s: previous session reused",
            options.option.client ? "connected" : "accepted");
    } else { /* a new session was negotiated */
        if(options.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);
    }
    return 0; /* OK */
}
Esempio n. 20
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 */

    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 */
		add_remote_ip_to_header(c);


            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 */
}
Esempio n. 21
0
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;
}
Esempio n. 22
0
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-)");
}
Esempio n. 23
0
static void transfer(CLI * c)
{
	int watchdog = 0;	
	int num, err;
	
	int sock_open_rd = 1, sock_open_wr = 1;
	
	int shutdown_wants_read = 0, shutdown_wants_write = 0;
	int read_wants_read, read_wants_write = 0;
	int write_wants_read = 0, write_wants_write;
	
	int sock_can_rd, sock_can_wr, ssl_can_rd, ssl_can_wr;

	c->sock_ptr = c->ssl_ptr = 0;

	do {			
	
		read_wants_read =
		    !(SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN)
		    && c->ssl_ptr < BUFFSIZE && !read_wants_write;
		write_wants_write =
		    !(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN)
		    && c->sock_ptr && !write_wants_read;

	
		s_poll_init(c->fds);	
		
		
		if (sock_open_rd)
			s_poll_add(c->fds, c->sock_rfd->fd,
				   c->sock_ptr < BUFFSIZE, 0);
		if (sock_open_wr)
			s_poll_add(c->fds, c->sock_wfd->fd, 0, c->ssl_ptr);
		
		if (read_wants_read || write_wants_read || shutdown_wants_read)
			s_poll_add(c->fds, c->ssl_rfd->fd, 1, 0);
		if (read_wants_write || write_wants_write
		    || shutdown_wants_write)
			s_poll_add(c->fds, c->ssl_wfd->fd, 0, 1);

	
		err = s_poll_wait(c->fds, (sock_open_rd &&	
					   !(SSL_get_shutdown(c->ssl) &
					     SSL_RECEIVED_SHUTDOWN))
				  || c->ssl_ptr	
				  ||
				  c->sock_ptr
				   ?
				  c->opt->timeout_idle : c->opt->timeout_close,
				  0);
		switch (err) {
		case -1:
			sockerror("transfer: s_poll_wait");
			longjmp(c->err, 1);
		case 0:	
			if ((sock_open_rd &&
			     !(SSL_get_shutdown(c->ssl) &
			       SSL_RECEIVED_SHUTDOWN)) || c->ssl_ptr
			    || c->sock_ptr) {
				s_log(LOG_INFO,
				      "transfer: s_poll_wait:"
				      " TIMEOUTidle exceeded: sending reset");
				longjmp(c->err, 1);
			} else {	
				s_log(LOG_ERR, "transfer: s_poll_wait:"
				      " TIMEOUTclose exceeded: closing");
				return;	
			}
		}

	
		err = s_poll_error(c->fds, c->sock_rfd);
		if (err) {
			s_log(LOG_NOTICE,
			      "Error detected on socket (read) file descriptor: %s (%d)",
			      s_strerror(err), err);
			longjmp(c->err, 1);
		}
		if (c->sock_wfd->fd != c->sock_rfd->fd) {	
			err = s_poll_error(c->fds, c->sock_wfd);
			if (err) {
				s_log(LOG_NOTICE,
				      "Error detected on socket write file descriptor: %s (%d)",
				      s_strerror(err), err);
				longjmp(c->err, 1);
			}
		}
		err = s_poll_error(c->fds, c->ssl_rfd);
		if (err) {
			s_log(LOG_NOTICE,
			      "Error detected on SSL (read) file descriptor: %s (%d)",
			      s_strerror(err), err);
			longjmp(c->err, 1);
		}
		if (c->ssl_wfd->fd != c->ssl_rfd->fd) {	
			err = s_poll_error(c->fds, c->ssl_wfd);
			if (err) {
				s_log(LOG_NOTICE,
				      "Error detected on SSL write file descriptor: %s (%d)",
				      s_strerror(err), err);
				longjmp(c->err, 1);
			}
		}

	
		sock_can_rd = s_poll_canread(c->fds, c->sock_rfd->fd);
		sock_can_wr = s_poll_canwrite(c->fds, c->sock_wfd->fd);
		ssl_can_rd = s_poll_canread(c->fds, c->ssl_rfd->fd);
		ssl_can_wr = s_poll_canwrite(c->fds, c->ssl_wfd->fd);

	
		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);
			longjmp(c->err, 1);
		}

	
		if (shutdown_wants_read || shutdown_wants_write) {
			num = SSL_shutdown(c->ssl);	
			if (num < 0)	
				err = SSL_get_error(c->ssl, num);
			else	
				err = SSL_ERROR_NONE;
			switch (err) {
			case SSL_ERROR_NONE:	
				s_log(LOG_INFO,
				      "SSL_shutdown successfully sent close_notify alert");
				shutdown_wants_read = shutdown_wants_write = 0;
				break;
			case SSL_ERROR_SYSCALL:	
				if (parse_socket_error(c, "SSL_shutdown"))
					break;	
				SSL_set_shutdown(c->ssl,
						 SSL_SENT_SHUTDOWN |
						 SSL_RECEIVED_SHUTDOWN);
				shutdown_wants_read = shutdown_wants_write = 0;
				break;
			case SSL_ERROR_WANT_WRITE:
				s_log(LOG_DEBUG,
				      "SSL_shutdown returned WANT_WRITE: retrying");
				shutdown_wants_read = 0;
				shutdown_wants_write = 1;
				break;
			case SSL_ERROR_WANT_READ:
				s_log(LOG_DEBUG,
				      "SSL_shutdown returned WANT_READ: retrying");
				shutdown_wants_read = 1;
				shutdown_wants_write = 0;
				break;
			case SSL_ERROR_SSL:	
				sslerror("SSL_shutdown");
				longjmp(c->err, 1);
			default:
				s_log(LOG_ERR,
				      "SSL_shutdown/SSL_get_error returned %d",
				      err);
				longjmp(c->err, 1);
			}
		}

	
		if (sock_open_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(c, "readsocket"))
					break;	
			case 0:	
				s_log(LOG_DEBUG, "Socket closed on read");
				sock_open_rd = 0;
				break;
			default:
				c->sock_ptr += num;
				watchdog = 0;	
			}
		}

	
		if (sock_open_wr && sock_can_wr) {
			num =
			    writesocket(c->sock_wfd->fd, c->ssl_buff,
					c->ssl_ptr);
			switch (num) {
			case -1:	
				if (parse_socket_error(c, "writesocket"))
					break;	
			case 0:
				s_log(LOG_DEBUG, "Socket closed on write");
				sock_open_rd = sock_open_wr = 0;
				break;
			default:
				memmove(c->ssl_buff, c->ssl_buff + num,
					c->ssl_ptr - num);
				c->ssl_ptr -= num;
				c->sock_bytes += num;
				watchdog = 0;	
			}
		}

	
		
		read_wants_read =
		    !(SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN)
		    && c->ssl_ptr < BUFFSIZE && !read_wants_write;
		write_wants_write =
		    !(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN)
		    && c->sock_ptr && !write_wants_read;

	
		if ((read_wants_read && (ssl_can_rd || SSL_pending(c->ssl))) ||
		    (read_wants_write && ssl_can_wr)) {
			read_wants_write = 0;
			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:
				if (num == 0)
					s_log(LOG_DEBUG, "SSL_read returned 0");
				c->ssl_ptr += num;
				watchdog = 0;	
				break;
			case SSL_ERROR_WANT_WRITE:
				s_log(LOG_DEBUG,
				      "SSL_read returned WANT_WRITE: retrying");
				read_wants_write = 1;
				break;
			case SSL_ERROR_WANT_READ:	
				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 && parse_socket_error(c, "SSL_read"))
					break;	
				if (c->sock_ptr) {
					s_log(LOG_ERR,
					      "SSL socket closed on SSL_read with %d unsent byte(s)",
					      c->sock_ptr);
					longjmp(c->err, 1);	
				}
				s_log(LOG_DEBUG,
				      "SSL socket closed on SSL_read");
				SSL_set_shutdown(c->ssl,
						 SSL_SENT_SHUTDOWN |
						 SSL_RECEIVED_SHUTDOWN);
				break;
			case SSL_ERROR_ZERO_RETURN:	
				s_log(LOG_DEBUG, "SSL closed on SSL_read");
				if (SSL_version(c->ssl) == SSL2_VERSION)
					SSL_set_shutdown(c->ssl,
							 SSL_SENT_SHUTDOWN |
							 SSL_RECEIVED_SHUTDOWN);
				break;
			case SSL_ERROR_SSL:
				sslerror("SSL_read");
				longjmp(c->err, 1);
			default:
				s_log(LOG_ERR,
				      "SSL_read/SSL_get_error returned %d",
				      err);
				longjmp(c->err, 1);
			}
		}

	
		if ((write_wants_read && ssl_can_rd) ||
		    (write_wants_write && ssl_can_wr)) {
			write_wants_read = 0;
			num = SSL_write(c->ssl, c->sock_buff, c->sock_ptr);
			switch (err = SSL_get_error(c->ssl, num)) {
			case SSL_ERROR_NONE:
				if (num == 0)
					s_log(LOG_DEBUG,
					      "SSL_write returned 0");
				memmove(c->sock_buff, c->sock_buff + num,
					c->sock_ptr - num);
				c->sock_ptr -= num;
				c->ssl_bytes += num;
				watchdog = 0;	
				break;
			case SSL_ERROR_WANT_WRITE:	
				break;
			case SSL_ERROR_WANT_READ:
				s_log(LOG_DEBUG,
				      "SSL_write returned WANT_READ: retrying");
				write_wants_read = 1;
				break;
			case SSL_ERROR_WANT_X509_LOOKUP:
				s_log(LOG_DEBUG,
				      "SSL_write returned WANT_X509_LOOKUP: retrying");
				break;
			case SSL_ERROR_SYSCALL:	
				if (num && parse_socket_error(c, "SSL_write"))
					break;	
				if (c->sock_ptr) {
					s_log(LOG_ERR,
					      "SSL socket closed on SSL_write with %d unsent byte(s)",
					      c->sock_ptr);
					longjmp(c->err, 1);	
				}
				s_log(LOG_DEBUG,
				      "SSL socket closed on SSL_write");
				SSL_set_shutdown(c->ssl,
						 SSL_SENT_SHUTDOWN |
						 SSL_RECEIVED_SHUTDOWN);
				break;
			case SSL_ERROR_ZERO_RETURN:	
				s_log(LOG_DEBUG, "SSL closed on SSL_write");
				if (SSL_version(c->ssl) == SSL2_VERSION)
					SSL_set_shutdown(c->ssl,
							 SSL_SENT_SHUTDOWN |
							 SSL_RECEIVED_SHUTDOWN);
				break;
			case SSL_ERROR_SSL:
				sslerror("SSL_write");
				longjmp(c->err, 1);
			default:
				s_log(LOG_ERR,
				      "SSL_write/SSL_get_error returned %d",
				      err);
				longjmp(c->err, 1);
			}
		}

	
		if (sock_open_wr
		    && SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN
		    && !c->ssl_ptr) {
			sock_open_wr = 0;	
			if (!c->sock_wfd->is_socket) {
				s_log(LOG_DEBUG,
				      "Closing the socket file descriptor");
				sock_open_rd = 0;	
			} else if (!shutdown(c->sock_wfd->fd, SHUT_WR)) {	
				s_log(LOG_DEBUG, "Sent socket write shutdown");
			} else {
				s_log(LOG_DEBUG,
				      "Failed to send socket write shutdown");
				sock_open_rd = 0;	
			}
		}
		if (!(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN)
		    && !sock_open_rd && !c->sock_ptr) {
			if (SSL_version(c->ssl) != SSL2_VERSION) {	
				s_log(LOG_DEBUG, "Sending close_notify alert");
				shutdown_wants_write = 1;
			} else {	
				s_log(LOG_DEBUG, "Closing SSLv2 socket");
				if (c->ssl_rfd->is_socket)
					shutdown(c->ssl_rfd->fd, SHUT_RD);	
				if (c->ssl_wfd->is_socket)
					shutdown(c->ssl_wfd->fd, SHUT_WR);	
				
				SSL_set_shutdown(c->ssl,
						 SSL_SENT_SHUTDOWN |
						 SSL_RECEIVED_SHUTDOWN);
			}
		}

	
		if (++watchdog > 100) {	
			s_log(LOG_ERR,
			      "transfer() loop executes not transferring any data");
			wifisec_info(LOG_ERR);
			s_log(LOG_ERR, "protocol=%s, SSL_pending=%d",
			      SSL_get_version(c->ssl), SSL_pending(c->ssl));
			s_log(LOG_ERR, "sock_open_rd=%s, sock_open_wr=%s",
			      sock_open_rd ? "Y" : "n",
			      sock_open_wr ? "Y" : "n");
			s_log(LOG_ERR,
			      "SSL_RECEIVED_SHUTDOWN=%s, SSL_SENT_SHUTDOWN=%s",
			      SSL_get_shutdown(c->ssl) & SSL_RECEIVED_SHUTDOWN ?
			      "Y" : "n",
			      SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN ? "Y"
			      : "n");
			s_log(LOG_ERR, "sock_can_rd=%s, sock_can_wr=%s",
			      sock_can_rd ? "Y" : "n", sock_can_wr ? "Y" : "n");
			s_log(LOG_ERR, "ssl_can_rd=%s, ssl_can_wr=%s",
			      ssl_can_rd ? "Y" : "n", ssl_can_wr ? "Y" : "n");
			s_log(LOG_ERR,
			      "read_wants_read=%s, read_wants_write=%s",
			      read_wants_read ? "Y" : "n",
			      read_wants_write ? "Y" : "n");
			s_log(LOG_ERR,
			      "write_wants_read=%s, write_wants_write=%s",
			      write_wants_read ? "Y" : "n",
			      write_wants_write ? "Y" : "n");
			s_log(LOG_ERR,
			      "shutdown_wants_read=%s, shutdown_wants_write=%s",
			      shutdown_wants_read ? "Y" : "n",
			      shutdown_wants_write ? "Y" : "n");
			s_log(LOG_ERR,
			      "socket input buffer: %d byte(s), "
			      "ssl input buffer: %d byte(s)", c->sock_ptr,
			      c->ssl_ptr);
			longjmp(c->err, 1);
		}

	} while (sock_open_wr || !(SSL_get_shutdown(c->ssl) & SSL_SENT_SHUTDOWN)
		 || shutdown_wants_read || shutdown_wants_write);
}
Esempio n. 24
0
NOEXPORT void imap_server(CLI *c) {
    char *line, *id, *tail, *capa;

    s_poll_init(c->fds);
    s_poll_add(c->fds, c->local_rfd.fd, 1, 0);
    switch(s_poll_wait(c->fds, 0, 200)) {
    case 0: /* fd not ready to read */
        s_log(LOG_DEBUG, "RFC 2595 detected");
        break;
    case 1: /* fd ready to read */
        s_log(LOG_DEBUG, "RFC 2595 not detected");
        return; /* return if RFC 2595 is not used */
    default: /* -1 */
        sockerror("RFC2595 (s_poll_wait)");
        longjmp(c->err, 1);
    }

    /* process server welcome and send it to client */
    line=fd_getline(c, c->remote_fd.fd);
    if(!is_prefix(line, "* OK")) {
        s_log(LOG_ERR, "Unknown server welcome");
        str_free(line);
        longjmp(c->err, 1);
    }
    capa=strstr(line, "CAPABILITY");
    if(!capa)
        capa=strstr(line, "capability");
    if(capa)
        *capa='K'; /* disable CAPABILITY within greeting */
    fd_printf(c, c->local_wfd.fd, "%s (stunnel)", line);

    id=str_dup("");
    while(1) { /* process client commands */
        str_free(line);
        line=fd_getline(c, c->local_rfd.fd);
        /* split line into id and tail */
        str_free(id);
        id=str_dup(line);
        tail=strchr(id, ' ');
        if(!tail)
            break;
        *tail++='\0';

        if(is_prefix(tail, "STARTTLS")) {
            fd_printf(c, c->local_wfd.fd,
                "%s OK Begin TLS negotiation now", id);
            str_free(line);
            str_free(id);
            return; /* success */
        } else if(is_prefix(tail, "CAPABILITY")) {
            fd_putline(c, c->remote_fd.fd, line); /* send it to server */
            str_free(line);
            line=fd_getline(c, c->remote_fd.fd); /* get the capabilites */
            if(*line=='*') {
                /*
                 * append STARTTLS
                 * should also add LOGINDISABLED, but can't because
                 * of Mozilla bug #324138/#312009
                 * LOGIN would fail as "unexpected command", anyway
                 */
                fd_printf(c, c->local_wfd.fd, "%s STARTTLS", line);
                str_free(line);
                line=fd_getline(c, c->remote_fd.fd); /* next line */
            }
            fd_putline(c, c->local_wfd.fd, line); /* forward to the client */
            tail=strchr(line, ' ');
            if(!tail || !is_prefix(tail+1, "OK")) { /* not OK? */
                fd_putline(c, c->local_wfd.fd,
                    "* BYE unexpected server response");
                s_log(LOG_ERR, "Unexpected server response: %s", line);
                break;
            }
        } else if(is_prefix(tail, "LOGOUT")) {
            fd_putline(c, c->local_wfd.fd, "* BYE server terminating");
            fd_printf(c, c->local_wfd.fd, "%s OK LOGOUT completed", id);
            break;
        } else {
            fd_putline(c, c->local_wfd.fd, "* BYE stunnel: unexpected command");
            fd_printf(c, c->local_wfd.fd, "%s BAD %s unexpected", id, tail);
            s_log(LOG_ERR, "Unexpected client command %s", tail);
            break;
        }
    }
    /* clean server shutdown */
    str_free(id);
    fd_putline(c, c->remote_fd.fd, "stunnel LOGOUT");
    str_free(line);
    line=fd_getline(c, c->remote_fd.fd);
    if(*line=='*') {
        str_free(line);
        line=fd_getline(c, c->remote_fd.fd);
    }
    str_free(line);
    longjmp(c->err, 2); /* don't reset */
}
Esempio n. 25
0
File: client.c Progetto: l7s/stunnel
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);
    }
}
Esempio n. 26
0
static void init_ssl(CLI *c) {
    int i, err;
    SSL_SESSION *old_session;
    int unsafe_openssl;

    c->ssl=SSL_new(c->opt->ctx);
    if(!c->ssl) {
        sslerror("SSL_new");
        longjmp(c->err, 1);
    }
    SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */
    if(c->opt->option.client) {
#ifndef OPENSSL_NO_TLSEXT
        if(c->opt->sni) {
            s_log(LOG_DEBUG, "SNI: host name: %s", c->opt->sni);
            if(!SSL_set_tlsext_host_name(c->ssl, c->opt->sni)) {
                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 sense 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);
    }

    unsafe_openssl=SSLeay()<0x0090810fL ||
        (SSLeay()>=0x10000000L && SSLeay()<0x1000002fL);
    while(1) {
        /* critical section for OpenSSL version < 0.9.8p or 1.x.x < 1.0.0b *
         * this critical section is a crude workaround for CVE-2010-3864   *
         * see http://www.securityfocus.com/bid/44884 for details          *
         * alternative solution is to disable internal session caching     *
         * NOTE: this critical section also covers callbacks (e.g. OCSP)   */
        if(unsafe_openssl)
            enter_critical_section(CRIT_SSL);

        if(c->opt->option.client)
            i=SSL_connect(c->ssl);
        else
            i=SSL_accept(c->ssl);

        if(unsafe_openssl)
            leave_critical_section(CRIT_SSL);

        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 S_EINTR:
            case S_EWOULDBLOCK:
#if S_EAGAIN!=S_EWOULDBLOCK
            case S_EAGAIN:
#endif
                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 */
#ifdef USE_WIN32
        win_new_chain(c);
#endif
        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);
    }
}
Esempio n. 27
0
static void init_ssl(CLI * c)
{
	int i, err;
	SSL_SESSION *old_session;

	c->ssl = SSL_new(c->opt->ctx);
	if (!c->ssl) {
		sslerror("SSL_new");
		longjmp(c->err, 1);
	}
	SSL_set_ex_data(c->ssl, cli_index, c);	
	if (c->opt->option.client) {
		if (c->opt->session) {
			SSL_set_session(c->ssl, c->opt->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 {
			
			SSL_set_rfd(c->ssl, c->local_rfd.fd);
			SSL_set_wfd(c->ssl, c->local_wfd.fd);
		}
		SSL_set_accept_state(c->ssl);
	}

	
	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 (c->opt->option.client)
			i = SSL_connect(c->ssl);
		else
			i = SSL_accept(c->ssl);

		err = SSL_get_error(c->ssl, i);
		if (err == SSL_ERROR_NONE)
			break;	
		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;	
			default:
				s_log(LOG_ERR,
				      "init_ssl: s_poll_wait: unknown result");
				longjmp(c->err, 1);
			}
			continue;	
		}
		if (err == SSL_ERROR_SYSCALL) {
			switch (get_last_socket_error()) {
			case S_EINTR:
			case S_EWOULDBLOCK:
#if S_EAGAIN!=S_EWOULDBLOCK
			case S_EAGAIN:
#endif
				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 {		
		if (c->opt->option.client) {
			s_log(LOG_INFO,
			      "SSL connected: new session negotiated");
			old_session = c->opt->session;
			c->opt->session = SSL_get1_session(c->ssl);	
			if (old_session)
				SSL_SESSION_free(old_session);	
		} else
			s_log(LOG_INFO, "SSL accepted: new session negotiated");
		print_cipher(c);
	}
}