From c370fc6305af0fc9c37f818d8b88726b899b0d0a Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Wed, 30 Apr 2008 16:35:35 +0200 Subject: server handshake now works with nonblocking sockets --- runtime/Makefile.am | 2 +- runtime/nsd_gtls.c | 9 ++++++- runtime/nsd_gtls.h | 3 ++- runtime/nsd_ptcp.c | 3 --- runtime/nsdsel_gtls.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++-- runtime/rsyslog.h | 1 + 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 571d36ac..381c3ae5 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -128,6 +128,6 @@ pkglib_LTLIBRARIES += lmnsdsel_gtls.la lmnsdsel_gtls_la_SOURCES = nsdsel_gtls.c nsdsel_gtls.h lmnsdsel_gtls_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmnsdsel_gtls_la_LDFLAGS = -module -avoid-version -lmnsdsel_gtls_la_LIBADD = +lmnsdsel_gtls_la_LIBADD = $(gnutls_libs) endif diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index 51d32b6c..d8279aaa 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -365,7 +365,14 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) * on non-blocking sockets. Usually, the handshake will not complete * immediately, so that we need to retry it some time later. */ - CHKgnutls(gnutls_handshake(pNew->sess)); + gnuRet = gnutls_handshake(pNew->sess); + if(gnuRet == GNUTLS_E_AGAIN || gnuRet == GNUTLS_E_INTERRUPTED) { + pNew->rtryCall = gtlsRtry_handshake; + dbgprintf("GnuTLS handshake does not complete immediately - setting to retry (this is OK and normal)\n"); + } else if(gnuRet != 0) { + ABORT_FINALIZE(RS_RET_TLS_HANDSHAKE_ERR); + } + pThis->iMode = 1; /* this session is now in TLS mode! */ *ppNew = (nsd_t*) pNew; diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h index 492a2da1..83e15f29 100644 --- a/runtime/nsd_gtls.h +++ b/runtime/nsd_gtls.h @@ -27,7 +27,8 @@ #include "nsd.h" typedef enum { - gtlsRtry_None = 0 /**< no call needs to be retried */ + gtlsRtry_None = 0, /**< no call needs to be retried */ + gtlsRtry_handshake = 1 } gtlsRtryCall_t; /**< IDs of calls that needs to be retried */ typedef nsd_if_t nsd_gtls_if_t; /* we just *implement* this interface */ diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c index c4fb1cf1..e26347c3 100644 --- a/runtime/nsd_ptcp.c +++ b/runtime/nsd_ptcp.c @@ -273,8 +273,6 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) CHKiRet(FillRemHost(pNew, (struct sockaddr*) &addr)); -#warning "socket set to blocking for tls testing - re-enable non-blocking mode!" -#if 0 /* set the new socket to non-blocking IO -TODO:do we really need to do this here? Do we always want it? */ if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { sockflags |= O_NONBLOCK; @@ -287,7 +285,6 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) dbgprintf("error %d setting fcntl(O_NONBLOCK) on tcp socket %d", errno, iNewSock); ABORT_FINALIZE(RS_RET_IO_ERROR); } -#endif pNew->sock = iNewSock; *ppNew = (nsd_t*) pNew; diff --git a/runtime/nsdsel_gtls.c b/runtime/nsdsel_gtls.c index 7cafec49..ab52999c 100644 --- a/runtime/nsdsel_gtls.c +++ b/runtime/nsdsel_gtls.c @@ -74,7 +74,21 @@ Add(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp) ISOBJ_TYPE_assert(pThis, nsdsel_gtls); ISOBJ_TYPE_assert(pNsdGTLS, nsd_gtls); - iRet = nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, waitOp); + if(pNsdGTLS->iMode == 1) { + if(pNsdGTLS->rtryCall != gtlsRtry_None) { + if(gnutls_record_get_direction(pNsdGTLS->sess) == 0) { + CHKiRet(nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, NSDSEL_RD)); + } else { + CHKiRet(nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, NSDSEL_WR)); + } + FINALIZE; + } + } + + /* if we reach this point, we need no special handling */ + CHKiRet(nsdsel_ptcp.Add(pThis->pTcp, pNsdGTLS->pTcp, waitOp)); + +finalize_it: RETiRet; } @@ -94,6 +108,47 @@ Select(nsdsel_t *pNsdsel, int *piNumReady) } +/* retry an interrupted GTLS operation + * rgerhards, 2008-04-30 + */ +static rsRetVal +doRetry(nsd_gtls_t *pNsd) +{ + DEFiRet; + int gnuRet; + + dbgprintf("GnuTLS requested retry of %d operation - executing\n", pNsd->rtryCall); + + /* We follow a common scheme here: first, we do the systen call and + * then we check the result. So far, the result is checked after the + * switch, because the result check is the same for all calls. Note that + * this may change once we deal with the read and write calls (but + * probably this becomes an issue only when we begin to work on TLS + * for relp). -- rgerhards, 2008-04-30 + */ + switch(pNsd->rtryCall) { + case gtlsRtry_handshake: + gnuRet = gnutls_handshake(pNsd->sess); + break; + default: + assert(0); /* this shall not happen! */ + break; + } + + if(gnuRet == 0) { + pNsd->rtryCall = gtlsRtry_None; /* we are done */ + } else if(gnuRet != GNUTLS_E_AGAIN && gnuRet != GNUTLS_E_INTERRUPTED) { + ABORT_FINALIZE(RS_RET_GNUTLS_ERR); + } + /* if we are interrupted once again (else case), we do not need to + * change our status because we are already setup for retries. + */ + +finalize_it: + RETiRet; +} + + /* check if a socket is ready for IO */ static rsRetVal IsReady(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp, int *pbIsReady) @@ -104,7 +159,20 @@ IsReady(nsdsel_t *pNsdsel, nsd_t *pNsd, nsdsel_waitOp_t waitOp, int *pbIsReady) ISOBJ_TYPE_assert(pThis, nsdsel_gtls); ISOBJ_TYPE_assert(pNsdGTLS, nsd_gtls); - iRet = nsdsel_ptcp.IsReady(pThis->pTcp, pNsdGTLS->pTcp, waitOp, pbIsReady); + if(pNsdGTLS->iMode == 1) { + if(pNsdGTLS->rtryCall != gtlsRtry_None) { + CHKiRet(doRetry(pNsdGTLS)); + /* we used this up for our own internal processing, so the socket + * is not ready from the upper layer point of view. + */ + *pbIsReady = 0; + FINALIZE; + } + } + + CHKiRet(nsdsel_ptcp.IsReady(pThis->pTcp, pNsdGTLS->pTcp, waitOp, pbIsReady)); + +finalize_it: RETiRet; } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 41f4f0c2..c32e190c 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -221,6 +221,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_MAX_LSTN_REACHED = -2080, /**< max nbr of listeners reached, can not create more */ RS_RET_INVAID_DRVR_MODE = -2081, /**< tried to set mode not supported by driver */ RS_RET_DRVRNAME_TOO_LONG = -2082, /**< driver name too long - should never happen */ + RS_RET_TLS_HANDSHAKE_ERR = -2083, /**< TLS handshake failed */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ -- cgit