From 87fcec25bcff00fb9abf448c5d2f49931e7d3ac3 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Thu, 21 Apr 2016 13:26:14 +0000 Subject: [PATCH] implemented outgoing ssl socket --- stio/lib/main.c | 54 +-- stio/lib/stio-prv.h | 2 +- stio/lib/stio-sck.c | 788 +++++++++++++++++++++++++++----------------- stio/lib/stio-sck.h | 32 +- stio/lib/stio.c | 14 +- stio/lib/stio.h | 13 +- 6 files changed, 569 insertions(+), 334 deletions(-) diff --git a/stio/lib/main.c b/stio/lib/main.c index 3502c31..2bd0e59 100644 --- a/stio/lib/main.c +++ b/stio/lib/main.c @@ -121,25 +121,35 @@ typedef struct tcp_server_t tcp_server_t; static void tcp_sck_on_disconnect (stio_dev_sck_t* tcp) { - if (tcp->state & STIO_DEV_SCK_CONNECTING) + switch (STIO_DEV_SCK_GET_PROGRESS(tcp)) { - printf ("TCP DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)tcp->sck); - } - else if (tcp->state & STIO_DEV_SCK_LISTENING) - { - printf ("SHUTTING DOWN THE SERVER SOCKET(%d)...\n", (int)tcp->sck); - } - else if (tcp->state & STIO_DEV_SCK_CONNECTED) - { - printf ("CLIENT ORIGINATING FROM HERE GOT DISCONNECTED(%d).......\n", (int)tcp->sck); - } - else if (tcp->state & STIO_DEV_SCK_ACCEPTED) - { - printf ("CLIENT BEING SERVED GOT DISCONNECTED(%d).......\n", (int)tcp->sck); - } - else - { - printf ("TCP DISCONNECTED - THIS MUST NOT HAPPEN (%d - %x)\n", (int)tcp->sck, (unsigned int)tcp->state); + case STIO_DEV_SCK_CONNECTING: + printf ("OUTGOING TCP DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)tcp->sck); + break; + + case STIO_DEV_SCK_CONNECTING_SSL: + printf ("OUTGOING TCP DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER\n", (int)tcp->sck); + break; + + case STIO_DEV_SCK_LISTENING: + printf ("SHUTTING DOWN THE SERVER SOCKET(%d)...\n", (int)tcp->sck); + break; + + case STIO_DEV_SCK_CONNECTED: + printf ("OUTGOING CLIENT CONNECTION GOT TORN DOWN(%d).......\n", (int)tcp->sck); + break; + + case STIO_DEV_SCK_ACCEPTING_SSL: + printf ("INCOMING SSL-ACCEPT GOT DISCONNECTED(%d) ....\n", (int)tcp->sck); + break; + + case STIO_DEV_SCK_ACCEPTED: + printf ("INCOMING CLIENT BEING SERVED GOT DISCONNECTED(%d).......\n", (int)tcp->sck); + break; + + default: + printf ("TCP DISCONNECTED - THIS MUST NOT HAPPEN (%d - %x)\n", (int)tcp->sck, (unsigned int)tcp->state); + break; } } static int tcp_sck_on_connect (stio_dev_sck_t* tcp) @@ -280,7 +290,7 @@ static stio_t* g_stio; static void handle_signal (int sig) { - if (g_stio) stio_stop (g_stio); + if (g_stio) stio_stop (g_stio, STIO_STOPREQ_TERMINATION); } int main () @@ -361,9 +371,11 @@ int main () in_addr_t ia = inet_addr("192.168.1.119"); stio_sckaddr_initforip4 (&tcp_conn.remoteaddr, 9999, (stio_ip4addr_t*)&ia); } - tcp_conn.tmout.sec = 5; + + stio_inittime (&tcp_conn.tmout, 5, 0); tcp_conn.on_connect = tcp_sck_on_connect; tcp_conn.on_disconnect = tcp_sck_on_disconnect; + tcp_conn.options = STIO_DEV_SCK_CONNECT_SSL; if (stio_dev_sck_connect (tcp[0], &tcp_conn) <= -1) { printf ("stio_dev_sck_connect() failed....\n"); @@ -427,7 +439,7 @@ int main () tcp_bind.options = STIO_DEV_SCK_BIND_REUSEADDR | /*STIO_DEV_SCK_BIND_REUSEPORT |*/ STIO_DEV_SCK_BIND_SSL; tcp_bind.ssl_certfile = STIO_MT("localhost.crt"); tcp_bind.ssl_keyfile = STIO_MT("localhost.key"); - //stio_inittime (&tcp_bind.ssl_accept_tmout, 0, 1); + stio_inittime (&tcp_bind.ssl_accept_tmout, 5, 1); if (stio_dev_sck_bind (tcp[2],&tcp_bind) <= -1) { diff --git a/stio/lib/stio-prv.h b/stio/lib/stio-prv.h index 85d6304..4f181d7 100644 --- a/stio/lib/stio-prv.h +++ b/stio/lib/stio-prv.h @@ -57,7 +57,7 @@ struct stio_t { stio_mmgr_t* mmgr; stio_errnum_t errnum; - int stopreq; /* stop request to abort stio_loop() */ + stio_stopreq_t stopreq; /* stop request to abort stio_loop() */ struct { diff --git a/stio/lib/stio-sck.c b/stio/lib/stio-sck.c index 93d76b2..f34276a 100644 --- a/stio/lib/stio-sck.c +++ b/stio/lib/stio-sck.c @@ -64,6 +64,7 @@ # define USE_SSL #endif + /* ========================================================================= */ void stio_closeasyncsck (stio_t* stio, stio_sckhnd_t sck) { @@ -359,8 +360,10 @@ static int dev_sck_kill (stio_dev_t* dev, int force) if (IS_STATEFUL(rdev)) { - if (rdev->state & (STIO_DEV_SCK_ACCEPTED | STIO_DEV_SCK_CONNECTED | STIO_DEV_SCK_CONNECTING | STIO_DEV_SCK_LISTENING)) + if (STIO_DEV_SCK_GET_PROGRESS(rdev)) { + /* for STIO_DEV_SCK_CONNECTING, STIO_DEV_SCK_CONNECTING_SSL, and STIO_DEV_ACCEPTING_SSL + * on_disconnect() is called without corresponding on_connect() */ if (rdev->on_disconnect) rdev->on_disconnect (rdev); } @@ -473,7 +476,6 @@ static int dev_sck_read_stateless (stio_dev_t* dev, void* buf, stio_iolen_t* len static int dev_sck_write_stateful (stio_dev_t* dev, const void* data, stio_iolen_t* len, const stio_devaddr_t* dstaddr) { stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; - #if defined(USE_SSL) if (rdev->ssl) @@ -561,6 +563,98 @@ static int dev_sck_write_stateless (stio_dev_t* dev, const void* data, stio_iole return 1; } +#if defined(USE_SSL) +static int connect_ssl (stio_dev_sck_t* dev) +{ + int ret; + + STIO_ASSERT (dev->ssl_ctx); + + if (!dev->ssl) + { + SSL* ssl; + + ssl = SSL_new (dev->ssl_ctx); + if (!ssl) + { + dev->stio->errnum = STIO_ESYSERR; + return -1; + } + + if (SSL_set_fd (ssl, dev->sck) == 0) + { + SSL_free (ssl); + dev->stio->errnum = STIO_ESYSERR; + return -1; + } + + SSL_set_read_ahead (ssl, 0); + dev->ssl = ssl; + } + + ret = SSL_connect (dev->ssl); + if (ret <= 0) + { + int err = SSL_get_error (dev->ssl, ret); + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) + { + /* handshaking isn't complete */ + return 0; + } + + dev->stio->errnum = STIO_ESYSERR; + return -1; + } + + return 1; /* connected */ +} + + +static int accept_ssl (stio_dev_sck_t* dev) +{ + int ret; + + STIO_ASSERT (dev->ssl_ctx); + + if (!dev->ssl) + { + SSL* ssl; + + ssl = SSL_new (dev->ssl_ctx); + if (!ssl) + { + dev->stio->errnum = STIO_ESYSERR; + return -1; + } + + if (SSL_set_fd (ssl, dev->sck) == 0) + { + dev->stio->errnum = STIO_ESYSERR; + return -1; + } + SSL_set_read_ahead (ssl, 0); + + dev->ssl = ssl; + } + + ret = SSL_accept ((SSL*)dev->ssl); + if (ret <= 0) + { + int err = SSL_get_error (dev->ssl, ret); + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) + { + /* handshaking isn't complete */ + return 0; + } + + dev->stio->errnum = STIO_ESYSERR; + return -1; + } + + return 1; /* accepted */ +} +#endif + static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg) { stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; @@ -654,8 +748,8 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg) SSL_CTX_check_private_key (ssl_ctx) == 0 /*|| SSL_CTX_use_certificate_chain_file (ssl_ctx, bnd->chainfile) == 0*/) { - rdev->stio->errnum = STIO_ESYSERR; SSL_CTX_free (ssl_ctx); + rdev->stio->errnum = STIO_ESYSERR; return -1; } @@ -699,7 +793,6 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg) int x; #if defined(USE_SSL) SSL_CTX* ssl_ctx = STIO_NULL; - SSL* ssl = STIO_NULL; #endif if (!IS_STATEFUL(rdev)) { @@ -715,25 +808,41 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg) return -1; } + #if defined(USE_SSL) if (conn->options & STIO_DEV_SCK_CONNECT_SSL) { - #if defined(USE_SSL) ssl_ctx = SSL_CTX_new(SSLv23_client_method()); if (!ssl_ctx) { rdev->stio->errnum = STIO_ESYSERR; return -1; } - #endif } + #endif + +/*{ +int flags = fcntl (rdev->sck, F_GETFL); +fcntl (rdev->sck, F_SETFL, flags & ~O_NONBLOCK); +}*/ /* the socket is already non-blocking */ x = connect (rdev->sck, sa, sl); +/*{ +int flags = fcntl (rdev->sck, F_GETFL); +fcntl (rdev->sck, F_SETFL, flags | O_NONBLOCK); +}*/ + if (x == -1) { if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) { - if (stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT) >= 0) + if (stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT) <= -1) + { + /* watcher update failure. it's critical */ + stio_stop (rdev->stio, STIO_STOPREQ_WATCHER_UPDATE_ERROR); + goto oops_connect; + } + else { stio_tmrjob_t tmrjob; @@ -748,58 +857,72 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg) STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID); rdev->tmrjob_index = stio_instmrjob (rdev->stio, &tmrjob); - if (rdev->tmrjob_index == STIO_TMRIDX_INVALID) - { - stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN); - /* event manipulation failure can't be handled properly. so ignore it. - * anyway, it's already in a failure condition */ - return -1; - } + if (rdev->tmrjob_index == STIO_TMRIDX_INVALID) goto oops_connect; } - rdev->state |= STIO_DEV_SCK_CONNECTING; rdev->remoteaddr = conn->remoteaddr; rdev->on_connect = conn->on_connect; rdev->on_disconnect = conn->on_disconnect; #if defined(USE_SSL) rdev->ssl_ctx = ssl_ctx; #endif - - ssl = SSL_new (ssl_ctx); - if (!ssl) - { - } - - SSL_set_fd (ssl, rdev->sck); - - if (SSL_connect(ssl) <= 0) - { - } - + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTING); return 0; } } rdev->stio->errnum = stio_syserrtoerrnum(errno); + + oops_connect: + if (stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_UPDATE, STIO_DEV_EVENT_IN) <= -1) + { + /* watcher update failure. it's critical */ + stio_stop (rdev->stio, STIO_STOPREQ_WATCHER_UPDATE_ERROR); + } + #if defined(USE_SSL) if (ssl_ctx) SSL_CTX_free (ssl_ctx); #endif return -1; } + else + { + /* connected immediately */ + + rdev->remoteaddr = conn->remoteaddr; + rdev->on_connect = conn->on_connect; + rdev->on_disconnect = conn->on_disconnect; + #if defined(USE_SSL) + rdev->ssl_ctx = ssl_ctx; + #endif - /* connected immediately */ - rdev->state |= STIO_DEV_SCK_CONNECTED; - rdev->remoteaddr = conn->remoteaddr; - rdev->on_connect = conn->on_connect; - rdev->on_disconnect = conn->on_disconnect; - #if defined(USE_SSL) - rdev->ssl_ctx = ssl_ctx; - #endif + sl = STIO_SIZEOF(localaddr); + if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &sl) == 0) rdev->localaddr = localaddr; - sl = STIO_SIZEOF(localaddr); - if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &sl) == 0) rdev->localaddr = localaddr; - - return 0; + #if defined(USE_SSL) + if (ssl_ctx) + { + int x; + x = connect_ssl (rdev); + if (x <= -1) return -1; + if (x == 0) + { + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTING_SSL); +/* TODO: schedule a ssl-connecting timeout job */ + } + else goto connect_ok; + } + else + { + connect_ok: + #endif + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTED); + if (rdev->on_connect (rdev) <= -1) return -1; + #if defined(USE_SSL) + } + #endif + return 0; + } } case STIO_DEV_SCK_LISTEN: @@ -820,7 +943,7 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg) return -1; } - rdev->state |= STIO_DEV_SCK_LISTENING; + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_LISTENING); rdev->on_connect = lstn->on_connect; rdev->on_disconnect = lstn->on_disconnect; return 0; @@ -866,6 +989,210 @@ static stio_dev_mth_t dev_mth_clisck = }; /* ========================================================================= */ +static int harvest_outgoing_connection (stio_dev_sck_t* rdev) +{ + int errcode; + stio_scklen_t len; + + STIO_ASSERT (!(rdev->state & STIO_DEV_SCK_CONNECTED)); + + len = STIO_SIZEOF(errcode); + if (getsockopt (rdev->sck, SOL_SOCKET, SO_ERROR, (char*)&errcode, &len) == -1) + { + rdev->stio->errnum = stio_syserrtoerrnum(errno); + return -1; + } + else if (errcode == 0) + { + stio_sckaddr_t localaddr; + stio_scklen_t addrlen; + + /* connected */ + + if (rdev->tmrjob_index != STIO_TMRIDX_INVALID) + { + stio_deltmrjob (rdev->stio, rdev->tmrjob_index); + STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID); + } + + addrlen = STIO_SIZEOF(localaddr); + if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &addrlen) == 0) rdev->localaddr = localaddr; + + if (stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_RENEW, 0) <= -1) + { + /* watcher update failure. it's critical */ + stio_stop (rdev->stio, STIO_STOPREQ_WATCHER_RENEW_ERROR); + return -1; + } + + #if defined(USE_SSL) + if (rdev->ssl_ctx) + { + int x; + STIO_ASSERT (!rdev->ssl); /* must not be SSL-connected yet */ + + x = connect_ssl (rdev); + if (x <= -1) return -1; + if (x == 0) + { + /* not SSL-connected */ + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTING_SSL); +/* TODO: schedule ssl_connect timeout job */ + return 0; + } + else + { + goto ssl_connected; + } + } + else + { + ssl_connected: + #endif + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTED); + if (rdev->on_connect (rdev) <= -1) return -1; + #if defined(USE_SSL) + } + #endif + + return 0; + } + else if (errcode == EINPROGRESS || errcode == EWOULDBLOCK) + { + /* still in progress */ + return 0; + } + else + { + rdev->stio->errnum = stio_syserrtoerrnum(errcode); + return -1; + } +} + +static int accept_incoming_connection (stio_dev_sck_t* rdev) +{ + stio_sckhnd_t clisck; + stio_sckaddr_t remoteaddr; + stio_scklen_t addrlen; + stio_dev_sck_t* clidev; + + /* this is a server(lisening) socket */ + + addrlen = STIO_SIZEOF(remoteaddr); + clisck = accept (rdev->sck, (struct sockaddr*)&remoteaddr, &addrlen); + if (clisck == STIO_SCKHND_INVALID) + { + if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) return 0; + if (errno == EINTR) return 0; /* if interrupted by a signal, treat it as if it's EINPROGRESS */ + + rdev->stio->errnum = stio_syserrtoerrnum(errno); + return -1; + } + + /* use rdev->dev_size when instantiating a client sck device + * instead of STIO_SIZEOF(stio_dev_sck_t). therefore, the + * extension area as big as that of the master sck device + * is created in the client sck device */ + clidev = (stio_dev_sck_t*)stio_makedev (rdev->stio, rdev->dev_size, &dev_mth_clisck, rdev->dev_evcb, &clisck); + if (!clidev) + { + close (clisck); + return -1; + } + + STIO_ASSERT (clidev->sck == clisck); + + clidev->dev_capa |= STIO_DEV_CAPA_IN | STIO_DEV_CAPA_OUT | STIO_DEV_CAPA_STREAM | STIO_DEV_CAPA_OUT_QUEUED; + clidev->remoteaddr = remoteaddr; + + addrlen = STIO_SIZEOF(clidev->localaddr); + if (getsockname(clisck, (struct sockaddr*)&clidev->localaddr, &addrlen) == -1) clidev->localaddr = rdev->localaddr; + +#if defined(SO_ORIGINAL_DST) + /* if REDIRECT is used, SO_ORIGINAL_DST returns the original + * destination address. When REDIRECT is not used, it returnes + * the address of the local socket. In this case, it should + * be same as the result of getsockname(). */ + addrlen = STIO_SIZEOF(clidev->orgdstaddr); + if (getsockopt (clisck, SOL_IP, SO_ORIGINAL_DST, &clidev->orgdstaddr, &addrlen) == -1) clidev->orgdstaddr = rdev->localaddr; +#else + clidev->orgdstaddr = rdev->localaddr; +#endif + + if (!stio_equalsckaddrs (rdev->stio, &clidev->orgdstaddr, &clidev->localaddr)) + { + clidev->state |= STIO_DEV_SCK_INTERCEPTED; + } + else if (stio_getsckaddrport (&clidev->localaddr) != stio_getsckaddrport(&rdev->localaddr)) + { + /* When TPROXY is used, getsockname() and SO_ORIGNAL_DST return + * the same addresses. however, the port number may be different + * as a typical TPROXY rule is set to change the port number. + * However, this check is fragile if the server port number is + * set to 0. + * + * Take note that the above assumption gets wrong if the TPROXY + * rule doesn't change the port number. so it won't be able + * to handle such a TPROXYed packet without port transformation. */ + clidev->state |= STIO_DEV_SCK_INTERCEPTED; + } + #if 0 + else if ((clidev->initial_ifindex = resolve_ifindex (fd, clidev->localaddr)) <= -1) + { + /* the local_address is not one of a local address. + * it's probably proxied. */ + clidev->state |= STIO_DEV_SCK_INTERCEPTED; + } + #endif + + /* inherit some event handlers from the parent. + * you can still change them inside the on_connect handler */ + clidev->on_connect = rdev->on_connect; + clidev->on_disconnect = rdev->on_disconnect; + clidev->on_write = rdev->on_write; + clidev->on_read = rdev->on_read; + + STIO_ASSERT (clidev->tmrjob_index == STIO_TMRIDX_INVALID); + + if (rdev->ssl_ctx) + { + STIO_DEV_SCK_SET_PROGRESS (clidev, STIO_DEV_SCK_ACCEPTING_SSL); + STIO_ASSERT (clidev->state & STIO_DEV_SCK_ACCEPTING_SSL); + /* actual SSL acceptance must be completed in the client device */ + + /* let the client device know the SSL context to use */ + clidev->ssl_ctx = rdev->ssl_ctx; + + if (stio_ispostime(&rdev->ssl_accept_tmout)) + { + stio_tmrjob_t tmrjob; + + STIO_MEMSET (&tmrjob, 0, STIO_SIZEOF(tmrjob)); + tmrjob.ctx = clidev; + stio_gettime (&tmrjob.when); + stio_addtime (&tmrjob.when, &rdev->ssl_accept_tmout, &tmrjob.when); + + tmrjob.handler = ssl_accept_timedout; + tmrjob.idxptr = &clidev->tmrjob_index; + + clidev->tmrjob_index = stio_instmrjob (clidev->stio, &tmrjob); + if (clidev->tmrjob_index == STIO_TMRIDX_INVALID) + { + /* TODO: call a warning/error callback */ + /* timer job scheduling failed. halt the device */ + stio_dev_halt ((stio_dev_t*)clidev); + } + } + } + else + { + STIO_DEV_SCK_SET_PROGRESS (clidev, STIO_DEV_SCK_ACCEPTED); + if (clidev->on_connect(clidev) <= -1) stio_dev_sck_halt (clidev); + } + + return 0; +} + static int dev_evcb_sck_ready_stateful (stio_dev_t* dev, int events) { stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; @@ -892,286 +1219,151 @@ static int dev_evcb_sck_ready_stateful (stio_dev_t* dev, int events) } /* this socket can connect */ - if (rdev->state & STIO_DEV_SCK_CONNECTING) + switch (STIO_DEV_SCK_GET_PROGRESS(rdev)) { - if (events & STIO_DEV_EVENT_HUP) - { - /* device hang-up */ - rdev->stio->errnum = STIO_EDEVHUP; - return -1; - } - else if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_IN)) - { - /* invalid event masks. generic device error */ - rdev->stio->errnum = STIO_EDEVERR; - return -1; - } - else if (events & STIO_DEV_EVENT_OUT) - { - int errcode; - stio_scklen_t len; - - STIO_ASSERT (!(rdev->state & STIO_DEV_SCK_CONNECTED)); - - len = STIO_SIZEOF(errcode); - if (getsockopt (rdev->sck, SOL_SOCKET, SO_ERROR, (char*)&errcode, &len) == -1) + case STIO_DEV_SCK_CONNECTING: + if (events & STIO_DEV_EVENT_HUP) { - rdev->stio->errnum = stio_syserrtoerrnum(errno); + /* device hang-up */ + rdev->stio->errnum = STIO_EDEVHUP; return -1; } - else if (errcode == 0) + else if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_IN)) { - stio_sckaddr_t localaddr; - stio_scklen_t addrlen; + /* invalid event masks. generic device error */ + rdev->stio->errnum = STIO_EDEVERR; + return -1; + } + else if (events & STIO_DEV_EVENT_OUT) + { + /* when connected, the socket becomes writable */ + return harvest_outgoing_connection (rdev); + } + else + { + return 0; /* success but don't invoke on_read() */ + } - rdev->state &= ~STIO_DEV_SCK_CONNECTING; - rdev->state |= STIO_DEV_SCK_CONNECTED; + case STIO_DEV_SCK_CONNECTING_SSL: + #if defined(USE_SSL) + if (events & STIO_DEV_EVENT_HUP) + { + /* device hang-up */ + rdev->stio->errnum = STIO_EDEVHUP; + return -1; + } + else if (events & STIO_DEV_EVENT_PRI) + { + /* invalid event masks. generic device error */ + rdev->stio->errnum = STIO_EDEVERR; + return -1; + } + else if (events & (STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT)) + { + int x; - if (stio_dev_watch ((stio_dev_t*)rdev, STIO_DEV_WATCH_RENEW, 0) <= -1) return -1; + x = connect_ssl (rdev); + if (x <= -1) return -1; + if (x == 0) return 0; /* not SSL-Connected */ if (rdev->tmrjob_index != STIO_TMRIDX_INVALID) { stio_deltmrjob (rdev->stio, rdev->tmrjob_index); - STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID); + rdev->tmrjob_index = STIO_TMRIDX_INVALID; } - addrlen = STIO_SIZEOF(localaddr); - if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &addrlen) == 0) rdev->localaddr = localaddr; - + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTED); if (rdev->on_connect (rdev) <= -1) return -1; - } - else if (errcode == EINPROGRESS || errcode == EWOULDBLOCK) - { - /* still in progress */ - } - else - { - rdev->stio->errnum = stio_syserrtoerrnum(errcode); - return -1; - } - } - - return 0; /* success but don't invoke on_read() */ - } - else if (rdev->state & STIO_DEV_SCK_LISTENING) - { - if (events & STIO_DEV_EVENT_HUP) - { - /* device hang-up */ - rdev->stio->errnum = STIO_EDEVHUP; - return -1; - } - else if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_OUT)) - { - rdev->stio->errnum = STIO_EDEVERR; - return -1; - } - else if (events & STIO_DEV_EVENT_IN) - { - stio_sckhnd_t clisck; - stio_sckaddr_t remoteaddr; - stio_scklen_t addrlen; - stio_dev_sck_t* clidev; - - /* this is a server(lisening) socket */ - - addrlen = STIO_SIZEOF(remoteaddr); - clisck = accept (rdev->sck, (struct sockaddr*)&remoteaddr, &addrlen); - if (clisck == STIO_SCKHND_INVALID) - { - if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) return 0; - if (errno == EINTR) return 0; /* if interrupted by a signal, treat it as if it's EINPROGRESS */ - - rdev->stio->errnum = stio_syserrtoerrnum(errno); - return -1; - } - - /* use rdev->dev_size when instantiating a client sck device - * instead of STIO_SIZEOF(stio_dev_sck_t). therefore, the - * extension area as big as that of the master sck device - * is created in the client sck device */ - clidev = (stio_dev_sck_t*)stio_makedev (rdev->stio, rdev->dev_size, &dev_mth_clisck, rdev->dev_evcb, &clisck); - if (!clidev) - { - close (clisck); - return -1; - } - - STIO_ASSERT (clidev->sck == clisck); - - clidev->dev_capa |= STIO_DEV_CAPA_IN | STIO_DEV_CAPA_OUT | STIO_DEV_CAPA_STREAM | STIO_DEV_CAPA_OUT_QUEUED; - if (rdev->ssl_ctx) - clidev->state |= STIO_DEV_SCK_ACCEPTING_SSL; - else - clidev->state |= STIO_DEV_SCK_ACCEPTED; - /*clidev->parent = sck;*/ - clidev->remoteaddr = remoteaddr; - - addrlen = STIO_SIZEOF(clidev->localaddr); - if (getsockname(clisck, (struct sockaddr*)&clidev->localaddr, &addrlen) == -1) clidev->localaddr = rdev->localaddr; - - #if defined(SO_ORIGINAL_DST) - /* if REDIRECT is used, SO_ORIGINAL_DST returns the original - * destination address. When REDIRECT is not used, it returnes - * the address of the local socket. In this case, it should - * be same as the result of getsockname(). */ - addrlen = STIO_SIZEOF(clidev->orgdstaddr); - if (getsockopt (clisck, SOL_IP, SO_ORIGINAL_DST, &clidev->orgdstaddr, &addrlen) == -1) clidev->orgdstaddr = rdev->localaddr; - #else - clidev->orgdstaddr = rdev->localaddr; - #endif - - if (!stio_equalsckaddrs (rdev->stio, &clidev->orgdstaddr, &clidev->localaddr)) - { - clidev->state |= STIO_DEV_SCK_INTERCEPTED; - } - else if (stio_getsckaddrport (&clidev->localaddr) != stio_getsckaddrport(&rdev->localaddr)) - { - /* When TPROXY is used, getsockname() and SO_ORIGNAL_DST return - * the same addresses. however, the port number may be different - * as a typical TPROXY rule is set to change the port number. - * However, this check is fragile if the server port number is - * set to 0. - * - * Take note that the above assumption gets wrong if the TPROXY - * rule doesn't change the port number. so it won't be able - * to handle such a TPROXYed packet without port transformation. */ - clidev->state |= STIO_DEV_SCK_INTERCEPTED; - } - #if 0 - else if ((clidev->initial_ifindex = resolve_ifindex (fd, clidev->localaddr)) <= -1) - { - /* the local_address is not one of a local address. - * it's probably proxied. */ - clidev->state |= STIO_DEV_SCK_INTERCEPTED; - } - #endif - - /* inherit some event handlers from the parent. - * you can still change them inside the on_connect handler */ - clidev->on_connect = rdev->on_connect; - clidev->on_disconnect = rdev->on_disconnect; - clidev->on_write = rdev->on_write; - clidev->on_read = rdev->on_read; - - STIO_ASSERT (clidev->tmrjob_index == STIO_TMRIDX_INVALID); - - if (clidev->state & STIO_DEV_SCK_ACCEPTED) - { - STIO_ASSERT (!(clidev->state & STIO_DEV_SCK_ACCEPTING_SSL)); - if (clidev->on_connect(clidev) <= -1) stio_dev_sck_halt (clidev); - } - else - { - - - STIO_ASSERT (clidev->state & STIO_DEV_SCK_ACCEPTING_SSL); - /* actual SSL acceptance must be completed in the client device */ - - /* let the client device know the SSL context to use */ - clidev->ssl_ctx = rdev->ssl_ctx; - - if (stio_ispostime(&rdev->ssl_accept_tmout)) - { - stio_tmrjob_t tmrjob; - - STIO_MEMSET (&tmrjob, 0, STIO_SIZEOF(tmrjob)); - tmrjob.ctx = clidev; - stio_gettime (&tmrjob.when); - stio_addtime (&tmrjob.when, &rdev->ssl_accept_tmout, &tmrjob.when); - - tmrjob.handler = ssl_accept_timedout; - tmrjob.idxptr = &clidev->tmrjob_index; - - clidev->tmrjob_index = stio_instmrjob (clidev->stio, &tmrjob); - if (clidev->tmrjob_index == STIO_TMRIDX_INVALID) - { - /* TODO: call a warning callback */ - printf ("SSL ACCEPT TIMEOUT CAN't BE HONORED....\n"); - } - } - } - - return 0; /* success but don't invoke on_read() */ - } - } - else if (rdev->state & STIO_DEV_SCK_ACCEPTING_SSL) - { - #if defined(USE_SSL) - int ret; - -printf ("SSL IN ACCPEING>.. %p.......................\n", rdev); - /* client socket has been accepted. SSL accpetance is needed here */ - if (!rdev->ssl) - { - SSL* ssl; - -printf ("SSL CREATED.....................\n"); - ssl = SSL_new (rdev->ssl_ctx); - if (!ssl) - { -printf ("SSL ERROR 1>..................... %s\n", ERR_reason_error_string(ERR_get_error())); - rdev->stio->errnum = STIO_ESYSERR; - return -1; - } - - if (SSL_set_fd (ssl, rdev->sck) == 0) - { -printf ("SSL ERROR 2>..................... %s\n", ERR_reason_error_string(ERR_get_error())); - rdev->stio->errnum = STIO_ESYSERR; - return -1; - } - SSL_set_read_ahead (ssl, 0); - - rdev->ssl = ssl; - } - - ret = SSL_accept ((SSL*)rdev->ssl); - if (ret <= 0) - { - int err = SSL_get_error (rdev->ssl, ret); - if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) - { - /* handshaking isn't complete */ return 0; } - -printf ("SSL ERROR 3>..................... %s\n", ERR_reason_error_string(err)); - rdev->stio->errnum = STIO_ESYSERR; + else + { + return 0; /* success. no actual I/O yet */ + } + #else + rdev->stio->errnum = STIO_EINTERN; return -1; - } + #endif -printf ("SSL ACCEPTED.....................\n"); - if (rdev->tmrjob_index != STIO_TMRIDX_INVALID) - { - stio_deltmrjob (rdev->stio, rdev->tmrjob_index); - rdev->tmrjob_index = STIO_TMRIDX_INVALID; - } + case STIO_DEV_SCK_LISTENING: - rdev->state &= ~STIO_DEV_SCK_ACCEPTING_SSL; - rdev->state |= STIO_DEV_SCK_ACCEPTED; - if (rdev->on_connect(rdev) <= -1) stio_dev_sck_halt (rdev); + if (events & STIO_DEV_EVENT_HUP) + { + /* device hang-up */ + rdev->stio->errnum = STIO_EDEVHUP; + return -1; + } + else if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_OUT)) + { + rdev->stio->errnum = STIO_EDEVERR; + return -1; + } + else if (events & STIO_DEV_EVENT_IN) + { + return accept_incoming_connection (rdev); + } + else + { + return 0; /* success but don't invoke on_read() */ + } - return 0; /* no reading or writing yet */ + case STIO_DEV_SCK_ACCEPTING_SSL: + #if defined(USE_SSL) + if (events & STIO_DEV_EVENT_HUP) + { + /* device hang-up */ + rdev->stio->errnum = STIO_EDEVHUP; + return -1; + } + else if (events & STIO_DEV_EVENT_PRI) + { + /* invalid event masks. generic device error */ + rdev->stio->errnum = STIO_EDEVERR; + return -1; + } + else if (events & (STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT)) + { + int x; + x = accept_ssl (rdev); + if (x <= -1) return -1; + if (x <= 0) return 0; /* not SSL-accepted yet */ - #else - rdev->stio->errnum = STIO_EINTERN; - return -1; - #endif + if (rdev->tmrjob_index != STIO_TMRIDX_INVALID) + { + stio_deltmrjob (rdev->stio, rdev->tmrjob_index); + rdev->tmrjob_index = STIO_TMRIDX_INVALID; + } + + STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_ACCEPTED); + if (rdev->on_connect(rdev) <= -1) stio_dev_sck_halt (rdev); + + return 0; + } + else + { + return 0; /* no reading or writing yet */ + } + #else + rdev->stio->errnum = STIO_EINTERN; + return -1; + #endif + + + default: + if (events & STIO_DEV_EVENT_HUP) + { + if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT)) + { + /* probably half-open? */ + return 1; + } + + rdev->stio->errnum = STIO_EDEVHUP; + return -1; + } + + return 1; /* the device is ok. carry on reading or writing */ } - else if (events & STIO_DEV_EVENT_HUP) - { - if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_IN | STIO_DEV_EVENT_OUT)) - { - /* probably half-open? */ - return 1; - } - - rdev->stio->errnum = STIO_EDEVHUP; - return -1; - } - - return 1; /* the device is ok. carry on reading or writing */ } static int dev_evcb_sck_ready_stateless (stio_dev_t* dev, int events) diff --git a/stio/lib/stio-sck.h b/stio/lib/stio-sck.h index 36b8c9e..2e34200 100644 --- a/stio/lib/stio-sck.h +++ b/stio/lib/stio-sck.h @@ -88,14 +88,34 @@ enum stio_dev_sck_ioctl_cmd_t typedef enum stio_dev_sck_ioctl_cmd_t stio_dev_sck_ioctl_cmd_t; +#define STIO_DEV_SCK_SET_PROGRESS(dev,bit) do { \ + (dev)->state &= ~STIO_DEV_SCK_ALL_PROGRESS_BITS; \ + (dev)->state |= (bit); \ +} while(0) + +#define STIO_DEV_SCK_GET_PROGRESS(dev) ((dev)->state & STIO_DEV_SCK_ALL_PROGRESS_BITS) + enum stio_dev_sck_state_t { - STIO_DEV_SCK_CONNECTING = (1 << 0), - STIO_DEV_SCK_CONNECTED = (1 << 1), - STIO_DEV_SCK_LISTENING = (1 << 2), - STIO_DEV_SCK_ACCEPTING_SSL = (1 << 3), - STIO_DEV_SCK_ACCEPTED = (1 << 4), - STIO_DEV_SCK_INTERCEPTED = (1 << 5) + /* the following items(progress bits) are mutually exclusive */ + STIO_DEV_SCK_CONNECTING = (1 << 0), + STIO_DEV_SCK_CONNECTING_SSL = (1 << 1), + STIO_DEV_SCK_CONNECTED = (1 << 2), + STIO_DEV_SCK_LISTENING = (1 << 3), + STIO_DEV_SCK_ACCEPTING_SSL = (1 << 4), + STIO_DEV_SCK_ACCEPTED = (1 << 5), + + /* the following items can be bitwise-ORed with an exclusive item above */ + STIO_DEV_SCK_INTERCEPTED = (1 << 15), + + + /* convenience bit masks */ + STIO_DEV_SCK_ALL_PROGRESS_BITS = (STIO_DEV_SCK_CONNECTING | + STIO_DEV_SCK_CONNECTING_SSL | + STIO_DEV_SCK_CONNECTED | + STIO_DEV_SCK_LISTENING | + STIO_DEV_SCK_ACCEPTING_SSL | + STIO_DEV_SCK_ACCEPTED) }; typedef enum stio_dev_sck_state_t stio_dev_sck_state_t; diff --git a/stio/lib/stio.c b/stio/lib/stio.c index 98dc61d..2812aa9 100644 --- a/stio/lib/stio.c +++ b/stio/lib/stio.c @@ -520,21 +520,21 @@ int stio_exec (stio_t* stio) return n; } -void stio_stop (stio_t* stio) +void stio_stop (stio_t* stio, stio_stopreq_t stopreq) { - stio->stopreq = 1; + stio->stopreq = stopreq; } int stio_loop (stio_t* stio) { if (!stio->actdev.head) return 0; - stio->stopreq = 0; + stio->stopreq = STIO_STOPREQ_NONE; stio->renew_watch = 0; if (stio_prologue (stio) <= -1) return -1; - while (!stio->stopreq && stio->actdev.head) + while (stio->stopreq == STIO_STOPREQ_NONE && stio->actdev.head) { if (stio_exec (stio) <= -1) break; /* you can do other things here */ @@ -619,7 +619,7 @@ oops_after_make: * if the kill method keep returning failure */ while (kill_and_free_device (dev, 1) <= -1) { - if (stio->stopreq) + if (stio->stopreq != STIO_STOPREQ_NONE) { /* i can't wait until destruction attempt gets * fully successful. there is a chance that some @@ -685,7 +685,7 @@ static void kill_zombie_job_handler (stio_t* stio, const stio_ntime_t* now, stio /* i have to choice but to free up the devide by force */ while (kill_and_free_device (dev, 1) <= -1) { - if (stio->stopreq) + if (stio->stopreq != STIO_STOPREQ_NONE) { /* i can't wait until destruction attempt gets * fully successful. there is a chance that some @@ -759,7 +759,7 @@ kill_device: /* i have to choice but to free up the devide by force */ while (kill_and_free_device (dev, 1) <= -1) { - if (stio->stopreq) + if (stio->stopreq != STIO_STOPREQ_NONE) { /* i can't wait until destruction attempt gets * fully successful. there is a chance that some diff --git a/stio/lib/stio.h b/stio/lib/stio.h index 8abd2ff..1d31a42 100644 --- a/stio/lib/stio.h +++ b/stio/lib/stio.h @@ -115,6 +115,15 @@ enum stio_errnum_t typedef enum stio_errnum_t stio_errnum_t; +enum stio_stopreq_t +{ + STIO_STOPREQ_NONE = 0, + STIO_STOPREQ_TERMINATION, + STIO_STOPREQ_WATCHER_UPDATE_ERROR, + STIO_STOPREQ_WATCHER_RENEW_ERROR +}; +typedef enum stio_stopreq_t stio_stopreq_t; + typedef struct stio_tmrjob_t stio_tmrjob_t; typedef stio_size_t stio_tmridx_t; @@ -303,6 +312,7 @@ enum stio_dev_event_t typedef enum stio_dev_event_t stio_dev_event_t; + /* ========================================================================= */ /* TOOD: move these to a separte file */ @@ -435,7 +445,8 @@ STIO_EXPORT int stio_loop ( ); STIO_EXPORT void stio_stop ( - stio_t* stio + stio_t* stio, + stio_stopreq_t stopreq ); STIO_EXPORT stio_dev_t* stio_makedev (