X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=comstack%2Ftcpip.c;h=9feb4558f7560d4a27847787eef8e35162fbfc39;hp=add37919caa891d80c2e36f5ac09fb603063cbe0;hb=53fc6ff5ab7aaf73cdf33a77ca11b2fbabf185d6;hpb=6517fa53d35512887780fd07de5667940da18a9e diff --git a/comstack/tcpip.c b/comstack/tcpip.c index add3791..9feb455 100644 --- a/comstack/tcpip.c +++ b/comstack/tcpip.c @@ -1,10 +1,81 @@ /* - * Copyright (c) 1995-1997, Index Data + * Copyright (c) 1995-2001, Index Data * See the file LICENSE for details. - * Sebastian Hammer, Adam Dickmeiss * * $Log: tcpip.c,v $ - * Revision 1.17 1997-09-17 12:10:30 adam + * Revision 1.39 2001-07-19 19:49:40 adam + * Fixed bug in tcpip_set_blocking. + * + * Revision 1.38 2001/03/21 12:43:36 adam + * Implemented cs_create_host. Better error reporting for SSL comstack. + * + * Revision 1.37 2001/03/08 20:18:55 adam + * Added cs_set_blocking. Patch from Matthew Carey. + * + * Revision 1.36 2001/02/21 13:46:53 adam + * C++ fixes. + * + * Revision 1.35 2000/11/27 15:17:40 adam + * Using SSLeay_add_all_algorithms instead of OpenSSL_add_all_algorithms. + * + * Revision 1.34 2000/11/23 10:58:32 adam + * SSL comstack support. Separate POSIX thread support library. + * + * Revision 1.33 2000/09/04 08:27:11 adam + * Work on error handling for tcpip_accept. + * + * Revision 1.32 1999/11/30 13:47:11 adam + * Improved installation. Moved header files to include/yaz. + * + * Revision 1.31 1999/04/29 07:31:23 adam + * Changed tcpip_strtoaddr_ex so that only part 'till '/' is considered + * part of hostname. + * + * Revision 1.30 1999/04/20 09:56:48 adam + * Added 'name' paramter to encoder/decoder routines (typedef Odr_fun). + * Modified all encoders/decoders to reflect this change. + * + * Revision 1.29 1999/04/16 14:45:55 adam + * Added interface for tcpd wrapper for access control. + * + * Revision 1.28 1999/03/31 11:11:14 adam + * Function getprotobyname only called once. Minor change in tcpip_get + * to handle multi-threaded conditions. + * + * Revision 1.27 1999/02/02 13:57:31 adam + * Uses preprocessor define WIN32 instead of WINDOWS to build code + * for Microsoft WIN32. + * + * Revision 1.26 1999/01/08 11:23:14 adam + * Added const modifier to some of the BER/ODR encoding routines. + * + * Revision 1.25 1998/07/07 15:49:23 adam + * Added braces to avoid warning. + * + * Revision 1.24 1998/06/29 07:59:17 adam + * Minor fix. + * + * Revision 1.23 1998/06/23 15:37:50 adam + * Added type cast to prevent warning. + * + * Revision 1.22 1998/06/22 11:32:36 adam + * Added 'conditional cs_listen' feature. + * + * Revision 1.21 1998/05/20 09:55:32 adam + * Function tcpip_get treats EINPROGRESS error in the same way as + * EWOULDBLOCK. EINPROGRESS shouldn't be returned - but it is on + * Solaris in some cases. + * + * Revision 1.20 1998/05/18 10:10:40 adam + * Minor change to avoid C++ warning. + * + * Revision 1.19 1998/02/11 11:53:33 adam + * Changed code so that it compiles as C++. + * + * Revision 1.18 1997/09/29 07:15:25 adam + * Changed use of setsockopt to avoid warnings on MSVC. + * + * Revision 1.17 1997/09/17 12:10:30 adam * YAZ version 1.4. * * Revision 1.16 1997/09/01 08:49:14 adam @@ -124,14 +195,22 @@ #include #include #include -#ifndef WINDOWS +#ifndef WIN32 #include #endif #include #include +#if HAVE_OPENSSL_SSL_H +#include +#include +#endif -#include -#include +#include +#include +#include + +/* Chas added the following, so we get the definition of completeBER */ +#include int tcpip_close(COMSTACK h); int tcpip_put(COMSTACK h, char *buf, int size); @@ -140,23 +219,27 @@ int tcpip_connect(COMSTACK h, void *address); int tcpip_more(COMSTACK h); int tcpip_rcvconnect(COMSTACK h); int tcpip_bind(COMSTACK h, void *address, int mode); -int tcpip_listen(COMSTACK h, char *addrp, int *addrlen); +int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, + int (*check_ip)(void *cd, const char *a, int len, int type), + void *cd); +int static tcpip_set_blocking(COMSTACK p, int blocking); + +#if HAVE_OPENSSL_SSL_H +int ssl_get(COMSTACK h, char **buf, int *bufsize); +int ssl_put(COMSTACK h, char *buf, int size); +#endif + COMSTACK tcpip_accept(COMSTACK h); char *tcpip_addrstr(COMSTACK h); void *tcpip_straddr(COMSTACK h, const char *str); -/* - * Determine length/completeness of incoming packages - */ -int completeBER(unsigned char *buf, int len); /* from the ODR module */ -int completeWAIS(unsigned char *buf, int len); /* from waislen.c */ - -#ifdef TRACE_TCPIP +#if 0 #define TRC(x) x #else #define TRC(X) #endif +/* this state is used for both SSL and straight TCP/IP */ typedef struct tcpip_state { char *altbuf; /* alternate buffer for surplus data */ @@ -165,12 +248,17 @@ typedef struct tcpip_state int written; /* -1 if we aren't writing */ int towrite; /* to verify against user input */ - int (*complete)(unsigned char *buf, int len); /* length/completeness */ + int (*complete)(const unsigned char *buf, int len); /* length/comple. */ struct sockaddr_in addr; /* returned by cs_straddr */ char buf[128]; /* returned by cs_addrstr */ +#if HAVE_OPENSSL_SSL_H + SSL_CTX *ctx; + SSL_CTX *ctx_alloc; + SSL *ssl; +#endif } tcpip_state; -#ifdef WINDOWS +#ifdef WIN32 static int tcpip_init (void) { static int initialized = 0; @@ -187,57 +275,64 @@ static int tcpip_init (void) return 1; } #else +static int proto_number = 0; + static int tcpip_init (void) { + struct protoent *proto; + /* only call getprotobyname once, in case it allocates memory */ + if (!(proto = getprotobyname("tcp"))) + return 0; + proto_number = proto->p_proto; return 1; } #endif + /* * This function is always called through the cs_create() macro. * s >= 0: socket has already been established for us. */ -COMSTACK tcpip_type(int s, int blocking, int protocol) +COMSTACK tcpip_type(int s, int blocking, int protocol, void *vp) { COMSTACK p; tcpip_state *state; int new_socket; -#ifdef WINDOWS +#ifdef WIN32 unsigned long tru = 1; -#else - struct protoent *proto; #endif if (!tcpip_init ()) return 0; if (s < 0) { -#ifndef WINDOWS - if (!(proto = getprotobyname("tcp"))) +#ifdef WIN32 + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) return 0; - if ((s = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) #else - if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) -#endif + if ((s = socket(AF_INET, SOCK_STREAM, proto_number)) < 0) return 0; +#endif new_socket = 1; } else new_socket = 0; - if (!(p = xmalloc(sizeof(struct comstack)))) + if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack)))) return 0; - if (!(state = p->cprivate = xmalloc(sizeof(tcpip_state)))) + if (!(state = (struct tcpip_state *)(p->cprivate = + xmalloc(sizeof(tcpip_state))))) return 0; -#ifdef WINDOWS +#ifdef WIN32 if (!(p->blocking = blocking) && ioctlsocket(s, FIONBIO, &tru) < 0) #else if (!(p->blocking = blocking) && fcntl(s, F_SETFL, O_NONBLOCK) < 0) #endif return 0; + p->io_pending = 0; p->iofile = s; p->type = tcpip_type; - p->protocol = protocol; + p->protocol = (enum oid_proto) protocol; p->f_connect = tcpip_connect; p->f_rcvconnect = tcpip_rcvconnect; @@ -250,12 +345,18 @@ COMSTACK tcpip_type(int s, int blocking, int protocol) p->f_accept = tcpip_accept; p->f_addrstr = tcpip_addrstr; p->f_straddr = tcpip_straddr; + p->f_set_blocking = tcpip_set_blocking; p->state = new_socket ? CS_UNBND : CS_IDLE; /* state of line */ p->event = CS_NONE; p->cerrno = 0; p->stackerr = 0; +#if HAVE_OPENSSL_SSL_H + state->ctx = state->ctx_alloc = 0; + state->ssl = 0; +#endif + state->altbuf = 0; state->altsize = state->altlen = 0; state->towrite = state->written = -1; @@ -270,7 +371,42 @@ COMSTACK tcpip_type(int s, int blocking, int protocol) return p; } -static int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add) +#if HAVE_OPENSSL_SSL_H + +COMSTACK ssl_type(int s, int blocking, int protocol, void *vp) +{ + tcpip_state *state; + COMSTACK p; + yaz_log(LOG_LOG, "ssl_type begin"); + + p = tcpip_type (s, blocking, protocol, 0); + if (!p) + return 0; + p->f_get = ssl_get; + p->f_put = ssl_put; + p->type = ssl_type; + state = (tcpip_state *) p->cprivate; + if (vp) + state->ctx = vp; + else + { + SSL_load_error_strings(); + SSLeay_add_all_algorithms(); + + state->ctx = state->ctx_alloc = SSL_CTX_new (SSLv23_method()); + if (!state->ctx) + { + tcpip_close(p); + return 0; + } + } + /* note: we don't handle already opened socket in SSL mode - yet */ + yaz_log(LOG_LOG, "ssl_type end"); + return p; +} +#endif + +int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add) { struct hostent *hp; char *p, buf[512]; @@ -281,7 +417,10 @@ static int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add) return 0; TRC(fprintf(stderr, "tcpip_strtoaddress: %s\n", str ? str : "NULL")); add->sin_family = AF_INET; - strcpy(buf, str); + strncpy(buf, str, 511); + buf[511] = 0; + if ((p = strchr(buf, '/'))) + *p = 0; if ((p = strchr(buf, ':'))) { *p = 0; @@ -302,7 +441,7 @@ static int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add) void *tcpip_straddr(COMSTACK h, const char *str) { - tcpip_state *sp = h->cprivate; + tcpip_state *sp = (tcpip_state *)h->cprivate; if (!tcpip_strtoaddr_ex (str, &sp->addr)) return 0; @@ -320,7 +459,7 @@ struct sockaddr_in *tcpip_strtoaddr(const char *str) int tcpip_more(COMSTACK h) { - tcpip_state *sp = h->cprivate; + tcpip_state *sp = (tcpip_state *)h->cprivate; return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf, sp->altlen); @@ -333,19 +472,75 @@ int tcpip_more(COMSTACK h) */ int tcpip_connect(COMSTACK h, void *address) { - struct sockaddr_in *add = address; + struct sockaddr_in *add = (struct sockaddr_in *)address; +#if HAVE_OPENSSL_SSL_H + tcpip_state *sp = (tcpip_state *)h->cprivate; +#endif + int r; TRC(fprintf(stderr, "tcpip_connect\n")); - if (connect(h->iofile, (struct sockaddr *) add, sizeof(*add)) < 0) + h->io_pending = 0; + if (h->state == CS_UNBND) { -#ifdef WINDOWS - if (WSAGetLastError() == WSAEWOULDBLOCK) + r = connect(h->iofile, (struct sockaddr *) add, sizeof(*add)); + if (r < 0) + { +#ifdef WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) + { + h->state = CS_CONNECTING; + h->io_pending = CS_WANT_WRITE; + return 1; + } #else - if (errno == EINPROGRESS) + if (errno == EINPROGRESS) + { + h->state = CS_CONNECTING; + h->io_pending = CS_WANT_WRITE|CS_WANT_READ; + return 1; + } #endif - return 1; - return -1; + h->cerrno = CSYSERR; + return -1; + } + h->state = CS_CONNECTING; + } + if (h->state != CS_CONNECTING) + { + h->cerrno = CSOUTSTATE; + return -1; } +#if HAVE_OPENSSL_SSL_H + if (sp->ctx) + { + int res; + + if (!sp->ssl) + { + sp->ssl = SSL_new (sp->ctx); + SSL_set_fd (sp->ssl, h->iofile); + } + res = SSL_connect (sp->ssl); + if (res <= 0) + { + int err = SSL_get_error(sp->ssl, res); + if (err == SSL_ERROR_WANT_READ) + { + yaz_log (LOG_LOG, "SSL_connect. want_read"); + h->io_pending = CS_WANT_READ; + return 1; + } + if (err == SSL_ERROR_WANT_WRITE) + { + yaz_log (LOG_LOG, "SSL_connect. want_write"); + h->io_pending = CS_WANT_WRITE; + return 1; + } + h->cerrno = CSERRORSSL; + return -1; + } + } +#endif h->state = CS_DATAXFER; return 0; } @@ -359,13 +554,57 @@ int tcpip_rcvconnect(COMSTACK h) return 0; } +#define CERTF "ztest.pem" +#define KEYF "ztest.pem" + int tcpip_bind(COMSTACK h, void *address, int mode) { - struct sockaddr *addr = address; + struct sockaddr *addr = (struct sockaddr *)address; +#ifdef WIN32 + BOOL one = 1; +#else unsigned long one = 1; +#endif - TRC(fprintf(stderr, "tcpip_bind\n")); - if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) +#if HAVE_OPENSSL_SSL_H + tcpip_state *sp = (tcpip_state *)h->cprivate; + if (sp->ctx) + { + if (sp->ctx_alloc) + { + int res; + res = SSL_CTX_use_certificate_file (sp->ctx, CERTF, + SSL_FILETYPE_PEM); + if (res <= 0) + { + ERR_print_errors_fp(stderr); + exit (2); + } + res = SSL_CTX_use_PrivateKey_file (sp->ctx, KEYF, + SSL_FILETYPE_PEM); + if (res <= 0) + { + ERR_print_errors_fp(stderr); + exit (3); + } + res = SSL_CTX_check_private_key (sp->ctx); + if (res <= 0) + { + ERR_print_errors_fp(stderr); + exit(5); + } + } + TRC (fprintf (stderr, "ssl_bind\n")); + } + else + { + TRC (fprintf (stderr, "tcpip_bind\n")); + } +#else + TRC (fprintf (stderr, "tcpip_bind\n")); +#endif + if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) + &one, sizeof(one)) < 0) { h->cerrno = CSYSERR; return -1; @@ -384,34 +623,54 @@ int tcpip_bind(COMSTACK h, void *address, int mode) return 0; } -int tcpip_listen(COMSTACK h, char *raddr, int *addrlen) +int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, + int (*check_ip)(void *cd, const char *a, int len, int t), + void *cd) { struct sockaddr_in addr; +#ifdef __cplusplus + socklen_t len = sizeof(addr); +#else int len = sizeof(addr); +#endif - TRC(fprintf(stderr, "tcpip_listen\n")); + TRC(fprintf(stderr, "tcpip_listen pid=%d\n", getpid())); if (h->state != CS_IDLE) { h->cerrno = CSOUTSTATE; return -1; } - if ((h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len)) < 0) + h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len); + if (h->newfd < 0) { -#ifdef WINDOWS - if (WSAGetLastError() == WSAEWOULDBLOCK) + if ( +#ifdef WIN32 + WSAGetLastError() == WSAEWOULDBLOCK #else - if (errno == EWOULDBLOCK) + errno == EWOULDBLOCK #endif - - h->cerrno = CSNODATA; - else - h->cerrno = CSYSERR; + ) + h->cerrno = CSNODATA; + else + h->cerrno = CSYSERR; return -1; } - if (addrlen && *addrlen > sizeof(struct sockaddr_in)) + if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_in)) memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in)); else if (addrlen) *addrlen = 0; + if (check_ip && (*check_ip)(cd, (const char *) &addr, + sizeof(addr), AF_INET)) + { + h->cerrno = CSDENY; +#ifdef WIN32 + closesocket(h->newfd); +#else + close(h->newfd); +#endif + h->newfd = -1; + return -1; + } h->state = CS_INCON; return 0; } @@ -419,42 +678,122 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen) COMSTACK tcpip_accept(COMSTACK h) { COMSTACK cnew; - tcpip_state *state, *st = h->cprivate; -#ifdef WINDOWS + tcpip_state *state, *st = (tcpip_state *)h->cprivate; +#ifdef WIN32 unsigned long tru = 1; #endif TRC(fprintf(stderr, "tcpip_accept\n")); - if (h->state != CS_INCON) + if (h->state == CS_INCON) { - h->cerrno = CSOUTSTATE; - return 0; + if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew)))) + { + h->cerrno = CSYSERR; +#ifdef WIN32 + closesocket(h->newfd); +#else + close(h->newfd); +#endif + h->newfd = -1; + return 0; + } + memcpy(cnew, h, sizeof(*h)); + cnew->iofile = h->newfd; + cnew->io_pending = 0; + if (!(state = (tcpip_state *) + (cnew->cprivate = xmalloc(sizeof(tcpip_state))))) + { + h->cerrno = CSYSERR; + if (h->newfd != -1) + { +#ifdef WIN32 + closesocket(h->newfd); +#else + close(h->newfd); +#endif + h->newfd = -1; + } + return 0; + } + if (!cnew->blocking && +#ifdef WIN32 + (ioctlsocket(cnew->iofile, FIONBIO, &tru) < 0) +#else + (!cnew->blocking && fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0) +#endif + ) + { + h->cerrno = CSYSERR; + if (h->newfd != -1) + { +#ifdef WIN32 + closesocket(h->newfd); +#else + close(h->newfd); +#endif + h->newfd = -1; + } + xfree (cnew); + xfree (state); + return 0; + } + h->newfd = -1; + state->altbuf = 0; + state->altsize = state->altlen = 0; + state->towrite = state->written = -1; + state->complete = st->complete; + cnew->state = CS_ACCEPT; + h->state = CS_IDLE; + +#if HAVE_OPENSSL_SSL_H + state->ctx = st->ctx; + state->ctx_alloc = 0; + state->ssl = st->ssl; + if (state->ctx) + { + state->ssl = SSL_new (state->ctx); + SSL_set_fd (state->ssl, cnew->iofile); + } +#endif + h = cnew; } - if (!(cnew = xmalloc(sizeof(*cnew)))) + if (h->state == CS_ACCEPT) { - h->cerrno = CSYSERR; - return 0; +#if HAVE_OPENSSL_SSL_H + tcpip_state *state = (tcpip_state *)h->cprivate; + if (state->ctx) + { + int res = SSL_accept (state->ssl); + TRC(fprintf(stderr, "SSL_accept\n")); + if (res <= 0) + { + int err = SSL_get_error(state->ssl, res); + if (err == SSL_ERROR_WANT_READ) + { + h->io_pending = CS_WANT_READ; + yaz_log (LOG_LOG, "SSL_accept. want_read"); + return h; + } + if (err == SSL_ERROR_WANT_WRITE) + { + h->io_pending = CS_WANT_WRITE; + yaz_log (LOG_LOG, "SSL_accept. want_write"); + return h; + } + cs_close (h); + return 0; + } + } +#endif } - memcpy(cnew, h, sizeof(*h)); - cnew->iofile = h->newfd; - if (!(state = cnew->cprivate = xmalloc(sizeof(tcpip_state)))) + else { - h->cerrno = CSYSERR; + h->cerrno = CSOUTSTATE; return 0; } -#ifdef WINDOWS - if (!cnew->blocking && ioctlsocket(cnew->iofile, FIONBIO, &tru) < 0) -#else - if (!cnew->blocking && fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0) -#endif - return 0; - state->altbuf = 0; - state->altsize = state->altlen = 0; - state->towrite = state->written = -1; - state->complete = st->complete; - cnew->state = CS_DATAXFER; - h->state = CS_IDLE; - return cnew; + h->io_pending = 0; + h->state = CS_DATAXFER; + return h; } #define CS_TCPIP_BUFCHUNK 4096 @@ -465,7 +804,7 @@ COMSTACK tcpip_accept(COMSTACK h) */ int tcpip_get(COMSTACK h, char **buf, int *bufsize) { - tcpip_state *sp = h->cprivate; + tcpip_state *sp = (tcpip_state *)h->cprivate; char *tmpc; int tmpi, berlen, rest, req, tomove; int hasread = 0, res; @@ -484,31 +823,137 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) sp->altbuf = tmpc; sp->altsize = tmpi; } + h->io_pending = 0; while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread))) { if (!*bufsize) { - if (!(*buf = xmalloc(*bufsize = CS_TCPIP_BUFCHUNK))) + if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK))) return -1; } else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK) - if (!(*buf =xrealloc(*buf, *bufsize *= 2))) + if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2))) return -1; - if ((res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0)) < 0) -#ifdef WINDOWS - if (WSAGetLastError() == WSAEWOULDBLOCK) + res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0); + TRC(fprintf(stderr, " recv res=%d, hasread=%d\n", res, hasread)); + if (res < 0) + { +#ifdef WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) + { + h->io_pending = CS_WANT_READ; + break; + } + else + return -1; #else - if (errno == EWOULDBLOCK) + if (errno == EWOULDBLOCK +#ifdef EINPROGRESS + || errno == EINPROGRESS #endif - break; - else - return -1; - if (!res) - return 0; + ) + { + h->io_pending = CS_WANT_READ; + break; + } + else if (errno == 0) + continue; + else + return -1; +#endif + } + else if (!res) + return 0; hasread += res; - TRC(fprintf(stderr, " res=%d, hasread=%d\n", res, hasread)); } - TRC(fprintf(stderr, " Out of read loop with hasread=%d, berlen=%d\n", + TRC (fprintf (stderr, " Out of read loop with hasread=%d, berlen=%d\n", + hasread, berlen)); + /* move surplus buffer (or everything if we didn't get a BER rec.) */ + if (hasread > berlen) + { + tomove = req = hasread - berlen; + rest = tomove % CS_TCPIP_BUFCHUNK; + if (rest) + req += CS_TCPIP_BUFCHUNK - rest; + if (!sp->altbuf) + { + if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req))) + return -1; + } else if (sp->altsize < req) + if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req))) + return -1; + TRC(fprintf(stderr, " Moving %d bytes to altbuf(0x%x)\n", tomove, + (unsigned) sp->altbuf)); + memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove); + } + if (berlen < CS_TCPIP_BUFCHUNK - 1) + *(*buf + berlen) = '\0'; + return berlen ? berlen : 1; +} + + +#if HAVE_OPENSSL_SSL_H +/* + * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer, + * 0=connection closed. + */ +int ssl_get(COMSTACK h, char **buf, int *bufsize) +{ + tcpip_state *sp = (tcpip_state *)h->cprivate; + char *tmpc; + int tmpi, berlen, rest, req, tomove; + int hasread = 0, res; + + TRC(fprintf(stderr, "ssl_get: bufsize=%d\n", *bufsize)); + if (sp->altlen) /* switch buffers */ + { + TRC(fprintf(stderr, " %d bytes in altbuf (0x%x)\n", sp->altlen, + (unsigned) sp->altbuf)); + tmpc = *buf; + tmpi = *bufsize; + *buf = sp->altbuf; + *bufsize = sp->altsize; + hasread = sp->altlen; + sp->altlen = 0; + sp->altbuf = tmpc; + sp->altsize = tmpi; + } + h->io_pending = 0; + while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread))) + { + if (!*bufsize) + { + if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK))) + return -1; + } + else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK) + if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2))) + return -1; + res = SSL_read (sp->ssl, *buf + hasread, CS_TCPIP_BUFCHUNK); + TRC(fprintf(stderr, " SSL_read res=%d, hasread=%d\n", res, hasread)); + if (res <= 0) + { + int ssl_err = SSL_get_error(sp->ssl, res); + if (ssl_err == SSL_ERROR_WANT_READ) + { + h->io_pending = CS_WANT_READ; + yaz_log (LOG_LOG, "SSL_read. want_read"); + break; + } + if (ssl_err == SSL_ERROR_WANT_WRITE) + { + h->io_pending = CS_WANT_WRITE; + yaz_log (LOG_LOG, "SSL_read. want_write"); + break; + } + if (res == 0) + return 0; + h->cerrno = CSERRORSSL; + return -1; + } + hasread += res; + } + TRC (fprintf (stderr, " Out of read loop with hasread=%d, berlen=%d\n", hasread, berlen)); /* move surplus buffer (or everything if we didn't get a BER rec.) */ if (hasread > berlen) @@ -519,10 +964,10 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) req += CS_TCPIP_BUFCHUNK - rest; if (!sp->altbuf) { - if (!(sp->altbuf = xmalloc(sp->altsize = req))) + if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req))) return -1; } else if (sp->altsize < req) - if (!(sp->altbuf =xrealloc(sp->altbuf, sp->altsize = req))) + if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req))) return -1; TRC(fprintf(stderr, " Moving %d bytes to altbuf(0x%x)\n", tomove, (unsigned) sp->altbuf)); @@ -532,6 +977,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) *(*buf + berlen) = '\0'; return berlen ? berlen : 1; } +#endif /* * Returns 1, 0 or -1 @@ -541,9 +987,10 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) int tcpip_put(COMSTACK h, char *buf, int size) { int res; - struct tcpip_state *state = h->cprivate; + struct tcpip_state *state = (struct tcpip_state *)h->cprivate; TRC(fprintf(stderr, "tcpip_put: size=%d\n", size)); + h->io_pending = 0; if (state->towrite < 0) { state->towrite = size; @@ -556,43 +1003,120 @@ int tcpip_put(COMSTACK h, char *buf, int size) } while (state->towrite > state->written) { - if ((res = send(h->iofile, buf + state->written, size - - state->written, 0)) < 0) - { -#ifdef WINDOWS - if (WSAGetLastError() == WSAEWOULDBLOCK) + if ((res = send(h->iofile, buf + state->written, size - + state->written, 0)) < 0) + { + if ( +#ifdef WIN32 + WSAGetLastError() == WSAEWOULDBLOCK #else - if (errno == EAGAIN) -#endif - { - TRC(fprintf(stderr, " Flow control stop\n")); - return 1; - } - h->cerrno = CSYSERR; - return -1; - } - state->written += res; - TRC(fprintf(stderr, " Wrote %d, written=%d, nbytes=%d\n", - res, state->written, size)); + errno == EAGAIN +#endif + ) + { + TRC(fprintf(stderr, " Flow control stop\n")); + h->io_pending = CS_WANT_WRITE; + return 1; + } + h->cerrno = CSYSERR; + return -1; + } + state->written += res; + TRC(fprintf(stderr, " Wrote %d, written=%d, nbytes=%d\n", + res, state->written, size)); + } + state->towrite = state->written = -1; + TRC(fprintf(stderr, " Ok\n")); + return 0; +} + + +#if HAVE_OPENSSL_SSL_H +/* + * Returns 1, 0 or -1 + * In nonblocking mode, you must call again with same buffer while + * return value is 1. + */ +int ssl_put(COMSTACK h, char *buf, int size) +{ + int res; + struct tcpip_state *state = (struct tcpip_state *)h->cprivate; + + TRC(fprintf(stderr, "ssl_put: size=%d\n", size)); + h->io_pending = 0; + if (state->towrite < 0) + { + state->towrite = size; + state->written = 0; + } + else if (state->towrite != size) + { + h->cerrno = CSWRONGBUF; + return -1; + } + while (state->towrite > state->written) + { + res = SSL_write (state->ssl, buf + state->written, + size - state->written); + if (res <= 0) + { + int ssl_err = SSL_get_error(state->ssl, res); + if (ssl_err == SSL_ERROR_WANT_READ) + { + h->io_pending = CS_WANT_READ; + yaz_log (LOG_LOG, "SSL_write. want_read"); + return 1; + } + if (ssl_err == SSL_ERROR_WANT_WRITE) + { + h->io_pending = CS_WANT_WRITE; + yaz_log (LOG_LOG, "SSL_write. want_write"); + return 1; + } + h->cerrno = CSERRORSSL; + return -1; + } + state->written += res; + TRC(fprintf(stderr, " Wrote %d, written=%d, nbytes=%d\n", + res, state->written, size)); } state->towrite = state->written = -1; TRC(fprintf(stderr, " Ok\n")); return 0; } +#endif int tcpip_close(COMSTACK h) { - tcpip_state *sp = h->cprivate; + tcpip_state *sp = (struct tcpip_state *)h->cprivate; TRC(fprintf(stderr, "tcpip_close\n")); if (h->iofile != -1) -#ifdef WINDOWS + { +#if HAVE_OPENSSL_SSL_H + if (sp->ssl) + { + SSL_shutdown (sp->ssl); + } +#endif +#ifdef WIN32 closesocket(h->iofile); #else close(h->iofile); #endif + } if (sp->altbuf) xfree(sp->altbuf); +#if HAVE_OPENSSL_SSL_H + if (sp->ssl) + { + TRC (fprintf(stderr, "SSL_free\n")); + SSL_free (sp->ssl); + } + sp->ssl = 0; + if (sp->ctx_alloc) + SSL_CTX_free (sp->ctx_alloc); +#endif xfree(sp); xfree(h); return 0; @@ -601,9 +1125,9 @@ int tcpip_close(COMSTACK h) char *tcpip_addrstr(COMSTACK h) { struct sockaddr_in addr; - tcpip_state *sp = h->cprivate; + tcpip_state *sp = (struct tcpip_state *)h->cprivate; char *r, *buf = sp->buf; - int len; + size_t len; struct hostent *host; len = sizeof(addr); @@ -618,5 +1142,32 @@ char *tcpip_addrstr(COMSTACK h) else r = inet_ntoa(addr.sin_addr); sprintf(buf, "tcp:%s", r); +#if HAVE_OPENSSL_SSL_H + if (sp->ctx) + sprintf(buf, "ssl:%s", r); +#endif return buf; } + +int static tcpip_set_blocking(COMSTACK p, int blocking) +{ + unsigned long flag; + + if (p->blocking == blocking) + return 1; +#ifdef WIN32 + flag = 1; + if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0) + return 0; +#else + flag = fcntl(p->iofile, F_GETFL, 0); + if(!blocking) + flag = flag & ~O_NONBLOCK; + else + flag = flag | O_NONBLOCK; + if (fcntl(p->iofile, F_SETFL, flag) < 0) + return 0; +#endif + p->blocking = blocking; + return 1; +}