X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Ftcpip.c;h=1c5f78047892eb6bc15830337b5172f627fb5e50;hp=a16fe5eb202c58d9edaf00a45c5f67a4ec29fb1d;hb=02b6a809f17f142071384ffac8e406b6e9fbd2fb;hpb=13f137b81808f6a3359f898614cc988995af3965 diff --git a/src/tcpip.c b/src/tcpip.c index a16fe5e..1c5f780 100644 --- a/src/tcpip.c +++ b/src/tcpip.c @@ -1,8 +1,6 @@ -/* - * Copyright (C) 1995-2006, Index Data ApS +/* This file is part of the YAZ toolkit. + * Copyright (C) 1995-2008 Index Data * See the file LICENSE for details. - * - * $Id: tcpip.c,v 1.31 2006-09-21 15:55:25 adam Exp $ */ /** * \file tcpip.c @@ -26,10 +24,17 @@ #endif #ifdef WIN32 + +/* VS 2003 or later has getaddrinfo; older versions do not */ #include +#if _MSC_VER >= 1300 #include #define HAVE_GETADDRINFO 1 #else +#define HAVE_GETADDRINFO 0 +#endif + +#else #include #include #include @@ -39,16 +44,19 @@ #if HAVE_SYS_SOCKET_H #include #endif -#if HAVE_SYS_SELECT_H -#include -#endif #if HAVE_SYS_WAIT_H #include #endif +#if HAVE_GNUTLS_H +#include +#define ENABLE_SSL 1 +#endif + #if HAVE_OPENSSL_SSL_H #include #include +#define ENABLE_SSL 1 #endif #include @@ -58,6 +66,8 @@ static int tcpip_close(COMSTACK h); static int tcpip_put(COMSTACK h, char *buf, int size); static int tcpip_get(COMSTACK h, char **buf, int *bufsize); +static int tcpip_put_connect(COMSTACK h, char *buf, int size); +static int tcpip_get_connect(COMSTACK h, char **buf, int *bufsize); static int tcpip_connect(COMSTACK h, void *address); static int tcpip_more(COMSTACK h); static int tcpip_rcvconnect(COMSTACK h); @@ -67,7 +77,7 @@ static int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, void *cd); static int tcpip_set_blocking(COMSTACK p, int blocking); -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL static int ssl_get(COMSTACK h, char **buf, int *bufsize); static int ssl_put(COMSTACK h, char *buf, int size); #endif @@ -95,23 +105,27 @@ typedef struct tcpip_state int written; /* -1 if we aren't writing */ int towrite; /* to verify against user input */ - int (*complete)(const unsigned char *buf, int len); /* length/comple. */ + int (*complete)(const char *buf, int len); /* length/complete. */ #if HAVE_GETADDRINFO struct addrinfo *ai; #else struct sockaddr_in addr; /* returned by cs_straddr */ #endif char buf[128]; /* returned by cs_addrstr */ -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL SSL_CTX *ctx; /* current CTX. */ SSL_CTX *ctx_alloc; /* If =ctx it is owned by CS. If 0 it is not owned */ SSL *ssl; char cert_fname[256]; #endif + char *connect_request_buf; + int connect_request_len; + char *connect_response_buf; + int connect_response_len; } tcpip_state; #ifdef WIN32 -static int tcpip_init (void) +static int tcpip_init(void) { static int initialized = 0; if (!initialized) @@ -127,7 +141,7 @@ static int tcpip_init (void) return 1; } #else -static int tcpip_init (void) +static int tcpip_init(void) { return 1; } @@ -142,7 +156,7 @@ COMSTACK tcpip_type(int s, int flags, int protocol, void *vp) COMSTACK p; tcpip_state *sp; - if (!tcpip_init ()) + if (!tcpip_init()) return 0; if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack)))) return 0; @@ -177,7 +191,7 @@ COMSTACK tcpip_type(int s, int flags, int protocol, void *vp) p->stackerr = 0; p->user = 0; -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL sp->ctx = sp->ctx_alloc = 0; sp->ssl = 0; strcpy(sp->cert_fname, "yaz.pem"); @@ -194,20 +208,46 @@ COMSTACK tcpip_type(int s, int flags, int protocol, void *vp) else sp->complete = cs_complete_auto; + sp->connect_request_buf = 0; + sp->connect_request_len = 0; + sp->connect_response_buf = 0; + sp->connect_response_len = 0; + p->timeout = COMSTACK_DEFAULT_TIMEOUT; TRC(fprintf(stderr, "Created new TCPIP comstack\n")); return p; } -#if HAVE_OPENSSL_SSL_H +COMSTACK yaz_tcpip_create(int s, int flags, int protocol, + const char *connect_host) +{ + COMSTACK p = tcpip_type(s, flags, protocol, 0); + if (!p) + return 0; + if (connect_host) + { + tcpip_state *sp = (tcpip_state *) p->cprivate; + sp->connect_request_buf = (char *) xmalloc(strlen(connect_host) + 30); + sprintf(sp->connect_request_buf, "CONNECT %s HTTP/1.0\r\n\r\n", + connect_host); + sp->connect_request_len = strlen(sp->connect_request_buf); + p->f_put = tcpip_put_connect; + p->f_get = tcpip_get_connect; + sp->complete = cs_complete_auto_head; /* only want HTTP header */ + } + return p; +} + + +#if ENABLE_SSL COMSTACK ssl_type(int s, int flags, int protocol, void *vp) { tcpip_state *sp; COMSTACK p; - p = tcpip_type (s, flags, protocol, 0); + p = tcpip_type(s, flags, protocol, 0); if (!p) return 0; p->f_get = ssl_get; @@ -220,6 +260,41 @@ COMSTACK ssl_type(int s, int flags, int protocol, void *vp) /* note: we don't handle already opened socket in SSL mode - yet */ return p; } + +int ssl_check_error(COMSTACK h, tcpip_state *sp, int res) +{ +#if HAVE_OPENSSL_SSL_H + int err = SSL_get_error(sp->ssl, res); + TRC(fprintf(stderr, "got err=%d\n", err)); + if (err == SSL_ERROR_WANT_READ) + { + TRC(fprintf(stderr, " -> SSL_ERROR_WANT_READ\n")); + h->io_pending = CS_WANT_READ; + return 1; + } + if (err == SSL_ERROR_WANT_WRITE) + { + TRC(fprintf(stderr, " -> SSL_ERROR_WANT_WRITE\n")); + h->io_pending = CS_WANT_WRITE; + return 1; + } +#else + int tls_error = sp->ssl->last_error; + TRC(fprintf(stderr, "ssl_check_error error=%d fatal=%d msg=%s\n", + sp->ssl->last_error, + gnutls_error_is_fatal(tls_error), + gnutls_strerror(tls_error))); + if (tls_error == GNUTLS_E_AGAIN || tls_error == GNUTLS_E_INTERRUPTED) + { + int dir = gnutls_record_get_direction(sp->ssl->gnutls_state); + TRC(fprintf(stderr, " -> incomplete dir=%d\n", dir)); + h->io_pending = dir ? CS_WANT_WRITE : CS_WANT_READ; + return 1; + } +#endif + h->cerrno = CSERRORSSL; + return 0; +} #endif #if HAVE_GETADDRINFO @@ -314,7 +389,7 @@ void *tcpip_straddr(COMSTACK h, const char *str) const char *port = "210"; if (h->protocol == PROTO_HTTP) port = "80"; - if (!tcpip_init ()) + if (!tcpip_init()) return 0; if (sp->ai) @@ -347,9 +422,9 @@ void *tcpip_straddr(COMSTACK h, const char *str) if (h->protocol == PROTO_HTTP) port = 80; - if (!tcpip_init ()) + if (!tcpip_init()) return 0; - if (!tcpip_strtoaddr_ex (str, &sp->addr, port)) + if (!tcpip_strtoaddr_ex(str, &sp->addr, port)) return 0; if (h->state == CS_ST_UNBND) { @@ -370,8 +445,7 @@ int tcpip_more(COMSTACK h) { tcpip_state *sp = (tcpip_state *)h->cprivate; - return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf, - sp->altlen); + return sp->altlen && (*sp->complete)(sp->altbuf, sp->altlen); } /* @@ -410,7 +484,7 @@ int tcpip_connect(COMSTACK h, void *address) * This gives the connect a chance to negotiate with the other side * (see 'man tcp') */ - if ( getsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, &rbufsize ) < 0 ) + if (getsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, &rbufsize ) < 0 ) { h->cerrno = CSYSERR; return -1; @@ -418,18 +492,18 @@ int tcpip_connect(COMSTACK h, void *address) TRC(fprintf( stderr, "Current Size of TCP Receive Buffer= %d\n", recbuflen )); recbuflen *= 10; /* lets be optimistic */ - if ( setsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, rbufsize ) < 0 ) + if (setsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, rbufsize ) < 0 ) { h->cerrno = CSYSERR; return -1; } - if ( getsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, &rbufsize ) ) + if (getsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, &rbufsize ) ) { h->cerrno = CSYSERR; return -1; } - TRC(fprintf( stderr, "New Size of TCP Receive Buffer = %d\n", - recbuflen )); + TRC(fprintf(stderr, "New Size of TCP Receive Buffer = %d\n", + recbuflen )); #endif #if HAVE_GETADDRINFO @@ -464,7 +538,7 @@ int tcpip_connect(COMSTACK h, void *address) h->event = CS_CONNECT; h->state = CS_ST_CONNECTING; - return tcpip_rcvconnect (h); + return tcpip_rcvconnect(h); } /* @@ -472,7 +546,7 @@ int tcpip_connect(COMSTACK h, void *address) */ int tcpip_rcvconnect(COMSTACK h) { -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL tcpip_state *sp = (tcpip_state *)h->cprivate; #endif TRC(fprintf(stderr, "tcpip_rcvconnect\n")); @@ -484,13 +558,13 @@ int tcpip_rcvconnect(COMSTACK h) h->cerrno = CSOUTSTATE; return -1; } -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL if (h->type == ssl_type && !sp->ctx) { + SSL_library_init(); SSL_load_error_strings(); - SSLeay_add_all_algorithms(); - sp->ctx = sp->ctx_alloc = SSL_CTX_new (SSLv23_method()); + sp->ctx = sp->ctx_alloc = SSL_CTX_new(SSLv23_client_method()); if (!sp->ctx) { h->cerrno = CSERRORSSL; @@ -503,24 +577,14 @@ int tcpip_rcvconnect(COMSTACK h) if (!sp->ssl) { - sp->ssl = SSL_new (sp->ctx); - SSL_set_fd (sp->ssl, h->iofile); + sp->ssl = SSL_new(sp->ctx); + SSL_set_fd(sp->ssl, h->iofile); } - res = SSL_connect (sp->ssl); + res = SSL_connect(sp->ssl); if (res <= 0) { - int err = SSL_get_error(sp->ssl, res); - if (err == SSL_ERROR_WANT_READ) - { - h->io_pending = CS_WANT_READ; + if (ssl_check_error(h, sp, res)) return 1; - } - if (err == SSL_ERROR_WANT_WRITE) - { - h->io_pending = CS_WANT_WRITE; - return 1; - } - h->cerrno = CSERRORSSL; return -1; } } @@ -533,7 +597,7 @@ int tcpip_rcvconnect(COMSTACK h) #define CERTF "ztest.pem" #define KEYF "ztest.pem" -static void tcpip_setsockopt (int fd) +static void tcpip_setsockopt(int fd) { #if 0 int len = 4096; @@ -576,13 +640,13 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) } #endif -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL if (h->type == ssl_type && !sp->ctx) { + SSL_library_init(); SSL_load_error_strings(); - SSLeay_add_all_algorithms(); - sp->ctx = sp->ctx_alloc = SSL_CTX_new (SSLv23_method()); + sp->ctx = sp->ctx_alloc = SSL_CTX_new(SSLv23_server_method()); if (!sp->ctx) { h->cerrno = CSERRORSSL; @@ -594,34 +658,47 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) if (sp->ctx_alloc) { int res; - res = SSL_CTX_use_certificate_chain_file(sp->ctx, sp->cert_fname); + res = SSL_CTX_use_certificate_file(sp->ctx, sp->cert_fname, + SSL_FILETYPE_PEM); if (res <= 0) { +#if HAVE_OPENSSL_SSL_H ERR_print_errors_fp(stderr); - exit (2); +#else + fprintf(stderr, " SSL_CTX_use_certificate_file %s failed\n", + sp->cert_fname); +#endif + exit(2); } - res = SSL_CTX_use_PrivateKey_file (sp->ctx, sp->cert_fname, + res = SSL_CTX_use_PrivateKey_file(sp->ctx, sp->cert_fname, SSL_FILETYPE_PEM); if (res <= 0) { +#if HAVE_OPENSSL_SSL_H ERR_print_errors_fp(stderr); - exit (3); +#else + fprintf(stderr, " SSL_CTX_use_certificate_file %s failed\n", + sp->cert_fname); +#endif + exit(3); } - res = SSL_CTX_check_private_key (sp->ctx); +#if HAVE_OPENSSL_SSL_H + res = SSL_CTX_check_private_key(sp->ctx); if (res <= 0) { ERR_print_errors_fp(stderr); exit(5); } +#endif } - TRC (fprintf (stderr, "ssl_bind\n")); + TRC(fprintf(stderr, "ssl_bind\n")); } else { - TRC (fprintf (stderr, "tcpip_bind\n")); + TRC(fprintf(stderr, "tcpip_bind\n")); } #else - TRC (fprintf (stderr, "tcpip_bind\n")); + TRC(fprintf(stderr, "tcpip_bind\n")); #endif #ifndef WIN32 if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) @@ -685,7 +762,15 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, ) h->cerrno = CSNODATA; else + { +#ifdef WIN32 + shutdown(h->iofile, SD_RECEIVE); +#else + shutdown(h->iofile, SHUT_RD); +#endif + listen(h->iofile, SOMAXCONN); h->cerrno = CSYSERR; + } return -1; } if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_in)) @@ -705,7 +790,7 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, return -1; } h->state = CS_ST_INCON; - tcpip_setsockopt (h->newfd); + tcpip_setsockopt(h->newfd); return 0; } @@ -761,8 +846,8 @@ COMSTACK tcpip_accept(COMSTACK h) #endif h->newfd = -1; } - xfree (cnew); - xfree (state); + xfree(cnew); + xfree(state); return 0; } h->newfd = -1; @@ -776,42 +861,41 @@ COMSTACK tcpip_accept(COMSTACK h) cnew->state = CS_ST_ACCEPT; h->state = CS_ST_IDLE; -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL 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); + state->ssl = SSL_new(state->ctx); + SSL_set_fd(state->ssl, cnew->iofile); } #endif + state->connect_request_buf = 0; + state->connect_response_buf = 0; h = cnew; } if (h->state == CS_ST_ACCEPT) { -#if HAVE_OPENSSL_SSL_H + +#if ENABLE_SSL tcpip_state *state = (tcpip_state *)h->cprivate; if (state->ctx) { - int res = SSL_accept (state->ssl); - TRC(fprintf(stderr, "SSL_accept\n")); + int res; + errno = 0; + res = SSL_accept(state->ssl); + TRC(fprintf(stderr, "SSL_accept res=%d\n", res)); if (res <= 0) { - int err = SSL_get_error(state->ssl, res); - if (err == SSL_ERROR_WANT_READ) + if (ssl_check_error(h, state, res)) { - h->io_pending = CS_WANT_READ; return h; } - if (err == SSL_ERROR_WANT_WRITE) - { - h->io_pending = CS_WANT_WRITE; - return h; - } - cs_close (h); + cs_close(h); return 0; } + TRC(fprintf(stderr, "SSL_accept complete\n")); } #endif } @@ -854,7 +938,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) sp->altsize = tmpi; } h->io_pending = 0; - while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread))) + while (!(berlen = (*sp->complete)(*buf, hasread))) { if (!*bufsize) { @@ -926,8 +1010,8 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) return -1; } } - TRC (fprintf (stderr, " Out of read loop with hasread=%d, berlen=%d\n", - hasread, berlen)); + 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) { @@ -958,7 +1042,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) } -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL /* * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer, * 0=connection closed. @@ -985,7 +1069,7 @@ int ssl_get(COMSTACK h, char **buf, int *bufsize) sp->altsize = tmpi; } h->io_pending = 0; - while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread))) + while (!(berlen = (*sp->complete)(*buf, hasread))) { if (!*bufsize) { @@ -995,24 +1079,12 @@ int ssl_get(COMSTACK h, char **buf, int *bufsize) 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); + 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; - break; - } - if (ssl_err == SSL_ERROR_WANT_WRITE) - { - h->io_pending = CS_WANT_WRITE; + if (ssl_check_error(h, sp, res)) break; - } - if (res == 0) - return 0; - h->cerrno = CSERRORSSL; return -1; } hasread += res; @@ -1112,7 +1184,7 @@ int tcpip_put(COMSTACK h, char *buf, int size) } -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL /* * Returns 1, 0 or -1 * In nonblocking mode, you must call again with same buffer while @@ -1138,22 +1210,12 @@ int ssl_put(COMSTACK h, char *buf, int size) } while (state->towrite > state->written) { - res = SSL_write (state->ssl, buf + state->written, - size - 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; + if (ssl_check_error(h, state, res)) return 1; - } - if (ssl_err == SSL_ERROR_WANT_WRITE) - { - h->io_pending = CS_WANT_WRITE; - return 1; - } - h->cerrno = CSERRORSSL; return -1; } state->written += res; @@ -1173,10 +1235,10 @@ int tcpip_close(COMSTACK h) TRC(fprintf(stderr, "tcpip_close\n")); if (h->iofile != -1) { -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL if (sp->ssl) { - SSL_shutdown (sp->ssl); + SSL_shutdown(sp->ssl); } #endif #ifdef WIN32 @@ -1187,20 +1249,22 @@ int tcpip_close(COMSTACK h) } if (sp->altbuf) xfree(sp->altbuf); -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL if (sp->ssl) { - TRC (fprintf(stderr, "SSL_free\n")); - SSL_free (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); + SSL_CTX_free(sp->ctx_alloc); #endif #if HAVE_GETADDRINFO if (sp->ai) freeaddrinfo(sp->ai); #endif + xfree(sp->connect_request_buf); + xfree(sp->connect_response_buf); xfree(sp); xfree(h); return 0; @@ -1256,7 +1320,7 @@ char *tcpip_addrstr(COMSTACK h) sprintf(buf, "http:%s", r); else sprintf(buf, "tcp:%s", r); -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL if (sp->ctx) { if (h->protocol == PROTO_HTTP) @@ -1292,7 +1356,7 @@ int static tcpip_set_blocking(COMSTACK p, int flags) return 1; } -#if HAVE_OPENSSL_SSL_H +#if ENABLE_SSL int cs_set_ssl_ctx(COMSTACK cs, void *ctx) { struct tcpip_state *sp; @@ -1330,7 +1394,8 @@ int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len) SSL *ssl = (SSL *) cs_get_ssl(cs); if (ssl) { - X509 *server_cert = SSL_get_peer_certificate (ssl); +#if HAVE_OPENSSL_SSL_H + X509 *server_cert = SSL_get_peer_certificate(ssl); if (server_cert) { BIO *bio = BIO_new(BIO_s_mem()); @@ -1343,6 +1408,7 @@ int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len) BIO_free(bio); return 1; } +#endif } return 0; } @@ -1368,6 +1434,38 @@ int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname) } #endif + +static int tcpip_put_connect(COMSTACK h, char *buf, int size) +{ + struct tcpip_state *state = (struct tcpip_state *)h->cprivate; + + int r = tcpip_put(h, state->connect_request_buf, + state->connect_request_len); + if (r == 0) + { + /* it's sent */ + h->f_put = tcpip_put; /* switch to normal tcpip put */ + r = tcpip_put(h, buf, size); + } + return r; +} + +static int tcpip_get_connect(COMSTACK h, char **buf, int *bufsize) +{ + struct tcpip_state *state = (struct tcpip_state *)h->cprivate; + int r; + + r = tcpip_get(h, &state->connect_response_buf, + &state->connect_response_len); + if (r < 1) + return r; + /* got the connect response completely */ + state->complete = cs_complete_auto; /* switch to normal tcpip get */ + h->f_get = tcpip_get; + return tcpip_get(h, buf, bufsize); +} + + /* * Local variables: * c-basic-offset: 4