Mon, 26 Dec 2016 16:46:55 +0100
adds support for ssl cert chain files and improves ssl error handling
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef __gnu_linux__ #define _GNU_SOURCE #endif #include <unistd.h> #include <stdlib.h> #include <sys/uio.h> #include <sys/uio.h> #if defined(LINUX) || defined(SOLARIS) #include <sys/sendfile.h> #define WS_SENDFILE #elif defined(BSD) #if defined(__NetBSD__) || defined(__OpenBSD__) #define net_sys_sendfile net_fallback_sendfile #else #define WS_SENDFILE #endif #else #define net_sys_sendfile net_fallback_sendfile #endif #include "../daemon/vfs.h" #include "io.h" #include "pool.h" #include "ucx/utils.h" IOStream native_io_funcs = { (io_write_f)net_sys_write, (io_writev_f)net_sys_writev, (io_read_f)net_sys_read, (io_sendfile_f)net_sys_sendfile, (io_close_f)net_sys_close, NULL }; IOStream http_io_funcs = { (io_write_f)net_http_write, (io_writev_f)net_http_writev, (io_read_f)net_http_read, (io_sendfile_f)net_http_sendfile, (io_close_f)net_http_close, (io_finish_f)net_http_finish }; IOStream ssl_io_funcs = { (io_write_f)net_ssl_write, (io_writev_f)net_ssl_writev, (io_read_f)net_ssl_read, NULL, (io_close_f)net_ssl_close, (io_finish_f)net_ssl_finish }; /* * SysStream implementation */ IOStream* sysstream_new(pool_handle_t *pool, int fd) { SysStream *st = pool_malloc(pool, sizeof(SysStream)); st->st = native_io_funcs; st->fd = fd; return (IOStream*)st; } #ifdef XP_UNIX ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes) { return write(st->fd, buf, nbytes); } ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt) { return writev(st->fd, iovec, iovcnt); } ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes) { return read(st->fd, buf, nbytes); } #ifdef WS_SENDFILE ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) { ssize_t ret = 0; off_t fileoffset = sfd->offset; if(sfd->fd->fd != -1) { #ifdef BSD struct iovec hdvec; hdvec.iov_base = (void*)sfd->header; hdvec.iov_len = sfd->hlen; struct iovec trvec; trvec.iov_base = (void*)sfd->trailer; trvec.iov_len = sfd->tlen; struct sf_hdtr hdtr; hdtr.headers = &hdvec; hdtr.hdr_cnt = 1; hdtr.trailers = &trvec; hdtr.trl_cnt = 1; off_t len = sfd->len; #ifdef OSX ret = sendfile(sfd->fd->fd, st->fd, fileoffset, &len, &hdtr, 0); #else // BSD ret = sendfile( sfd->fd->fd, st->fd, fileoffset, sfd->len, &hdtr, NULL, 0); #endif #else // Solaris/Linux if(sfd->header) { ret += write(st->fd, sfd->header, sfd->hlen); } ret += sendfile(st->fd, sfd->fd->fd, &fileoffset, sfd->len); if(sfd->trailer) { ret += write(st->fd, sfd->trailer, sfd->tlen); } #endif } else { return net_fallback_sendfile((IOStream*)st, sfd); } return ret; } #endif void net_sys_close(SysStream *st) { close(st->fd); } #elif defined(XP_WIN32) ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes) { int ret = send(st->socket, buf, nbytes, 0); if(ret == SOCKET_ERROR) { return IO_ERROR; } return ret; } ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt) { // TODO } ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes) { int ret = recv(st->socket, buf, nbytes, 0); if(ret == SOCKET_ERROR) { return IO_ERROR; } return ret; } ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) { // TODO } void net_sys_close(SysStream *st) { closesocket(st->socket); } #endif /* * HttpStream implementation */ IOStream* httpstream_new(pool_handle_t *pool, IOStream *fd) { HttpStream *st = pool_malloc(pool, sizeof(HttpStream)); st->st = http_io_funcs; st->fd = fd; st->max_read = 0; st->read = 0; st->chunked_enc = WS_FALSE; st->buffered = WS_FALSE; return (IOStream*)st; } ssize_t net_http_write(HttpStream *st, void *buf, size_t nbytes) { IOStream *fd = st->fd; if(st->chunked_enc) { // TODO: on some plattforms iov_len is smaller than size_t struct iovec io[3]; char chunk_len[16]; io[0].iov_base = chunk_len; io[0].iov_len = snprintf(chunk_len, 16, "%zx\r\n", nbytes); io[1].iov_base = buf; io[1].iov_len = nbytes; io[2].iov_base = "\r\n"; io[2].iov_len = 2; ssize_t r = fd->writev(fd, io, 3); return r - io[0].iov_len; } else { return fd->write(fd, buf, nbytes); } } ssize_t net_http_writev(HttpStream *st, struct iovec *iovec, int iovcnt) { IOStream *fd = st->fd; if(st->chunked_enc) { struct iovec *io = calloc(iovcnt + 1, sizeof(struct iovec)); char chunk_len[16]; io[0].iov_base = chunk_len; size_t len = 0; for(int i=0;i<iovcnt;i++) { len += iovec[i].iov_len; } io[0].iov_len = snprintf(chunk_len, 16, "\r\n%zx\r\n", len); memcpy(io + 1, iovec, iovcnt * sizeof(struct iovec)); ssize_t r = fd->writev(fd, io, iovcnt + 1); return r - io[0].iov_len; } else { return fd->writev(fd, iovec, iovcnt); } } ssize_t net_http_read(HttpStream *st, void *buf, size_t nbytes) { if(st->max_read != 0 && st->read >= st->max_read) { return 0; } ssize_t r = st->fd->read(st->fd, buf, nbytes); st->read += r; return r; } ssize_t net_http_sendfile(HttpStream *st, sendfiledata *sfd) { ssize_t ret = 0; // TODO: support chunked transfer encoding if(st->fd->sendfile) { ret = st->fd->sendfile(st->fd, sfd); } else { ret = net_fallback_sendfile((IOStream*)st, sfd); } return ret; } void net_http_close(HttpStream *st) { st->fd->close(st->fd); } void net_http_finish(HttpStream *st) { if(st->chunked_enc) { st->fd->write(st->fd, "0\r\n\r\n", 5); } } /* * SSLStream implementation */ IOStream* sslstream_new(pool_handle_t *pool, SSL *ssl) { SSLStream *st = pool_malloc(pool, sizeof(SSLStream)); st->st = ssl_io_funcs; st->ssl = ssl; return (IOStream*)st; } ssize_t net_ssl_write(SSLStream *st, void *buf, size_t nbytes) { return SSL_write(st->ssl, buf, nbytes); } ssize_t net_ssl_writev(SSLStream *st, struct iovec *iovec, int iovcnt) { ssize_t r = 0; for(int i=0;i<iovcnt;i++) { int ret = SSL_write(st->ssl, iovec[i].iov_base, iovec[i].iov_len); if(ret <= 0) { return 0; } r += ret; } return r; } ssize_t net_ssl_read(SSLStream *st, void *buf, size_t nbytes) { return SSL_read(st->ssl, buf, nbytes); } void net_ssl_close(SSLStream *st) { SSL_shutdown(st->ssl); close(SSL_get_fd(st->ssl)); } void net_ssl_finish(SSLStream *st) { } /* -------------------- public nsapi network functions -------------------- */ ssize_t net_read(SYS_NETFD fd, void *buf, size_t nbytes) { ssize_t r = ((IOStream*)fd)->read(fd, buf, nbytes); if(r == 0) { return IO_EOF; } return r; } ssize_t net_write(SYS_NETFD fd, void *buf, size_t nbytes) { ssize_t r = ((IOStream*)fd)->write(fd, buf, nbytes); if(r < 0) { return IO_ERROR; } return r; } ssize_t net_writev(SYS_NETFD fd, struct iovec *iovec, int iovcnt) { ssize_t r = ((IOStream*)fd)->writev(fd, iovec, iovcnt); if(r < 0) { return IO_ERROR; } return r; } ssize_t net_printf(SYS_NETFD fd, char *format, ...) { va_list arg; va_start(arg, format); sstr_t buf = ucx_vasprintf(ucx_default_allocator(), format, arg); ssize_t r = net_write(fd, buf.ptr, buf.length); free(buf.ptr); return r; } ssize_t net_sendfile(SYS_NETFD fd, sendfiledata *sfd) { IOStream *out = fd; if(out->sendfile && sfd->fd && sfd->fd->fd != -1) { ssize_t r = out->sendfile(fd, sfd); if(r < 0) { return IO_ERROR; } } else { // stream/file does not support sendfile // do regular copy return net_fallback_sendfile(out, sfd); } return IO_ERROR; } // private ssize_t net_fallback_sendfile(IOStream *fd, sendfiledata *sfd) { char *buf = malloc(4096); if(!buf) { // TODO: out of memory error return IO_ERROR; } char *header = (char*)sfd->header; int hlen = sfd->hlen; char *trailer = (char*)sfd->trailer; int tlen = sfd->tlen; if(header == NULL) { hlen = 0; } if(trailer == NULL) { tlen = 0; } ssize_t r; while(hlen > 0) { r = fd->write(fd, header, hlen); header += r; hlen -= r; if(r <= 0) { free(buf); return IO_ERROR; } } if(system_lseek(sfd->fd, sfd->offset, SEEK_SET) == -1) { free(buf); return IO_ERROR; } size_t length = sfd->len; while(length > 0) { if((r = system_fread(sfd->fd, buf, 4096)) <= 0) { break; } char *write_buf = buf; while(r > 0) { ssize_t w = fd->write(fd, write_buf, r); r -= w; length -= w; write_buf += w; } } free(buf); if(length > 0) { return IO_ERROR; } while(tlen > 0) { r = fd->write(fd, trailer, tlen); trailer += r; tlen -= r; if(r <= 0) { return IO_ERROR; } } return sfd->hlen + sfd->len + sfd->tlen; } int net_flush(SYS_NETFD sd) { // TODO: implement return 0; } void net_close(SYS_NETFD fd) { ((IOStream*)fd)->close(fd); } // private void net_finish(SYS_NETFD fd) { ((IOStream*)fd)->finish(fd); }