diff options
-rw-r--r-- | include/mailutils/datetime.h | 47 | ||||
-rw-r--r-- | include/mailutils/stream.h | 63 | ||||
-rw-r--r-- | include/mailutils/sys/file_stream.h | 1 | ||||
-rw-r--r-- | libmailutils/diag/errors | 6 | ||||
-rw-r--r-- | libmailutils/stream/file_stream.c | 112 | ||||
-rw-r--r-- | libmailutils/stream/iostream.c | 4 | ||||
-rw-r--r-- | libmailutils/stream/stream.c | 217 | ||||
-rw-r--r-- | libmu_auth/tls.c | 59 |
8 files changed, 424 insertions, 85 deletions
diff --git a/include/mailutils/datetime.h b/include/mailutils/datetime.h index 47447e698..912c748d6 100644 --- a/include/mailutils/datetime.h +++ b/include/mailutils/datetime.h @@ -22,6 +22,7 @@ extern "C" { #endif +#include <sys/time.h> #include <time.h> #include <mailutils/types.h> @@ -107,7 +108,53 @@ int mu_scan_datetime (const char *input, const char *fmt, struct tm *tm, one for outputting and one for scanning: */ #define MU_DATETIME_FORM_RFC822 "%a, %e %b %Y %H:%M:%S %z" #define MU_DATETIME_SCAN_RFC822 "%[%a, %]%e %b %Y %H:%M%[:%S%] %z" + +static inline int +mu_timeval_cmp (struct timeval const *a, struct timeval const *b) +{ + if (a->tv_sec < b->tv_sec) + return -1; + if (a->tv_sec > b->tv_sec) + return 1; + if (a->tv_usec < b->tv_usec) + return -1; + if (a->tv_usec > b->tv_usec) + return 1; + return 0; +} +static inline struct timeval +mu_timeval_add (struct timeval const *a, struct timeval const *b) +{ + struct timeval sum; + + sum.tv_sec = a->tv_sec + b->tv_sec; + sum.tv_usec = a->tv_usec + b->tv_usec; + if (sum.tv_usec >= 1000000) + { + ++sum.tv_sec; + sum.tv_usec -= 1000000; + } + + return sum; +} + +static inline struct timeval +mu_timeval_sub (struct timeval const *a, struct timeval const *b) +{ + struct timeval diff; + + diff.tv_sec = a->tv_sec - b->tv_sec; + diff.tv_usec = a->tv_usec - b->tv_usec; + if (diff.tv_usec < 0) + { + --diff.tv_sec; + diff.tv_usec += 1000000; + } + + return diff; +} + #ifdef __cplusplus } #endif diff --git a/include/mailutils/stream.h b/include/mailutils/stream.h index fd2d555c6..f5c8a673e 100644 --- a/include/mailutils/stream.h +++ b/include/mailutils/stream.h @@ -17,8 +17,9 @@ #ifndef _MAILUTILS_STREAM_H #define _MAILUTILS_STREAM_H -#include <mailutils/types.h> +#include <stdlib.h> #include <stdarg.h> +#include <mailutils/types.h> #ifdef __cplusplus extern "C" { @@ -79,7 +80,10 @@ enum mu_buffer_type #define MU_IOCTL_TLSSTREAM 13 /* TLS stream */ #define MU_IOCTL_WORDWRAPSTREAM 14 /* Word-wrapper stream */ #define MU_IOCTL_TCPSTREAM 15 /* TCP stream */ - + +#define MU_IOCTL_TIMEOUT 16 /* Get or set the I/O timeout value + (struct timeval) */ + /* Opcodes common for various families */ #define MU_IOCTL_OP_GET 0 #define MU_IOCTL_OP_SET 1 @@ -337,13 +341,54 @@ int mu_stream_set_buffer (mu_stream_t stream, enum mu_buffer_type type, int mu_stream_get_buffer (mu_stream_t stream, struct mu_buffer_query *qry); int mu_stream_read (mu_stream_t stream, void *buf, size_t size, size_t *pread); -int mu_stream_readdelim (mu_stream_t stream, char *buf, size_t size, - int delim, size_t *pread); -int mu_stream_readline (mu_stream_t stream, char *buf, size_t size, size_t *pread); -int mu_stream_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, - int delim, size_t *pread); -int mu_stream_getline (mu_stream_t stream, char **pbuf, size_t *psize, - size_t *pread); +int mu_stream_timed_readdelim (mu_stream_t stream, char *buf, size_t size, + int delim, struct timeval *to, size_t *pread); +static inline int +mu_stream_readdelim (mu_stream_t stream, char *buf, size_t size, + int delim, size_t *pread) +{ + return mu_stream_timed_readdelim (stream, buf, size, delim, NULL, pread); +} + +static inline int +mu_stream_timed_readline (mu_stream_t stream, char *buf, size_t size, + struct timeval *tv, + size_t *pread) +{ + return mu_stream_timed_readdelim (stream, buf, size, '\n', tv, pread); +} + +static inline int +mu_stream_readline (mu_stream_t stream, char *buf, size_t size, size_t *pread) +{ + return mu_stream_timed_readline (stream, buf, size, NULL, pread); +} + +int mu_stream_timed_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, + int delim, struct timeval *to, size_t *pread); + +static inline int +mu_stream_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, + int delim, size_t *pread) +{ + return mu_stream_timed_getdelim (stream, pbuf, psize, delim, NULL, pread); +} + +static inline int +mu_stream_timed_getline (mu_stream_t stream, char **pbuf, size_t *psize, + struct timeval *to, + size_t *pread) +{ + return mu_stream_timed_getdelim (stream, pbuf, psize, '\n', to, pread); +} + +static inline int +mu_stream_getline (mu_stream_t stream, char **pbuf, size_t *psize, + size_t *pread) +{ + return mu_stream_timed_getline (stream, pbuf, psize, NULL, pread); +} + int mu_stream_write (mu_stream_t stream, const void *buf, size_t size, size_t *pwrite); int mu_stream_writeline (mu_stream_t stream, const char *buf, size_t size); diff --git a/include/mailutils/sys/file_stream.h b/include/mailutils/sys/file_stream.h index c54f9dc49..52583691e 100644 --- a/include/mailutils/sys/file_stream.h +++ b/include/mailutils/sys/file_stream.h @@ -33,6 +33,7 @@ struct _mu_file_stream int flags; char *filename; void *echo_state; + unsigned io_timeout; }; int _mu_file_stream_create (struct _mu_file_stream **pstream, size_t size, diff --git a/libmailutils/diag/errors b/libmailutils/diag/errors index ca563ec27..54df0b0f1 100644 --- a/libmailutils/diag/errors +++ b/libmailutils/diag/errors @@ -141,3 +141,9 @@ MU_ERR_USER6 _("User-defined error 6") MU_ERR_USER7 _("User-defined error 7") MU_ERR_BASE64 _("invalid base64 input") + +MU_ERR_TIMEOUT _("timed out") +MU_ERR_SET_TIMEOUT _("error setting timeout") + +MU_ERR_WRITE _("write error") +MU_ERR_TLS _("TLS error") diff --git a/libmailutils/stream/file_stream.c b/libmailutils/stream/file_stream.c index f8283b5b8..781fae98c 100644 --- a/libmailutils/stream/file_stream.c +++ b/libmailutils/stream/file_stream.c @@ -22,6 +22,8 @@ #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> +#include <poll.h> + #if HAVE_TERMIOS_H # include <termios.h> #endif @@ -40,7 +42,38 @@ static int fd_read (struct _mu_stream *str, char *buf, size_t size, size_t *pret) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; - ssize_t n = read (fstr->fd, buf, size); + ssize_t n; + + if (fstr->io_timeout > 0) + { + struct pollfd pfd; + pfd.fd = fstr->fd; + pfd.events = POLLIN; + switch (poll (&pfd, 1, fstr->io_timeout)) + { + case 0: + return MU_ERR_TIMEOUT; + + case -1: + return errno; + + case 1: + break; + + default: + /* Should not happen */ + return MU_ERR_FAILURE; + } + + if (!(pfd.revents & POLLIN)) + { + if (pfd.revents & POLLHUP) + return 0; + return MU_ERR_READ; + } + } + + n = read (fstr->fd, buf, size); if (n == -1) return errno; *pret = n; @@ -51,7 +84,36 @@ static int fd_write (struct _mu_stream *str, const char *buf, size_t size, size_t *pret) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; - ssize_t n = write (fstr->fd, (char*) buf, size); + ssize_t n; + + if (fstr->io_timeout > 0) + { + struct pollfd pfd; + pfd.fd = fstr->fd; + pfd.events = POLLOUT; + switch (poll (&pfd, 1, fstr->io_timeout)) + { + case 0: + return MU_ERR_TIMEOUT; + + case -1: + return errno; + + case 1: + break; + + default: + /* Should not happen */ + return MU_ERR_FAILURE; + } + + if (!(pfd.revents & POLLOUT)) + { + return MU_ERR_FAILURE; //FIXME: MU_ERR_WRITE? + } + } + + n = write (fstr->fd, (char*) buf, size); if (n == -1) return errno; *pret = n; @@ -188,6 +250,9 @@ fd_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr) ptrans = ptr; fstr->fd = (int) (intptr_t) ptrans[0]; break; + + default: + return EINVAL; } break; @@ -201,8 +266,12 @@ fd_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr) { case MU_IOCTL_OP_GET: return mu_stream_get_buffer (str, qp); + case MU_IOCTL_OP_SET: return mu_stream_set_buffer (str, qp->buftype, qp->bufsize); + + default: + return EINVAL; } } break; @@ -268,6 +337,9 @@ fd_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr) return ENOSYS; #endif } + + default: + return EINVAL; } break; @@ -286,9 +358,43 @@ fd_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr) else fstr->flags &= ~_MU_FILE_STREAM_FD_BORROWED; break; + + default: + return EINVAL; + } + break; + + case MU_IOCTL_TIMEOUT: + if (!ptr) + return EINVAL; + else + { + unsigned n; + struct timeval *tv = ptr; +# define MSEC_PER_SECOND 1000 + switch (opcode) + { + case MU_IOCTL_OP_GET: + tv->tv_sec = fstr->io_timeout / MSEC_PER_SECOND; + tv->tv_usec = (fstr->io_timeout % MSEC_PER_SECOND) * 1000; + break; + + case MU_IOCTL_OP_SET: + if (tv->tv_sec > UINT_MAX / MSEC_PER_SECOND) + return ERANGE; + n = tv->tv_sec * MSEC_PER_SECOND; + if (UINT_MAX - n < (unsigned long) tv->tv_usec / 1000) + return ERANGE; + n += tv->tv_usec / 1000; + fstr->io_timeout = n; + break; + + default: + return EINVAL; + } } break; - + default: return ENOSYS; } diff --git a/libmailutils/stream/iostream.c b/libmailutils/stream/iostream.c index 83b142a92..0f7b767b5 100644 --- a/libmailutils/stream/iostream.c +++ b/libmailutils/stream/iostream.c @@ -190,7 +190,9 @@ _iostream_ctl (struct _mu_stream *str, int code, int opcode, void *arg) } case MU_IOCTL_TCPSTREAM: - return mu_stream_ioctl (sp->transport[0], code, opcode, arg); + case MU_IOCTL_TIMEOUT: + //FIXME: what about output stream? + return mu_stream_ioctl (sp->transport[_MU_STREAM_INPUT], code, opcode, arg); default: return ENOSYS; diff --git a/libmailutils/stream/stream.c b/libmailutils/stream/stream.c index c816d9d05..9e0ae846e 100644 --- a/libmailutils/stream/stream.c +++ b/libmailutils/stream/stream.c @@ -23,6 +23,7 @@ #ifndef SIZE_MAX # define SIZE_MAX (~((size_t)0)) #endif +#include <sys/time.h> #include <mailutils/types.h> #include <mailutils/alloc.h> @@ -31,6 +32,7 @@ #include <mailutils/nls.h> #include <mailutils/stream.h> #include <mailutils/cstr.h> +#include <mailutils/datetime.h> #include <mailutils/sys/stream.h> size_t mu_stream_default_buffer_size = MU_STREAM_DEFBUFSIZ; @@ -776,16 +778,26 @@ mu_stream_read (mu_stream_t stream, void *buf, size_t size, size_t *pread) return 0; } -int +static int _stream_scandelim (mu_stream_t stream, char *buf, size_t size, int delim, - size_t *pnread) + struct timeval *to, size_t *pnread) { int rc = 0; size_t nread = 0; + + struct timeval stop_time, saved_timeout; + + if (to) + { + gettimeofday (&stop_time, NULL); + stop_time = mu_timeval_add (&stop_time, to); + + rc = mu_stream_ioctl (stream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_GET, + &saved_timeout); + if (rc) + return MU_ERR_SET_TIMEOUT; + } - size--; - if (size == 0) - return MU_ERR_BUFSPACE; while (size) { char *p, *q; @@ -793,6 +805,26 @@ _stream_scandelim (mu_stream_t stream, char *buf, size_t size, int delim, if (stream->pos == stream->level) { + if (to) + { + struct timeval now, d; + + gettimeofday (&now, NULL); + if (mu_timeval_cmp (&now, &stop_time) >= 0) + { + rc = MU_ERR_TIMEOUT; + break; + } + d = mu_timeval_sub (&stop_time, &now); + rc = mu_stream_ioctl (stream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, + &d); + if (rc) + { + rc = MU_ERR_SET_TIMEOUT; + break; + } + } + if ((rc = _stream_flush_buffer (stream, FLUSH_RDWR))) break; if ((rc = _stream_fill_buffer (stream)) || stream->level == 0) @@ -814,47 +846,99 @@ _stream_scandelim (mu_stream_t stream, char *buf, size_t size, int delim, if (p) /* Delimiter found */ break; } - *buf = 0; + + if (to) + { + mu_stream_ioctl (stream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, + &saved_timeout); + } + *pnread = nread; return rc; } static int _stream_readdelim (mu_stream_t stream, char *buf, size_t size, - int delim, size_t *pread) + int delim, + struct timeval *to, + size_t *pread) { int rc; char c; size_t n = 0, rdn; - size--; - if (size == 0) - return MU_ERR_BUFSPACE; - for (n = 0; - n < size && (rc = mu_stream_read (stream, &c, 1, &rdn)) == 0 && rdn;) + struct timeval stop_time, saved_timeout; + + if (to) { + gettimeofday (&stop_time, NULL); + stop_time = mu_timeval_add (&stop_time, to); + + rc = mu_stream_ioctl (stream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_GET, + &saved_timeout); + if (rc) + return MU_ERR_SET_TIMEOUT; + } + + n = 0; + while (n < size) + { + if (to) + { + struct timeval now, d; + + gettimeofday (&now, NULL); + if (mu_timeval_cmp (&now, &stop_time) >= 0) + { + rc = MU_ERR_TIMEOUT; + break; + } + d = mu_timeval_sub (&stop_time, &now); + rc = mu_stream_ioctl (stream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, + &d); + if (rc) + { + rc = MU_ERR_SET_TIMEOUT; + break; + } + } + + rc = mu_stream_read (stream, &c, 1, &rdn); + if (rc || rdn == 0) + break; + *buf++ = c; n++; if (c == delim) break; + } + + if (to) + { + mu_stream_ioctl (stream, MU_IOCTL_TIMEOUT, MU_IOCTL_OP_SET, + &saved_timeout); } - *buf = 0; + if (pread) *pread = n; return rc; } int -mu_stream_readdelim (mu_stream_t stream, char *buf, size_t size, - int delim, size_t *pread) +mu_stream_timed_readdelim (mu_stream_t stream, char *buf, size_t size, + int delim, struct timeval *to, size_t *pread) { int rc; - + size_t n; _bootstrap_event (stream); if (size == 0) return EINVAL; + size--; + if (size == 0) + return MU_ERR_BUFSPACE; + if (!(stream->flags & _MU_STR_OPEN)) { if (stream->open) @@ -864,26 +948,60 @@ mu_stream_readdelim (mu_stream_t stream, char *buf, size_t size, if (stream->buftype == mu_buffer_none) { - rc = _stream_readdelim (stream, buf, size, delim, pread); + rc = _stream_readdelim (stream, buf, size, delim, to, &n); } else { if ((rc = _stream_flush_buffer (stream, FLUSH_WRITE))) return rc; - rc = _stream_scandelim (stream, buf, size, delim, pread); + rc = _stream_scandelim (stream, buf, size, delim, to, &n); + } + + if (rc == 0) + { + buf[n] = 0; + if (pread) + *pread = n; } + return rc; } -int -mu_stream_readline (mu_stream_t stream, char *buf, size_t size, size_t *pread) +static int +bufexpand (char **pbuf, size_t *pn, size_t cur_len) { - return mu_stream_readdelim (stream, buf, size, '\n', pread); + size_t n = *pn; + + if (n == 0) + *pbuf = NULL; + if (cur_len == n) + { + char *p; + + if (!*pbuf) + { + if (!n) + { + n = 64; + } + } + else if ((size_t) -1 / 3 * 2 <= n) + return ENOMEM; + else + n += (n + 1) / 2; + + p = realloc (*pbuf, n); + if (!p) + return errno; + *pbuf = p; + *pn = n; + } + return 0; } int -mu_stream_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, - int delim, size_t *pread) +mu_stream_timed_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, + int delim, struct timeval *to, size_t *pread) { int rc; char *lineptr = *pbuf; @@ -902,53 +1020,19 @@ mu_stream_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, if ((rc = _stream_flush_buffer (stream, FLUSH_WRITE))) return rc; - if (lineptr == NULL || n == 0) - { - char *new_lineptr; - n = 120; - new_lineptr = realloc (lineptr, n); - if (new_lineptr == NULL) - return ENOMEM; - lineptr = new_lineptr; - } - for (;;) { size_t rdn; - /* Make enough space for len+1 (for final NUL) bytes. */ - if (cur_len + 1 >= n) - { - size_t needed_max = - SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; - size_t needed = 2 * n + 1; /* Be generous. */ - char *new_lineptr; - - if (needed_max < needed) - needed = needed_max; - if (cur_len + 1 >= needed) - { - rc = EOVERFLOW; - break; - } - - new_lineptr = realloc (lineptr, needed); - if (new_lineptr == NULL) - { - rc = ENOMEM; - break; - } - - lineptr = new_lineptr; - n = needed; - } + if ((rc = bufexpand (&lineptr, &n, cur_len)) != 0) + break; if (stream->buftype == mu_buffer_none) rc = _stream_readdelim (stream, lineptr + cur_len, n - cur_len, delim, - &rdn); + to, &rdn); else rc = _stream_scandelim (stream, lineptr + cur_len, n - cur_len, delim, - &rdn); + to, &rdn); if (rc || rdn == 0) break; @@ -957,7 +1041,9 @@ mu_stream_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, if (lineptr[cur_len - 1] == delim) break; } - lineptr[cur_len] = '\0'; + + if (rc == 0 && (rc = bufexpand (&lineptr, &n, cur_len)) == 0) + lineptr[cur_len] = '\0'; *pbuf = lineptr; *psize = n; @@ -967,13 +1053,6 @@ mu_stream_getdelim (mu_stream_t stream, char **pbuf, size_t *psize, return rc; } -int -mu_stream_getline (mu_stream_t stream, char **pbuf, size_t *psize, - size_t *pread) -{ - return mu_stream_getdelim (stream, pbuf, psize, '\n', pread); -} - /* Return 1 if no more data can be written to the current buffer. */ static inline int _stream_buffer_full_p (struct _mu_stream *stream) diff --git a/libmu_auth/tls.c b/libmu_auth/tls.c index 44469efd0..7db0982b6 100644 --- a/libmu_auth/tls.c +++ b/libmu_auth/tls.c @@ -253,16 +253,47 @@ _tls_open (mu_stream_t stream) rc = gnutls_handshake (sp->session); if (rc != GNUTLS_E_SUCCESS) { + mu_transport_t t[2]; + mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, ("gnutls_handshake: %s", gnutls_strerror (rc))); sp->tls_err = rc; + switch (rc) + { + case GNUTLS_E_PULL_ERROR: + if (mu_stream_ioctl (sp->transport[0], + MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, + &t) == 0 && + mu_stream_err (t[0])) + rc = mu_stream_last_error (t[0]); + else + rc = MU_ERR_READ; + break; + + case GNUTLS_E_PUSH_ERROR: + if (mu_stream_ioctl (sp->transport[1], + MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, + &t) == 0 && + mu_stream_err (t[1])) + rc = mu_stream_last_error (t[1]); + else + rc = MU_ERR_WRITE; + break; + + default: + rc = MU_ERR_TLS; + } + gnutls_deinit (sp->session); sp->session = NULL; sp->state = state_init; } else - /* FIXME: if (ssl_cafile) verify_certificate (s->session); */ - sp->state = state_open; + { + /* FIXME: if (ssl_cafile) verify_certificate (s->session); */ + sp->state = state_open; + rc = 0; + } break; default: @@ -391,7 +422,29 @@ _tls_ioctl (struct _mu_stream *stream, int code, int opcode, void *arg) case MU_IOCTL_TCPSTREAM: return mu_stream_ioctl (sp->transport[0], code, opcode, arg); - + + case MU_IOCTL_TIMEOUT: + { + int rc; + + switch (opcode) + { + case MU_IOCTL_OP_SET: + rc = mu_stream_ioctl (sp->transport[0], code, opcode, arg); + if (rc == 0) + rc = mu_stream_ioctl (sp->transport[1], code, opcode, arg); + break; + + case MU_IOCTL_OP_GET: + rc = mu_stream_ioctl (sp->transport[0], code, opcode, arg); + break; + + default: + return EINVAL; + } + return rc; + } + default: return ENOSYS; } |