diff --git a/lib/sxmplv2.c b/lib/sxmplv2.c index 67bd9db..61d5ea9 100644 --- a/lib/sxmplv2.c +++ b/lib/sxmplv2.c @@ -84,6 +84,108 @@ int __resolvehost(const char *hostname, char *buf, int buf_len, } #endif +/* connection helper, since for accept and connect are the same sets of ops + * using defines here ... ugly again, btw works. + */ + +#define SSLX_ACCEPT 0 +#define SSLX_CONNECT 1 + +static int __conn_negotiate(sxlink_t *co, int way) +{ + int rfd = SSL_get_fd(co->ssl), r; + fd_set readset, writeset; + int read_blocked = 0, read_blocked_on_write = 0; + char em[3]; + + memset(em, 0, sizeof(char)*3); + switch(way) { + case SSLX_ACCEPT: strcpy(em, "AC"); break; + case SSLX_CONNECT: strcpy(em, "CO"); break; + default: + sxlink_log(co, SXERROR_LOG, "[sxmplv2] Negotiate way (accept?connect?) is invalid.\n"); + return -1; + break; + } + + __retry: + + do { + __try_again: + ERR_clear_error(); + if(way == SSLX_ACCEPT) r = SSL_accept(co->ssl); + else r = SSL_connect(co->ssl); + switch(SSL_get_error (co->ssl, r)) { + case SSL_ERROR_NONE: + return r; + break; + case SSL_ERROR_WANT_READ: + /* get prepare to select */ + read_blocked = 1; + break; + case SSL_ERROR_WANT_WRITE: /* here we blocked on write */ + read_blocked_on_write = 1; + break; + case SSL_ERROR_SYSCALL: + if(errno == EAGAIN || errno == EINTR) goto __try_again; + else { + sxlink_log(co, SXERROR_LOG, "[sxmplv2] (%s)SSL syscall error.\n", em); + goto __close_conn; + } + break; + case SSL_ERROR_SSL: + sxlink_log(co, SXERROR_LOG, "[sxmplv2] (%s)SSL error occured:%s. Connection will be closed.\n", + em, ERR_error_string(ERR_get_error(), NULL)); + goto __close_conn; + break; + case SSL_ERROR_ZERO_RETURN: + sxlink_log(co, SXERROR_LOG, "[sxmplv2] (%s)SSL connection is cleary closed.\n", em); + default: + __close_conn: + ERR_free_strings(); + co->flags |= SXMP_CLOSED; + sxlink_log(co, SXERROR_LOG, "[sxmplv2] (%s)Unknown error on %s (errno = %d)\n", + em, co->uuid, errno); + ERR_remove_state(0); + return -1; + } + } while(SSL_pending(co->ssl) && !read_blocked); + + __select_retry: + + if(read_blocked) { + FD_ZERO(&readset); + FD_SET(rfd, &readset); + /* waits until something will be ready to read */ + r = select(rfd + 1, &readset, NULL, NULL, NULL); + if(r < 0) { + if(errno == EINTR || errno == EAGAIN) goto __select_retry; + sxlink_log(co, SXERROR_LOG, "[sxmplv2] (%s)Select (%d) on %s\n", em, errno, co->uuid); + ERR_remove_state(0); + return -1; + } + if(!r) { + sxlink_log(co, SXERROR_LOG, "[sxmplv2] (%s)Nothing to wait for\n", em); + ERR_remove_state(0); + return 0; + } + read_blocked = 0; + if(r && FD_ISSET(rfd, &readset)) goto __retry; /* try to read again */ + } + if(read_blocked_on_write) { /* we was blocked on write */ + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_SET(rfd, &readset); + FD_SET(rfd, &writeset); + + r = select(rfd + 1, &readset, &writeset, NULL, NULL); + read_blocked_on_write = 0; + if(r && FD_ISSET(rfd, &writeset)) goto __retry; + } + + return r; +} + static int __conn_read(sxlink_t *co, void *buf, size_t buf_len) { int rfd = SSL_get_fd(co->ssl), r; @@ -979,7 +1081,7 @@ sxlink_t *sxlink_master_accept(sxhub_t *hub, int sck, struct in_addr *addr) /* ok, now we need to init ssl stuff */ /* check up - do we need to initialize SSL context? */ - r = _sxhub_settls_ctx_s(hub); + r = _sxhub_settls_ctx_s(hub); /* TODO: mark on first failed attempt */ if(r != SXE_SUCCESS) goto __fail; /* now we will create an SSL connection */ @@ -990,7 +1092,8 @@ sxlink_t *sxlink_master_accept(sxhub_t *hub, int sck, struct in_addr *addr) /* set the context to verify ssl connection */ SSL_set_ex_data(link->ssl, ex_ssldata_index, (void *)link); SSL_set_accept_state(link->ssl); - if(SSL_accept(link->ssl) == -1) { r = SXE_EPERM; goto __fail; } /* leak here ? */ + if(__conn_negotiate(link, SSLX_ACCEPT) <= 0) + { r = SXE_EPERM; goto __fail; } /* leak here ? */ /* set connection to the batch mode */ link->flags |= SXMP_BATCHMODE; @@ -1214,7 +1317,7 @@ sxlink_t *sxlink_connect_at(sxhub_t *hub, const char *host, } SSL_set_fd(link->ssl, sck); /* attach connected socket */ SSL_set_connect_state(link->ssl); - if(SSL_connect(link->ssl) == -1) { + if(__conn_negotiate(link, SSLX_CONNECT) <= 0) { r = SXE_EPERM; /* shutdown connection */ goto __fail; @@ -1359,6 +1462,9 @@ sxlink_t *sxlink_connect_at(sxhub_t *hub, const char *host, return NULL; } +#undef SSLX_ACCEPT +#undef SSLX_CONNECT + int sxlink_close(sxlink_t *co) { sxmplv2_head_t mhead;