implemented outgoing ssl socket

This commit is contained in:
hyung-hwan 2016-04-21 13:26:14 +00:00
parent fe82425de3
commit 87fcec25bc
6 changed files with 569 additions and 334 deletions

View File

@ -121,25 +121,35 @@ typedef struct tcp_server_t tcp_server_t;
static void tcp_sck_on_disconnect (stio_dev_sck_t* tcp) 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); case STIO_DEV_SCK_CONNECTING:
} printf ("OUTGOING TCP DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)tcp->sck);
else if (tcp->state & STIO_DEV_SCK_LISTENING) break;
{
printf ("SHUTTING DOWN THE SERVER SOCKET(%d)...\n", (int)tcp->sck); case STIO_DEV_SCK_CONNECTING_SSL:
} printf ("OUTGOING TCP DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER\n", (int)tcp->sck);
else if (tcp->state & STIO_DEV_SCK_CONNECTED) break;
{
printf ("CLIENT ORIGINATING FROM HERE GOT DISCONNECTED(%d).......\n", (int)tcp->sck); case STIO_DEV_SCK_LISTENING:
} printf ("SHUTTING DOWN THE SERVER SOCKET(%d)...\n", (int)tcp->sck);
else if (tcp->state & STIO_DEV_SCK_ACCEPTED) break;
{
printf ("CLIENT BEING SERVED GOT DISCONNECTED(%d).......\n", (int)tcp->sck); case STIO_DEV_SCK_CONNECTED:
} printf ("OUTGOING CLIENT CONNECTION GOT TORN DOWN(%d).......\n", (int)tcp->sck);
else break;
{
printf ("TCP DISCONNECTED - THIS MUST NOT HAPPEN (%d - %x)\n", (int)tcp->sck, (unsigned int)tcp->state); 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) 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) 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 () int main ()
@ -361,9 +371,11 @@ int main ()
in_addr_t ia = inet_addr("192.168.1.119"); in_addr_t ia = inet_addr("192.168.1.119");
stio_sckaddr_initforip4 (&tcp_conn.remoteaddr, 9999, (stio_ip4addr_t*)&ia); 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_connect = tcp_sck_on_connect;
tcp_conn.on_disconnect = tcp_sck_on_disconnect; 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) if (stio_dev_sck_connect (tcp[0], &tcp_conn) <= -1)
{ {
printf ("stio_dev_sck_connect() failed....\n"); 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.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_certfile = STIO_MT("localhost.crt");
tcp_bind.ssl_keyfile = STIO_MT("localhost.key"); 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) if (stio_dev_sck_bind (tcp[2],&tcp_bind) <= -1)
{ {

View File

@ -57,7 +57,7 @@ struct stio_t
{ {
stio_mmgr_t* mmgr; stio_mmgr_t* mmgr;
stio_errnum_t errnum; stio_errnum_t errnum;
int stopreq; /* stop request to abort stio_loop() */ stio_stopreq_t stopreq; /* stop request to abort stio_loop() */
struct struct
{ {

View File

@ -64,6 +64,7 @@
# define USE_SSL # define USE_SSL
#endif #endif
/* ========================================================================= */ /* ========================================================================= */
void stio_closeasyncsck (stio_t* stio, stio_sckhnd_t sck) 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 (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); if (rdev->on_disconnect) rdev->on_disconnect (rdev);
} }
@ -474,7 +477,6 @@ static int dev_sck_write_stateful (stio_dev_t* dev, const void* data, stio_iolen
{ {
stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev;
#if defined(USE_SSL) #if defined(USE_SSL)
if (rdev->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; 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) static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
{ {
stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; 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_check_private_key (ssl_ctx) == 0 /*||
SSL_CTX_use_certificate_chain_file (ssl_ctx, bnd->chainfile) == 0*/) SSL_CTX_use_certificate_chain_file (ssl_ctx, bnd->chainfile) == 0*/)
{ {
rdev->stio->errnum = STIO_ESYSERR;
SSL_CTX_free (ssl_ctx); SSL_CTX_free (ssl_ctx);
rdev->stio->errnum = STIO_ESYSERR;
return -1; return -1;
} }
@ -699,7 +793,6 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
int x; int x;
#if defined(USE_SSL) #if defined(USE_SSL)
SSL_CTX* ssl_ctx = STIO_NULL; SSL_CTX* ssl_ctx = STIO_NULL;
SSL* ssl = STIO_NULL;
#endif #endif
if (!IS_STATEFUL(rdev)) if (!IS_STATEFUL(rdev))
{ {
@ -715,25 +808,41 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
return -1; return -1;
} }
#if defined(USE_SSL)
if (conn->options & STIO_DEV_SCK_CONNECT_SSL) if (conn->options & STIO_DEV_SCK_CONNECT_SSL)
{ {
#if defined(USE_SSL)
ssl_ctx = SSL_CTX_new(SSLv23_client_method()); ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx) if (!ssl_ctx)
{ {
rdev->stio->errnum = STIO_ESYSERR; rdev->stio->errnum = STIO_ESYSERR;
return -1; 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 */ /* the socket is already non-blocking */
x = connect (rdev->sck, sa, sl); 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 (x == -1)
{ {
if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) 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; 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); STIO_ASSERT (rdev->tmrjob_index == STIO_TMRIDX_INVALID);
rdev->tmrjob_index = stio_instmrjob (rdev->stio, &tmrjob); rdev->tmrjob_index = stio_instmrjob (rdev->stio, &tmrjob);
if (rdev->tmrjob_index == STIO_TMRIDX_INVALID) if (rdev->tmrjob_index == STIO_TMRIDX_INVALID) goto oops_connect;
{
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;
}
} }
rdev->state |= STIO_DEV_SCK_CONNECTING;
rdev->remoteaddr = conn->remoteaddr; rdev->remoteaddr = conn->remoteaddr;
rdev->on_connect = conn->on_connect; rdev->on_connect = conn->on_connect;
rdev->on_disconnect = conn->on_disconnect; rdev->on_disconnect = conn->on_disconnect;
#if defined(USE_SSL) #if defined(USE_SSL)
rdev->ssl_ctx = ssl_ctx; rdev->ssl_ctx = ssl_ctx;
#endif #endif
STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTING);
ssl = SSL_new (ssl_ctx);
if (!ssl)
{
}
SSL_set_fd (ssl, rdev->sck);
if (SSL_connect(ssl) <= 0)
{
}
return 0; return 0;
} }
} }
rdev->stio->errnum = stio_syserrtoerrnum(errno); 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 defined(USE_SSL)
if (ssl_ctx) SSL_CTX_free (ssl_ctx); if (ssl_ctx) SSL_CTX_free (ssl_ctx);
#endif #endif
return -1; return -1;
} }
else
{
/* connected immediately */
/* connected immediately */ rdev->remoteaddr = conn->remoteaddr;
rdev->state |= STIO_DEV_SCK_CONNECTED; rdev->on_connect = conn->on_connect;
rdev->remoteaddr = conn->remoteaddr; rdev->on_disconnect = conn->on_disconnect;
rdev->on_connect = conn->on_connect; #if defined(USE_SSL)
rdev->on_disconnect = conn->on_disconnect; rdev->ssl_ctx = ssl_ctx;
#if defined(USE_SSL) #endif
rdev->ssl_ctx = ssl_ctx;
#endif
sl = STIO_SIZEOF(localaddr); sl = STIO_SIZEOF(localaddr);
if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &sl) == 0) rdev->localaddr = 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: case STIO_DEV_SCK_LISTEN:
@ -820,7 +943,7 @@ static int dev_sck_ioctl (stio_dev_t* dev, int cmd, void* arg)
return -1; 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_connect = lstn->on_connect;
rdev->on_disconnect = lstn->on_disconnect; rdev->on_disconnect = lstn->on_disconnect;
return 0; 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) static int dev_evcb_sck_ready_stateful (stio_dev_t* dev, int events)
{ {
stio_dev_sck_t* rdev = (stio_dev_sck_t*)dev; 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 */ /* this socket can connect */
if (rdev->state & STIO_DEV_SCK_CONNECTING) switch (STIO_DEV_SCK_GET_PROGRESS(rdev))
{ {
if (events & STIO_DEV_EVENT_HUP) case STIO_DEV_SCK_CONNECTING:
{ 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)
{ {
rdev->stio->errnum = stio_syserrtoerrnum(errno); /* device hang-up */
rdev->stio->errnum = STIO_EDEVHUP;
return -1; return -1;
} }
else if (errcode == 0) else if (events & (STIO_DEV_EVENT_PRI | STIO_DEV_EVENT_IN))
{ {
stio_sckaddr_t localaddr; /* invalid event masks. generic device error */
stio_scklen_t addrlen; 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; case STIO_DEV_SCK_CONNECTING_SSL:
rdev->state |= STIO_DEV_SCK_CONNECTED; #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) if (rdev->tmrjob_index != STIO_TMRIDX_INVALID)
{ {
stio_deltmrjob (rdev->stio, rdev->tmrjob_index); 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); STIO_DEV_SCK_SET_PROGRESS (rdev, STIO_DEV_SCK_CONNECTED);
if (getsockname (rdev->sck, (struct sockaddr*)&localaddr, &addrlen) == 0) rdev->localaddr = localaddr;
if (rdev->on_connect (rdev) <= -1) return -1; 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; return 0;
} }
else
printf ("SSL ERROR 3>..................... %s\n", ERR_reason_error_string(err)); {
rdev->stio->errnum = STIO_ESYSERR; return 0; /* success. no actual I/O yet */
}
#else
rdev->stio->errnum = STIO_EINTERN;
return -1; return -1;
} #endif
printf ("SSL ACCEPTED.....................\n"); case STIO_DEV_SCK_LISTENING:
if (rdev->tmrjob_index != STIO_TMRIDX_INVALID)
{
stio_deltmrjob (rdev->stio, rdev->tmrjob_index);
rdev->tmrjob_index = STIO_TMRIDX_INVALID;
}
rdev->state &= ~STIO_DEV_SCK_ACCEPTING_SSL; if (events & STIO_DEV_EVENT_HUP)
rdev->state |= STIO_DEV_SCK_ACCEPTED; {
if (rdev->on_connect(rdev) <= -1) stio_dev_sck_halt (rdev); /* 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 if (rdev->tmrjob_index != STIO_TMRIDX_INVALID)
rdev->stio->errnum = STIO_EINTERN; {
return -1; stio_deltmrjob (rdev->stio, rdev->tmrjob_index);
#endif 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) static int dev_evcb_sck_ready_stateless (stio_dev_t* dev, int events)

View File

@ -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; 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 enum stio_dev_sck_state_t
{ {
STIO_DEV_SCK_CONNECTING = (1 << 0), /* the following items(progress bits) are mutually exclusive */
STIO_DEV_SCK_CONNECTED = (1 << 1), STIO_DEV_SCK_CONNECTING = (1 << 0),
STIO_DEV_SCK_LISTENING = (1 << 2), STIO_DEV_SCK_CONNECTING_SSL = (1 << 1),
STIO_DEV_SCK_ACCEPTING_SSL = (1 << 3), STIO_DEV_SCK_CONNECTED = (1 << 2),
STIO_DEV_SCK_ACCEPTED = (1 << 4), STIO_DEV_SCK_LISTENING = (1 << 3),
STIO_DEV_SCK_INTERCEPTED = (1 << 5) 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; typedef enum stio_dev_sck_state_t stio_dev_sck_state_t;

View File

@ -520,21 +520,21 @@ int stio_exec (stio_t* stio)
return n; 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) int stio_loop (stio_t* stio)
{ {
if (!stio->actdev.head) return 0; if (!stio->actdev.head) return 0;
stio->stopreq = 0; stio->stopreq = STIO_STOPREQ_NONE;
stio->renew_watch = 0; stio->renew_watch = 0;
if (stio_prologue (stio) <= -1) return -1; 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; if (stio_exec (stio) <= -1) break;
/* you can do other things here */ /* you can do other things here */
@ -619,7 +619,7 @@ oops_after_make:
* if the kill method keep returning failure */ * if the kill method keep returning failure */
while (kill_and_free_device (dev, 1) <= -1) 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 /* i can't wait until destruction attempt gets
* fully successful. there is a chance that some * 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 */ /* i have to choice but to free up the devide by force */
while (kill_and_free_device (dev, 1) <= -1) 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 /* i can't wait until destruction attempt gets
* fully successful. there is a chance that some * 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 */ /* i have to choice but to free up the devide by force */
while (kill_and_free_device (dev, 1) <= -1) 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 /* i can't wait until destruction attempt gets
* fully successful. there is a chance that some * fully successful. there is a chance that some

View File

@ -115,6 +115,15 @@ enum stio_errnum_t
typedef enum stio_errnum_t 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 struct stio_tmrjob_t stio_tmrjob_t;
typedef stio_size_t stio_tmridx_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; typedef enum stio_dev_event_t stio_dev_event_t;
/* ========================================================================= */ /* ========================================================================= */
/* TOOD: move these to a separte file */ /* TOOD: move these to a separte file */
@ -435,7 +445,8 @@ STIO_EXPORT int stio_loop (
); );
STIO_EXPORT void stio_stop ( STIO_EXPORT void stio_stop (
stio_t* stio stio_t* stio,
stio_stopreq_t stopreq
); );
STIO_EXPORT stio_dev_t* stio_makedev ( STIO_EXPORT stio_dev_t* stio_makedev (