From cb45c6bab6711b3b62ca0ac8ae18a1fdb95fb44a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Wilfried=20G=C3=B6esgens?= Date: Mon, 8 Feb 2010 22:11:29 +0000 Subject: [PATCH] * start migration to buffered I/O --- citadel/context.c | 4 + citadel/context.h | 36 +++--- citadel/modules/crypto/serv_crypto.c | 146 ++++++++++++++++------ citadel/modules/crypto/serv_crypto.h | 4 +- citadel/sysdep.c | 177 ++++++++++++++++----------- 5 files changed, 238 insertions(+), 129 deletions(-) diff --git a/citadel/context.c b/citadel/context.c index 70fdd2795..c20ec98f7 100644 --- a/citadel/context.c +++ b/citadel/context.c @@ -327,6 +327,8 @@ void RemoveContext (CitContext *con) con->ldap_dn = NULL; } + FreeStrBuf(&con->MigrateBuf); + FreeStrBuf(&con->ReadBuf); CtdlLogPrintf(CTDL_DEBUG, "Done with RemoveContext()\n"); } @@ -362,6 +364,8 @@ CitContext *CreateNewContext(void) { * Generate a unique session number and insert this context into * the list. */ + me->MigrateBuf = NewStrBuf(); + me->ReadBuf = NewStrBuf(); begin_critical_section(S_SESSION_TABLE); me->cs_pid = ++next_pid; me->prev = NULL; diff --git a/citadel/context.h b/citadel/context.h index ff62295e7..f9c3aeada 100644 --- a/citadel/context.h +++ b/citadel/context.h @@ -21,19 +21,31 @@ struct CitContext { struct CitContext *prev; /* Link to previous session in list */ struct CitContext *next; /* Link to next session in the list */ - int state; /* thread state (see CON_ values below) */ - int kill_me; /* Set to nonzero to flag for termination */ - int client_socket; int cs_pid; /* session ID */ int dont_term; /* for special activities like artv so we don't get killed */ time_t lastcmd; /* time of last command executed */ time_t lastidle; /* For computing idle time */ + int state; /* thread state (see CON_ values below) */ + int kill_me; /* Set to nonzero to flag for termination */ + + const char *Pos; /* Our read position inside of the ReadBuf */ + StrBuf *ReadBuf; /* Our block buffered read buffer */ + StrBuf *MigrateBuf; /* Our block buffered read buffer */ + int client_socket; + int is_local_socket; /* set to 1 if client is on unix domain sock */ + /* Redirect this session's output to a memory buffer? */ + char *redirect_buffer; /* the buffer */ + size_t redirect_len; /* length of data in buffer */ + size_t redirect_alloc; /* length of allocated buffer */ +#ifdef HAVE_OPENSSL + SSL *ssl; + int redirect_ssl; +#endif char curr_user[USERNAME_SIZE]; /* name of current user */ int logged_in; /* logged in */ int internal_pgm; /* authenticated as internal program */ int nologin; /* not allowed to log in */ - int is_local_socket; /* set to 1 if client is on unix domain sock */ int curr_view; /* The view type for the current user/room */ int is_master; /* Is this session logged in using the master user? */ @@ -41,9 +53,6 @@ struct CitContext { time_t previous_login; /* Date/time of previous login */ char lastcmdname[5]; /* name of last command executed */ unsigned cs_flags; /* miscellaneous flags */ - void (*h_command_function) (void) ; /* service command function */ - void (*h_async_function) (void) ; /* do async msgs function */ - void (*h_greeting_function) (void) ; /* greeting function for session startup */ int is_async; /* Nonzero if client accepts async msgs */ int async_waiting; /* Nonzero if there are async msgs waiting */ int input_waiting; /* Nonzero if there is client input waiting */ @@ -81,14 +90,6 @@ struct CitContext { /* Beginning of cryptography - session nonce */ char cs_nonce[NONCE_SIZE]; /* The nonce for this session's next auth transaction */ - /* Redirect this session's output to a memory buffer? */ - char *redirect_buffer; /* the buffer */ - size_t redirect_len; /* length of data in buffer */ - size_t redirect_alloc; /* length of allocated buffer */ -#ifdef HAVE_OPENSSL - SSL *ssl; - int redirect_ssl; -#endif /* A linked list of all instant messages sent to us. */ struct ExpressMessage *FirstExpressMessage; @@ -111,6 +112,11 @@ struct CitContext { const char *ServiceName; /* readable purpose of this session */ void *openid_data; /* Data stored by the OpenID module */ char *ldap_dn; /* DN of user when using AUTHMODE_LDAP */ + + + void (*h_command_function) (void) ; /* service command function */ + void (*h_async_function) (void) ; /* do async msgs function */ + void (*h_greeting_function) (void) ; /* greeting function for session startup */ }; typedef struct CitContext CitContext; diff --git a/citadel/modules/crypto/serv_crypto.c b/citadel/modules/crypto/serv_crypto.c index 5224aaac8..f323be662 100644 --- a/citadel/modules/crypto/serv_crypto.c +++ b/citadel/modules/crypto/serv_crypto.c @@ -431,62 +431,130 @@ void client_write_ssl(const char *buf, int nbytes) /* - * client_read_ssl() - read data from the encrypted layer. + * read data from the encrypted layer. */ -int client_read_ssl(char *buf, int bytes, int timeout) +int client_read_sslbuffer(StrBuf *buf, int timeout) { -#if 0 - fd_set rfds; - struct timeval tv; - int retval; - int s; -#endif - int len, rlen; + char sbuf[16384]; /* OpenSSL communicates in 16k blocks, so let's speak its native tongue. */ + int rlen; char junk[1]; + SSL *pssl = CC->ssl; - len = 0; - while (len < bytes) { -#if 0 - /* - * This code is disabled because we don't need it when - * using blocking reads (which we are). -IO - */ - FD_ZERO(&rfds); - s = BIO_get_fd(CC->ssl->rbio, NULL); - FD_SET(s, &rfds); - tv.tv_sec = timeout; - tv.tv_usec = 0; - - retval = select(s + 1, &rfds, NULL, NULL, &tv); - - if (FD_ISSET(s, &rfds) == 0) { - return (0); - } + if (pssl == NULL) return(-1); -#endif - if (SSL_want_read(CC->ssl)) { - if ((SSL_write(CC->ssl, junk, 0)) < 1) { - CtdlLogPrintf(CTDL_DEBUG, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error())); + while (1) { + if (SSL_want_read(pssl)) { + if ((SSL_write(pssl, junk, 0)) < 1) { + CtdlLogPrintf(CTDL_DEBUG, "SSL_write in client_read\n"); } } - rlen = SSL_read(CC->ssl, &buf[len], bytes - len); + rlen = SSL_read(pssl, sbuf, sizeof(sbuf)); if (rlen < 1) { long errval; - errval = SSL_get_error(CC->ssl, rlen); - if (errval == SSL_ERROR_WANT_READ || - errval == SSL_ERROR_WANT_WRITE) { + errval = SSL_get_error(pssl, rlen); + if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) { sleep(1); continue; } CtdlLogPrintf(CTDL_DEBUG, "SSL_read got error %ld\n", errval); endtls(); - return (client_read_to - (&buf[len], bytes - len, timeout)); + return (-1); + } + StrBufAppendBufPlain(buf, sbuf, rlen, 0); + return rlen; + } + return (0); +} + +int client_readline_sslbuffer(StrBuf *Target, StrBuf *Buffer, int timeout) +{ + int ntries = 0; + const char *pch, *pchs; + int rlen, len, retval = 0; + CitContext *CCC = CC; + + if (StrLength(Target) > 0) { + pchs = ChrPtr(Buffer); + pch = strchr(pchs, '\n'); + if (pch != NULL) { + rlen = 0; + len = pch - pchs; + if (len > 0 && (*(pch - 1) == '\r') ) + rlen ++; + StrBufSub(Target, Buffer, 0, len - rlen); + StrBufCutLeft(Buffer, len + 1); + return len - rlen; + } + } + + while ((retval == 0) && (CCC->ssl != NULL)) { + pch = NULL; + pchs = ChrPtr(Buffer); + if (*pchs != '\0') + pch = strchr(pchs, '\n'); + if (pch == NULL) { + retval = client_read_sslbuffer(Buffer, timeout); + pchs = ChrPtr(Buffer); + pch = strchr(pchs, '\n'); + } + if (retval == 0) { + sleep(1); + ntries ++; + } + if (ntries > 10) + return 0; + } + if ((retval > 0) && (pch != NULL)) { + rlen = 0; + len = pch - pchs; + if (len > 0 && (*(pch - 1) == '\r') ) + rlen ++; + StrBufSub(Target, Buffer, 0, len - rlen); + StrBufCutLeft(Buffer, len + 1); + return len - rlen; + + } + else + return -1; +} + + + +int client_read_sslblob(StrBuf *Target, long bytes, int timeout) +{ + long bufremain; + long baselen; + int retval; + CitContext *CCC = CC; + + baselen = StrLength(Target); + + if (CCC->Pos == NULL) + CCC->Pos = ChrPtr(CCC->ReadBuf); + bufremain = StrLength(CCC->ReadBuf) - + (CCC->Pos - ChrPtr(CCC->ReadBuf)); + + if (bytes < bufremain) + bufremain = bytes; + StrBufAppendBufPlain(Target, CCC->Pos, bufremain, 0); + StrBufCutLeft(CCC->ReadBuf, bufremain); + + if (bytes > bufremain) + { + while ((StrLength(CCC->ReadBuf) + StrLength(Target) < bytes + baselen) && + (retval >= 0)) + retval = client_read_sslbuffer(CCC->ReadBuf, timeout); + if (retval >= 0) { + StrBufAppendBuf(Target, CCC->ReadBuf, 0); /* todo: Buf > bytes? */ + return 1; + } + else { + return -1; } - len += rlen; } - return (1); + else + return 1; } diff --git a/citadel/modules/crypto/serv_crypto.h b/citadel/modules/crypto/serv_crypto.h index ff20828be..40c5d4807 100644 --- a/citadel/modules/crypto/serv_crypto.h +++ b/citadel/modules/crypto/serv_crypto.h @@ -15,7 +15,9 @@ void destruct_ssl(void); void init_ssl(void); void client_write_ssl (const char *buf, int nbytes); -int client_read_ssl (char *buf, int bytes, int timeout); +int client_read_sslbuffer(StrBuf *buf, int timeout); +int client_readline_sslbuffer(StrBuf *Target, StrBuf *Buffer, int timeout); +int client_read_sslblob(StrBuf *Target, long want_len, int timeout); void cmd_stls(char *params); void cmd_gtls(char *params); void endtls(void); diff --git a/citadel/sysdep.c b/citadel/sysdep.c index 2891baa21..595d9b5b1 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -436,7 +436,14 @@ void flush_output(void) { #endif } +static void flush_client_inbuf(void) +{ + CitContext *CCC=CC; + + FlushStrBuf(CCC->ReadBuf); + CCC->Pos = NULL; +} /* * client_write() ... Send binary data to the client. @@ -452,6 +459,7 @@ int client_write(const char *buf, int nbytes) CitContext *Ctx; int fdflags; + flush_client_inbuf(); Ctx = CC; if (Ctx->redirect_buffer != NULL) { if ((Ctx->redirect_len + nbytes + 2) >= Ctx->redirect_alloc) { @@ -541,76 +549,69 @@ void cprintf(const char *format, ...) { /* * Read data from the client socket. - * Return values are: - * 1 Requested number of bytes has been read. - * 0 Request timed out. - * -1 The socket is broken. - * If the socket breaks, the session will be terminated. + * + * sock socket fd to read from + * buf buffer to read into + * bytes number of bytes to read + * timeout Number of seconds to wait before timing out + * + * Possible return values: + * 1 Requested number of bytes has been read. + * 0 Request timed out. + * -1 Connection is broken, or other error. */ -INLINE int client_read_backend(char *buf, int bytes, int timeout, CitContext *CCC) +int client_read_blob(StrBuf *Target, int bytes, int timeout) { - int len,rlen; - fd_set rfds; - int fd; - struct timeval tv; - int retval; + CitContext *CCC=CC; + const char *Error; + int retval = 0; #ifdef HAVE_OPENSSL if (CCC->redirect_ssl) { - return (client_read_ssl(buf, bytes, timeout)); + retval = client_read_sslblob(Target, bytes, timeout); } + else #endif - len = 0; - fd = CCC->client_socket; - while(lenkill_me = 1; - return (-1); - } else { - /* can't trust fd's and stuff so we need to re-create them */ - continue; - } - } - else { - CtdlLogPrintf(CTDL_DEBUG, "Failed select() in client_read_to().\n"); - CCC->kill_me = 1; - return (-1); - } - } - - if (FD_ISSET(fd, &rfds) == 0) { - return(0); - } - rlen = read(fd, &buf[len], bytes-len); - if (rlen<1) { - /* The socket has been disconnected! */ - CCC->kill_me = 1; - return(-1); - } - len = len + rlen; + retval = StrBufReadBLOBBuffered(Target, + CCC->ReadBuf, + &CCC->Pos, + &CCC->client_socket, + 1, + bytes, + O_TERM, + &Error); + if (retval < 0) { + CtdlLogPrintf(CTDL_CRIT, + "%s failed: %s\n", + __FUNCTION__, + Error); } - return(1); + return retval; } - int client_read_to(char *buf, int bytes, int timeout) { - return client_read_backend(buf, bytes, timeout, CC); + CitContext *CCC=CC; + int rc; + + rc = client_read_blob(CCC->MigrateBuf, bytes, timeout); + if (rc < 0) + { + *buf = '\0'; + return rc; + } + else + { + memcpy(buf, + ChrPtr(CCC->MigrateBuf), + StrLength(CCC->MigrateBuf) + 1); + FlushStrBuf(CCC->MigrateBuf); + return rc; + } } + /* * Read data from the client socket with default timeout. * (This is implemented in terms of client_read_to() and could be @@ -621,6 +622,37 @@ INLINE int client_read(char *buf, int bytes) return(client_read_to(buf, bytes, config.c_sleeping)); } +int CtdlClientGetLine(StrBuf *Target) +{ + CitContext *CCC=CC; + const char *Error; + int rc; + +#ifdef HAVE_OPENSSL + if (CCC->redirect_ssl) { + return client_readline_sslbuffer(Target, + CCC->ReadBuf, + 1); + } + else +#endif + { + rc = StrBufTCP_read_buffered_line_fast(Target, + CCC->ReadBuf, + &CCC->Pos, + &CCC->client_socket, + 5, + 1, + &Error); + if ((rc < 0) && (Error != NULL)) + CtdlLogPrintf(CTDL_CRIT, + "%s failed: %s\n", + __FUNCTION__, + Error); + return rc; + } +} + /* * client_getln() ... Get a LF-terminated line of text from the client. @@ -631,32 +663,29 @@ int client_getln(char *buf, int bufsize) { int i, retval; CitContext *CCC=CC; + const char *pCh; - /* Read one character at a time. - */ - for (i = 0;;i++) { - retval = client_read_backend(&buf[i], 1, config.c_sleeping, CCC); - if (retval != 1 || buf[i] == '\n' || i == (bufsize-1)) - break; - } - - /* If we got a long line, discard characters until the newline. - */ - if (i == (bufsize-1)) - while (buf[i] != '\n' && retval == 1) - retval = client_read(&buf[i], 1); + retval = CtdlClientGetLine(CCC->MigrateBuf); + i = StrLength(CCC->MigrateBuf); + pCh = ChrPtr(CCC->MigrateBuf); /* Strip the trailing LF, and the trailing CR if present. */ - buf[i] = 0; + if (bufsize <= i) + i = bufsize - 1; while ( (i > 0) - && ( (buf[i - 1]==13) - || ( buf[i - 1]==10)) ) { + && ( (pCh[i - 1]==13) + || ( pCh[i - 1]==10)) ) { i--; - buf[i] = 0; } - if (retval < 0) safestrncpy(&buf[i], "000", bufsize - i); - return(retval); + memcpy(buf, pCh, i); + buf[i] = 0; + + FlushStrBuf(CCC->MigrateBuf); + if (retval < 0) { + safestrncpy(&buf[i], "000", bufsize - i); + } + return(retval >= 0); } -- 2.30.2