X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Ftcpip.c;h=ad93f01e41f157029c145cb7fabe90c73d57de75;hp=3fa26defb4e4971ce5e8f4c5ad313b90f5a6725b;hb=b3e4cf00229da3e4bdb6931b836f834fbcb43118;hpb=5ba0bc49a8588ea1df229f4f1d60a8629acf71cf diff --git a/src/tcpip.c b/src/tcpip.c index 3fa26de..ad93f01 100644 --- a/src/tcpip.c +++ b/src/tcpip.c @@ -1,11 +1,14 @@ /* This file is part of the YAZ toolkit. - * Copyright (C) 1995-2009 Index Data + * Copyright (C) 1995-2012 Index Data * See the file LICENSE for details. */ /** * \file tcpip.c * \brief Implements TCP/IP + SSL COMSTACK. */ +#if HAVE_CONFIG_H +#include +#endif #include #include @@ -70,7 +73,7 @@ #include #include -static int tcpip_close(COMSTACK h); +static void 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); @@ -90,7 +93,7 @@ static int ssl_put(COMSTACK h, char *buf, int size); #endif static COMSTACK tcpip_accept(COMSTACK h); -static char *tcpip_addrstr(COMSTACK h); +static const char *tcpip_addrstr(COMSTACK h); static void *tcpip_straddr(COMSTACK h, const char *str); #if 0 @@ -201,12 +204,11 @@ COMSTACK tcpip_type(int s, int flags, int protocol, void *vp) p->f_addrstr = tcpip_addrstr; p->f_straddr = tcpip_straddr; p->f_set_blocking = tcpip_set_blocking; - p->max_recv_bytes = 5000000; + p->max_recv_bytes = 128 * 1024 * 1024; p->state = s < 0 ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */ p->event = CS_NONE; p->cerrno = 0; - p->stackerr = 0; p->user = 0; #if HAVE_GNUTLS_H @@ -364,6 +366,13 @@ struct addrinfo *tcpip_getaddrinfo(const char *str, const char *port) if (!strcmp("@", host)) { hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + error = getaddrinfo(0, port, &hints, &res); + } + else if (!strcmp("@6", host)) + { + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; error = getaddrinfo(0, port, &hints, &res); } else @@ -423,9 +432,14 @@ void *tcpip_straddr(COMSTACK h, const char *str) { tcpip_state *sp = (tcpip_state *)h->cprivate; const char *port = "210"; - struct addrinfo *ai; + struct addrinfo *ai = 0; if (h->protocol == PROTO_HTTP) - port = "80"; + { + if (h->type == ssl_type) + port = "443"; + else + port = "80"; + } if (!tcpip_init()) return 0; @@ -435,31 +449,17 @@ void *tcpip_straddr(COMSTACK h, const char *str) if (sp->ai && h->state == CS_ST_UNBND) { int s = -1; - /* try to make IPV6 socket first */ for (ai = sp->ai; ai; ai = ai->ai_next) { - if (ai->ai_family == AF_INET6) - { - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s != -1) - break; - } - } - if (s == -1) - { - /* no IPV6 could be made.. Try them all */ - for (ai = sp->ai; ai; ai = ai->ai_next) - { - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s != -1) - break; - } + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s != -1) + break; } if (s == -1) return 0; assert(ai); h->iofile = s; - + if (!tcpip_set_blocking(h, h->flags)) return 0; } @@ -471,7 +471,12 @@ void *tcpip_straddr(COMSTACK h, const char *str) tcpip_state *sp = (tcpip_state *)h->cprivate; int port = 210; if (h->protocol == PROTO_HTTP) - port = 80; + { + if (h->type == ssl_type) + port = 443; + else + port = 80; + } if (!tcpip_init()) return 0; @@ -495,7 +500,7 @@ void *tcpip_straddr(COMSTACK h, const char *str) int tcpip_more(COMSTACK h) { tcpip_state *sp = (tcpip_state *)h->cprivate; - + return sp->altlen && (*sp->complete)(sp->altbuf, sp->altlen); } @@ -513,10 +518,6 @@ int tcpip_connect(COMSTACK h, void *address) struct sockaddr_in *add = (struct sockaddr_in *) address; #endif int r; -#ifdef __sun__ - int recbuflen; - YAZ_SOCKLEN_T rbufsize = sizeof(recbuflen); -#endif TRC(fprintf(stderr, "tcpip_connect\n")); h->io_pending = 0; if (h->state != CS_ST_UNBND) @@ -524,33 +525,6 @@ int tcpip_connect(COMSTACK h, void *address) h->cerrno = CSOUTSTATE; return -1; } -#ifdef __sun__ - /* On Suns, you must set a bigger Receive Buffer BEFORE a call to connect - * 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 ) - { - h->cerrno = CSYSERR; - return -1; - } - 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 ) - { - h->cerrno = CSYSERR; - return -1; - } - 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 )); -#endif - #if HAVE_GETADDRINFO r = connect(h->iofile, ai->ai_addr, ai->ai_addrlen); freeaddrinfo(sp->ai); @@ -608,17 +582,17 @@ int tcpip_rcvconnect(COMSTACK h) { int res; gnutls_global_init(); - + tcpip_create_cred(h); gnutls_init(&sp->session, GNUTLS_CLIENT); gnutls_set_default_priority(sp->session); gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE, sp->cred_ptr->xcred); - + /* cast to intermediate size_t to avoid GCC warning. */ - gnutls_transport_set_ptr(sp->session, - (gnutls_transport_ptr_t) + gnutls_transport_set_ptr(sp->session, + (gnutls_transport_ptr_t) (size_t) h->iofile); res = gnutls_handshake(sp->session); if (res < 0) @@ -667,33 +641,12 @@ int tcpip_rcvconnect(COMSTACK h) #define CERTF "ztest.pem" #define KEYF "ztest.pem" -static void tcpip_setsockopt(int fd) -{ -#if 0 - int len = 4096; - int set = 1; - - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&set, sizeof(int))) - { - yaz_log(LOG_WARN|LOG_ERRNO, "setsockopt TCP_NODELAY"); - } - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&len, sizeof(int))) - { - yaz_log(LOG_WARN|LOG_ERRNO, "setsockopt SNDBUF"); - } - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int))) - { - yaz_log(LOG_WARN|LOG_ERRNO, "setsockopt RCVBUF"); - } -#endif -} - static int tcpip_bind(COMSTACK h, void *address, int mode) { int r; tcpip_state *sp = (tcpip_state *)h->cprivate; -#if HAVE_GETADDRINFO - struct addrinfo *ai = (struct addrinfo *) address; +#if HAVE_GETADDRINFO + struct addrinfo *ai = (struct addrinfo *) address; #else struct sockaddr *addr = (struct sockaddr *)address; #endif @@ -711,7 +664,7 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) tcpip_create_cred(h); - res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred, + res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred, sp->cert_fname, sp->cert_fname, GNUTLS_X509_FMT_PEM); @@ -770,14 +723,13 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) TRC(fprintf(stderr, "tcpip_bind\n")); #endif #ifndef WIN32 - if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) + if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one)) < 0) { h->cerrno = CSYSERR; return -1; } #endif - tcpip_setsockopt(h->iofile); #if HAVE_GETADDRINFO r = bind(h->iofile, ai->ai_addr, ai->ai_addrlen); freeaddrinfo(sp->ai); @@ -805,8 +757,12 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, int (*check_ip)(void *cd, const char *a, int len, int t), void *cd) { +#ifdef WIN32 + /* we don't get peer address on Windows (via accept) */ +#else struct sockaddr_in addr; YAZ_SOCKLEN_T len = sizeof(addr); +#endif TRC(fprintf(stderr, "tcpip_listen pid=%d\n", getpid())); if (h->state != CS_ST_IDLE) @@ -825,7 +781,7 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, #ifdef WIN32 WSAGetLastError() == WSAEWOULDBLOCK #else - yaz_errno() == EWOULDBLOCK + yaz_errno() == EWOULDBLOCK #ifdef EAGAIN #if EAGAIN != EWOULDBLOCK || yaz_errno() == EAGAIN @@ -846,6 +802,10 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, } return -1; } +#ifdef WIN32 + if (addrlen) + *addrlen = 0; +#else if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_in)) memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in)); else if (addrlen) @@ -862,8 +822,8 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, h->newfd = -1; return -1; } +#endif h->state = CS_ST_INCON; - tcpip_setsockopt(h->newfd); return 0; } @@ -934,7 +894,7 @@ COMSTACK tcpip_accept(COMSTACK h) #endif cnew->state = CS_ST_ACCEPT; h->state = CS_ST_IDLE; - + #if HAVE_GNUTLS_H state->cred_ptr = st->cred_ptr; state->session = 0; @@ -958,7 +918,7 @@ COMSTACK tcpip_accept(COMSTACK h) return 0; } res = gnutls_credentials_set(state->session, - GNUTLS_CRD_CERTIFICATE, + GNUTLS_CRD_CERTIFICATE, st->cred_ptr->xcred); if (res != GNUTLS_E_SUCCESS) { @@ -967,7 +927,7 @@ COMSTACK tcpip_accept(COMSTACK h) return 0; } /* cast to intermediate size_t to avoid GCC warning. */ - gnutls_transport_set_ptr(state->session, + gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr_t) (size_t) cnew->iofile); } @@ -1090,7 +1050,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) TRC(fprintf(stderr, " recv res=%d, hasread=%d\n", res, hasread)); if (res < 0) { - TRC(fprintf(stderr, " recv errno=%d, (%s)\n", yaz_errno(), + TRC(fprintf(stderr, " recv errno=%d, (%s)\n", yaz_errno(), strerror(yaz_errno()))); #ifdef WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) @@ -1104,8 +1064,8 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) return -1; } #else - if (yaz_errno() == EWOULDBLOCK -#ifdef EAGAIN + if (yaz_errno() == EWOULDBLOCK +#ifdef EAGAIN #if EAGAIN != EWOULDBLOCK || yaz_errno() == EAGAIN #endif @@ -1209,7 +1169,12 @@ int ssl_get(COMSTACK h, char **buf, int *bufsize) #if HAVE_GNUTLS_H res = gnutls_record_recv(sp->session, *buf + hasread, CS_TCPIP_BUFCHUNK); - if (res < 0) + if (res == 0) + { + TRC(fprintf(stderr, "gnutls_record_recv returned 0\n")); + return 0; + } + else if (res < 0) { if (ssl_check_error(h, sp, res)) break; @@ -1280,7 +1245,7 @@ int tcpip_put(COMSTACK h, char *buf, int size) { if ((res = send(h->iofile, buf + state->written, size - - state->written, + state->written, #ifdef MSG_NOSIGNAL MSG_NOSIGNAL #else @@ -1292,7 +1257,7 @@ int tcpip_put(COMSTACK h, char *buf, int size) #ifdef WIN32 WSAGetLastError() == WSAEWOULDBLOCK #else - yaz_errno() == EWOULDBLOCK + yaz_errno() == EWOULDBLOCK #ifdef EAGAIN #if EAGAIN != EWOULDBLOCK || yaz_errno() == EAGAIN @@ -1349,7 +1314,7 @@ int ssl_put(COMSTACK h, char *buf, int size) while (state->towrite > state->written) { #if HAVE_GNUTLS_H - res = gnutls_record_send(state->session, buf + state->written, + res = gnutls_record_send(state->session, buf + state->written, size - state->written); if (res <= 0) { @@ -1358,7 +1323,7 @@ int ssl_put(COMSTACK h, char *buf, int size) return -1; } #else - res = SSL_write(state->ssl, buf + state->written, + res = SSL_write(state->ssl, buf + state->written, size - state->written); if (res <= 0) { @@ -1377,7 +1342,7 @@ int ssl_put(COMSTACK h, char *buf, int size) } #endif -int tcpip_close(COMSTACK h) +void tcpip_close(COMSTACK h) { tcpip_state *sp = (struct tcpip_state *)h->cprivate; @@ -1412,7 +1377,7 @@ int tcpip_close(COMSTACK h) if (--(sp->cred_ptr->ref) == 0) { - TRC(fprintf(stderr, "Removed credentials %p pid=%d\n", + TRC(fprintf(stderr, "Removed credentials %p pid=%d\n", sp->cred_ptr->xcred, getpid())); gnutls_certificate_free_credentials(sp->cred_ptr->xcred); xfree(sp->cred_ptr); @@ -1437,10 +1402,9 @@ int tcpip_close(COMSTACK h) xfree(sp->connect_response_buf); xfree(sp); xfree(h); - return 0; } -char *tcpip_addrstr(COMSTACK h) +const char *tcpip_addrstr(COMSTACK h) { tcpip_state *sp = (struct tcpip_state *)h->cprivate; char *r = 0, *buf = sp->buf; @@ -1449,27 +1413,27 @@ char *tcpip_addrstr(COMSTACK h) char host[120]; struct sockaddr_storage addr; YAZ_SOCKLEN_T len = sizeof(addr); - + if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0) { h->cerrno = CSYSERR; return 0; } - if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1, - 0, 0, + if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1, + 0, 0, (h->flags & CS_FLAGS_NUMERICHOST) ? NI_NUMERICHOST : 0)) { r = "unknown"; } else r = host; - + #else struct sockaddr_in addr; YAZ_SOCKLEN_T len = sizeof(addr); struct hostent *host; - + if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0) { h->cerrno = CSYSERR; @@ -1483,7 +1447,7 @@ char *tcpip_addrstr(COMSTACK h) r = (char*) host->h_name; } if (!r) - r = inet_ntoa(addr.sin_addr); + r = inet_ntoa(addr.sin_addr); #endif if (h->protocol == PROTO_HTTP) @@ -1513,7 +1477,7 @@ char *tcpip_addrstr(COMSTACK h) static int tcpip_set_blocking(COMSTACK p, int flags) { unsigned long flag; - + #ifdef WIN32 flag = (flags & CS_FLAGS_BLOCKING) ? 0 : 1; if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0) @@ -1534,39 +1498,130 @@ static int tcpip_set_blocking(COMSTACK p, int flags) return 1; } +#if HAVE_GNUTLS_H +static const char *bin2hex(const void *bin, size_t bin_size) +{ + static char printable[110]; + const unsigned char *_bin = bin; + char *print; + size_t i; + if (bin_size > 50) + bin_size = 50; + print = printable; + for (i = 0; i < bin_size; i++) + { + sprintf(print, "%.2x ", _bin[i]); + print += 2; + } + return printable; +} +#endif + void cs_print_session_info(COMSTACK cs) { #if HAVE_GNUTLS_H - struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; - if (sp->session) + if (cs->type == ssl_type) { - if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509) - return; - printf("X509 certificate\n"); + struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; + if (sp->session) + { + const gnutls_datum_t *cert_list; + unsigned i, cert_list_size; + if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509) + return; + printf("X509 certificate\n"); + cert_list = gnutls_certificate_get_peers(sp->session, + &cert_list_size); + printf("Peer provided %u certificates\n", cert_list_size); + for (i = 0; i < cert_list_size; i++) + { + gnutls_x509_crt_t cert; + gnutls_datum_t cinfo; + int ret; + time_t expiration_time, activation_time; + size_t size; + char serial[40]; + char dn[256]; + unsigned int algo, bits; + + /* we only print information about the first certificate. */ + gnutls_x509_crt_init(&cert); + gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER); + printf("Certificate info %d:\n", i + 1); + /* This is the preferred way of printing short information about + a certificate. */ + ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_ONELINE, &cinfo); + if (ret == 0) + { + printf("\t%s\n", cinfo.data); + gnutls_free(cinfo.data); + } + + /* If you want to extract fields manually for some other reason, + below are popular example calls. */ + + expiration_time = gnutls_x509_crt_get_expiration_time(cert); + activation_time = gnutls_x509_crt_get_activation_time(cert); + + printf("\tCertificate is valid since: %s", ctime(&activation_time)); + printf("\tCertificate expires: %s", ctime(&expiration_time)); + + /* Print the serial number of the certificate. */ + size = sizeof(serial); + gnutls_x509_crt_get_serial(cert, serial, &size); + + printf("\tCertificate serial number: %s\n", + bin2hex(serial, size)); + + /* Extract some of the public key algorithm's parameters + */ + algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); + + printf("Certificate public key: %s", + gnutls_pk_algorithm_get_name(algo)); + + /* Print the version of the X.509 certificate. */ + printf("\tCertificate version: #%d\n", + gnutls_x509_crt_get_version(cert)); + + size = sizeof(dn); + gnutls_x509_crt_get_dn(cert, dn, &size); + printf("\tDN: %s\n", dn); + + size = sizeof(dn); + gnutls_x509_crt_get_issuer_dn(cert, dn, &size); + printf("\tIssuer's DN: %s\n", dn); + + gnutls_x509_crt_deinit(cert); + + } + } } #elif HAVE_OPENSSL_SSL_H - struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; - SSL *ssl = (SSL *) sp->ssl; - if (ssl) + if (cs->type == ssl_type) { - X509 *server_cert = SSL_get_peer_certificate(ssl); - - if (server_cert) + struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; + SSL *ssl = (SSL *) sp->ssl; + if (ssl) { - char *pem_buf; - int pem_len; - BIO *bio = BIO_new(BIO_s_mem()); + X509 *server_cert = SSL_get_peer_certificate(ssl); + if (server_cert) + { + char *pem_buf; + int pem_len; + BIO *bio = BIO_new(BIO_s_mem()); - /* get PEM buffer in memory */ - PEM_write_bio_X509(bio, server_cert); - pem_len = BIO_get_mem_data(bio, &pem_buf); - fwrite(pem_buf, pem_len, 1, stdout); + /* get PEM buffer in memory */ + PEM_write_bio_X509(bio, server_cert); + pem_len = BIO_get_mem_data(bio, &pem_buf); + fwrite(pem_buf, pem_len, 1, stdout); - /* print all info on screen .. */ - X509_print_fp(stdout, server_cert); - BIO_free(bio); + /* print all info on screen .. */ + X509_print_fp(stdout, server_cert); + BIO_free(bio); - X509_free(server_cert); + X509_free(server_cert); + } } } #endif @@ -1575,47 +1630,44 @@ void cs_print_session_info(COMSTACK cs) void *cs_get_ssl(COMSTACK cs) { #if HAVE_OPENSSL_SSL_H - struct tcpip_state *sp; - if (!cs || cs->type != ssl_type) - return 0; - sp = (struct tcpip_state *) cs->cprivate; - return sp->ssl; -#else - return 0; + if (cs && cs->type == ssl_type) + { + struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; + return sp->ssl; + } #endif + return 0; } int cs_set_ssl_ctx(COMSTACK cs, void *ctx) { #if ENABLE_SSL - struct tcpip_state *sp; - if (!cs || cs->type != ssl_type) - return 0; - sp = (struct tcpip_state *) cs->cprivate; + if (cs && cs->type == ssl_type) + { #if HAVE_OPENSSL_SSL_H - if (sp->ctx_alloc) - return 0; - sp->ctx = (SSL_CTX *) ctx; + struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; + if (sp->ctx_alloc) + return 0; + sp->ctx = (SSL_CTX *) ctx; #endif - return 1; -#else - return 0; + return 1; + } #endif + return 0; } int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname) { #if ENABLE_SSL - struct tcpip_state *sp; - if (!cs || cs->type != ssl_type) - return 0; - sp = (struct tcpip_state *) cs->cprivate; - strncpy(sp->cert_fname, fname, sizeof(sp->cert_fname)-1); - sp->cert_fname[sizeof(sp->cert_fname)-1] = '\0'; - return 1; -#else - return 0; + if (cs && cs->type == ssl_type) + { + struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; + strncpy(sp->cert_fname, fname, sizeof(sp->cert_fname)-1); + sp->cert_fname[sizeof(sp->cert_fname)-1] = '\0'; + return 1; + } #endif + return 0; } int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len) @@ -1662,7 +1714,7 @@ 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, + r = tcpip_get(h, &state->connect_response_buf, &state->connect_response_len); if (r < 1) return r;