diff --git a/client/client.c b/client/client.c index 4693cc54..f22f3ef0 100644 --- a/client/client.c +++ b/client/client.c @@ -277,6 +277,17 @@ static void add_otherInfos(Z_APDU *a) } } +static void print_comstack_error(const char *operation, COMSTACK cs) +{ + const char *details = 0; + int code = cs_get_error(cs, &details); + + fprintf(stderr, "%s: cs=%d msg=%s", operation, code, cs_errmsg(code)); + if (details) + fprintf(stderr, " details=%s", details); + fprintf(stderr, "\n"); +} + int send_apdu(Z_APDU *a) { char *buf; @@ -301,7 +312,7 @@ int send_apdu(Z_APDU *a) do_hex_dump(buf, len); if (cs_put(conn, buf, len) < 0) { - fprintf(stderr, "cs_put: %s\n", cs_errmsg(cs_errno(conn))); + print_comstack_error("cs_put", conn); close_session(); return 0; } @@ -736,7 +747,7 @@ static int session_connect_base(const char *arg, const char **basep) fflush(stdout); if (cs_connect(conn, add) < 0) { - printf("error = %s\n", cs_strerror(conn)); + print_comstack_error("connect failed", conn); cs_close(conn); conn = 0; return 0; @@ -1322,6 +1333,7 @@ static int send_gdu(Z_GDU *gdu) if (r >= 0) return 2; + print_comstack_error("cs_put", conn); } return 0; } @@ -2740,22 +2752,27 @@ static WRBUF get_url(const char *uri, WRBUF username, WRBUF password, "text/xml"); if (!z_GDU(out, &gdu, 0, 0)) { - yaz_log(YLOG_WARN, "Can not encode HTTP request URL:%s", uri); + fprintf(stderr, "Can not encode HTTP request URL:%s\n", uri); } else { void *add; int cs_flags = CS_FLAGS_BLOCKING | (check_cert ? CS_FLAGS_CHECK_CERT : 0); COMSTACK conn = cs_create_host(uri, cs_flags, &add); - if (cs_connect(conn, add) < 0) - yaz_log(YLOG_WARN, "Can not connect to URL:%s", uri); + if (!conn) + fprintf(stderr, "Can not create connection for URL:%s\n", uri); + else if (cs_connect(conn, add) < 0) + { + print_comstack_error("Can not connect", conn); + cs_close(conn); + } else { int len; char *buf = odr_getbuf(out, &len, 0); if (cs_put(conn, buf, len) < 0) - yaz_log(YLOG_WARN, "cs_put failed URL:%s", uri); + print_comstack_error("cs_put failed", conn); else { char *netbuffer = 0; @@ -2763,7 +2780,12 @@ static WRBUF get_url(const char *uri, WRBUF username, WRBUF password, int res = cs_get(conn, &netbuffer, &netlen); if (res <= 0) { - yaz_log(YLOG_WARN, "cs_get failed URL:%s", uri); + if (res < 0) + print_comstack_error("cs_get failed", conn); + else + fprintf(stderr, + "Connection closed while reading URL:%s\n", + uri); } else { @@ -2772,7 +2794,7 @@ static WRBUF get_url(const char *uri, WRBUF username, WRBUF password, if (!z_GDU(in, &gdu, 0, 0) || gdu->which != Z_GDU_HTTP_Response) { - yaz_log(YLOG_WARN, "decode failed URL: %s", uri); + fprintf(stderr, "decode failed URL: %s\n", uri); } else { @@ -4554,6 +4576,8 @@ static void wait_and_handle_response(int one_response_only) res = cs_get(conn, &netbuffer, &netbufferlen); if (res <= 0) { + if (res < 0) + print_comstack_error("cs_get", conn); if (reconnect_ok && protocol == PROTO_HTTP) { cs_close(conn); @@ -4566,7 +4590,12 @@ static void wait_and_handle_response(int one_response_only) int len_out; buf_out = odr_getbuf(out, &len_out, 0); do_hex_dump(buf_out, len_out); - cs_put(conn, buf_out, len_out); + if (cs_put(conn, buf_out, len_out) < 0) + { + print_comstack_error("cs_put", conn); + close_session(); + break; + } odr_reset(out); continue; } @@ -4603,7 +4632,12 @@ static void wait_and_handle_response(int one_response_only) int len_out; buf_out = odr_getbuf(out, &len_out, 0); do_hex_dump(buf_out, len_out); - cs_put(conn, buf_out, len_out); + if (cs_put(conn, buf_out, len_out) < 0) + { + print_comstack_error("cs_put", conn); + close_session(); + break; + } odr_reset(out); continue; } diff --git a/doc/book.xml b/doc/book.xml index 6fec4553..159277ef 100644 --- a/doc/book.xml +++ b/doc/book.xml @@ -9880,6 +9880,17 @@ void odr_choice_bias(ODR o, int what); const char *cs_strerror(COMSTACK h); + + Additional information about the current error can be retrieved with + cs_get_error. The returned string is owned by the + COMSTACK and remains valid until the error is changed or the COMSTACK is + closed. If no additional information is available, + *details is set to null. The function also returns the + current error code. + + + int cs_get_error(COMSTACK h, const char **details); + Summary and Synopsis @@ -9916,6 +9927,8 @@ void odr_choice_bias(ODR o, int what); int cs_look(COMSTACK handle); + int cs_get_error(COMSTACK handle, const char **details); + void *cs_straddr(COMSTACK handle, const char *str); const char *cs_addrstr(COMSTACK h); diff --git a/src/BUILD b/src/BUILD index d465c5f9..260c3777 100644 --- a/src/BUILD +++ b/src/BUILD @@ -230,7 +230,7 @@ cc_library( "dirent", "mutex", "condvar", "thread_id", "gettimeofday", "thread_create", "spipe", "url", "backtrace" ]) - + h_dir(".", ["cclp", "iconv-p", "mime", "mutex-p", + + h_dir(".", ["cclp", "comstack-p", "iconv-p", "mime", "mutex-p", "odr-priv", "sru-p", "zoom-p", "config", "diag-entry" ]) , diff --git a/src/Makefile.am b/src/Makefile.am index fc874f70..96318b4f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -120,7 +120,7 @@ libyaz_la_SOURCES= $(GEN_FILES) \ odr_seq.c odr_oct.c ber_oct.c odr_bit.c ber_bit.c odr_oid.c \ ber_oid.c odr_use.c odr_choice.c odr_any.c ber_any.c odr.c odr_mem.c \ dumpber.c odr_enum.c odr-priv.h \ - comstack.c tcpip.c unix.c \ + comstack.c comstack-p.h tcpip.c unix.c \ prt-ext.c \ proxunit.c \ ill-get.c \ diff --git a/src/comstack-p.h b/src/comstack-p.h new file mode 100644 index 00000000..b2579313 --- /dev/null +++ b/src/comstack-p.h @@ -0,0 +1,52 @@ +/* This file is part of the YAZ toolkit. + * Copyright (C) Index Data. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Index Data nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS AND 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. + */ + +/** + * \file comstack-p.h + * \brief Private Header for COMSTACK + */ + +#ifndef COMSTACK_P_H +#define COMSTACK_P_H + +#include + +YAZ_BEGIN_CDECL + +const char *yaz_tcpip_get_error_details(COMSTACK cs); + +YAZ_END_CDECL + +#endif +/* + * Local variables: + * c-basic-offset: 4 + * c-file-style: "Stroustrup" + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ diff --git a/src/comstack.c b/src/comstack.c index 997d9246..810ec374 100644 --- a/src/comstack.c +++ b/src/comstack.c @@ -16,7 +16,7 @@ #include #include -#include +#include "comstack-p.h" #include #include #include @@ -48,6 +48,13 @@ const char *cs_strerror(COMSTACK h) return cs_errmsg(h->cerrno); } +int cs_get_error(COMSTACK cs, const char **details) +{ + if (details) + *details = yaz_tcpip_get_error_details(cs); + return cs->cerrno; +} + void cs_get_host_args(const char *type_and_host, const char **args) { *args = ""; @@ -470,4 +477,3 @@ void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes) * End: * vim: shiftwidth=4 tabstop=8 expandtab */ - diff --git a/src/statserv.c b/src/statserv.c index e22176e2..4c40708d 100644 --- a/src/statserv.c +++ b/src/statserv.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef WIN32 #include @@ -132,6 +133,25 @@ static int log_session = 0; /* one-line logs for session */ static int log_sessiondetail = 0; /* more detailed stuff */ static int log_server = 0; +static void log_comstack_error(int level, COMSTACK cs, const char *fmt, ...) +{ + char message[512]; + const char *details = 0; + int code = cs_get_error(cs, &details); + va_list ap; + + va_start(ap, fmt); + yaz_vsnprintf(message, sizeof(message), fmt, ap); + va_end(ap); + + if (details) + yaz_log(level, "%s: cs=%d msg=%s details=%s", + message, code, cs_errmsg(code), details); + else + yaz_log(level, "%s: cs=%d msg=%s", + message, code, cs_errmsg(code)); +} + /** get_logbits sets global loglevel bits */ static void get_logbits(int force) { /* needs to be called after parsing cmd-line args that can set loglevels!*/ @@ -857,7 +877,8 @@ static void listener(IOCHAN h, int event) if ((res = cs_listen(line, 0, 0)) < 0) { - yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed"); + log_comstack_error(YLOG_FATAL|YLOG_ERRNO, line, + "cs_listen failed"); return; } else if (res == 1) @@ -866,7 +887,7 @@ static void listener(IOCHAN h, int event) new_line = cs_accept(line); if (!new_line) { - yaz_log(YLOG_FATAL, "Accept failed."); + log_comstack_error(YLOG_FATAL, line, "Accept failed"); return; } yaz_log(YLOG_DEBUG, "Accept ok"); @@ -955,7 +976,8 @@ static void listener(IOCHAN h, int event) if ((res = cs_listen_check(line, 0, 0, control_block.check_ip, control_block.daemon_name)) < 0) { - yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed"); + log_comstack_error(YLOG_WARN|YLOG_ERRNO, line, + "cs_listen failed"); return; } else if (res == 1) @@ -966,7 +988,7 @@ static void listener(IOCHAN h, int event) new_line = cs_accept(line); if (!new_line) { - yaz_log(YLOG_FATAL, "Accept failed."); + log_comstack_error(YLOG_FATAL, line, "Accept failed"); iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */ return; } @@ -1170,11 +1192,11 @@ static int add_listener(char *where, int listen_id) if (cs_bind(l, ap, CS_SERVER) < 0) { + int level = YLOG_FATAL; + if (cs_errno(l) == CSYSERR) - yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where); - else - yaz_log(YLOG_FATAL, "Failed to bind to %s: %s", where, - cs_strerror(l)); + level |= YLOG_ERRNO; + log_comstack_error(level, l, "Failed to bind to %s", where); cs_close(l); return -1; } @@ -1532,4 +1554,3 @@ int statserv_main(int argc, char **argv, * End: * vim: shiftwidth=4 tabstop=8 expandtab */ - diff --git a/src/tcpip.c b/src/tcpip.c index eff830b1..442bc64c 100644 --- a/src/tcpip.c +++ b/src/tcpip.c @@ -62,7 +62,7 @@ #endif #include -#include +#include "comstack-p.h" #include #include #include @@ -109,6 +109,7 @@ struct tcpip_cred_ptr { gnutls_certificate_credentials_t xcred; int ref; }; +static void tcpip_release_cred(struct tcpip_cred_ptr **ptr); #endif /* this state is used for both SSL and straight TCP/IP */ @@ -145,6 +146,7 @@ typedef struct tcpip_state WRBUF connect_request; char *connect_response_buf; int connect_response_len; + WRBUF error_details; /* additional information for the current error */ } tcpip_state; static int log_level = 0; @@ -206,9 +208,29 @@ static struct tcpip_state *tcpip_state_create(void) sp->connect_request = 0; sp->connect_response_buf = 0; sp->connect_response_len = 0; + sp->error_details = wrbuf_alloc(); return sp; } +static void tcpip_state_destroy(tcpip_state *sp) +{ +#if HAVE_GNUTLS_H + if (sp->session) + gnutls_deinit(sp->session); + tcpip_release_cred(&sp->cred_ptr); +#endif + if (sp->ai) + freeaddrinfo(sp->ai); + xfree(sp->altbuf); + xfree(sp->bind_host); + xfree(sp->host_port); + xfree(sp->connect_host); + wrbuf_destroy(sp->connect_request); + xfree(sp->connect_response_buf); + wrbuf_destroy(sp->error_details); + xfree(sp); +} + /* * This function is always called through the cs_create() macro. * s >= 0: socket has already been established for us. @@ -254,6 +276,15 @@ COMSTACK tcpip_type(int s, int flags, int protocol, void *vp) return p; } +static void cs_set_error(COMSTACK cs, int error, const char *details) +{ + struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate; + wrbuf_rewind(sp->error_details); + if (details) + wrbuf_puts(sp->error_details, details); + cs->cerrno = error; +} + static void connect_and_bind(COMSTACK p, const char *connect_host, const char *connect_auth, const char *bind_host) @@ -393,7 +424,7 @@ static int ssl_check_again(COMSTACK h, tcpip_state *sp, int res) h->io_pending = dir ? CS_WANT_WRITE : CS_WANT_READ; return 1; } - h->cerrno = CSERRORSSL; + cs_set_error(h, CSERRORSSL, gnutls_strerror(res)); return 0; } #endif @@ -536,7 +567,7 @@ static struct addrinfo *create_net_socket(COMSTACK h) if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one)) < 0) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return 0; } #endif @@ -554,7 +585,7 @@ static struct addrinfo *create_net_socket(COMSTACK h) } if (r) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); freeaddrinfo(ai); return 0; } @@ -676,7 +707,7 @@ static int cont_connect(COMSTACK h) return tcpip_connect(h, ai); } } - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } @@ -695,7 +726,7 @@ int tcpip_connect(COMSTACK h, void *address) h->io_pending = 0; if (h->state != CS_ST_UNBND) { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE, 0); return -1; } #if RESOLVER_THREAD @@ -764,15 +795,21 @@ static int check_cert(COMSTACK h, tcpip_state *sp) { yaz_log(log_level, "TLS certificate verification error: %s", gnutls_strerror(vr)); - h->cerrno = CSERRORSSL; + cs_set_error(h, CSERRORSSL, gnutls_strerror(vr)); return -1; } if (status != 0) { + char details[600]; + yaz_log(log_level, "TLS certificate verification failed:" " status=%u host=%s", status, vhost ? vhost : ""); - h->cerrno = CSERRORSSL; + yaz_snprintf(details, sizeof(details), + "TLS certificate verification failed:" + " status=%u host=%s", + status, vhost ? vhost : ""); + cs_set_error(h, CSERRORSSL, details); return -1; } } @@ -799,7 +836,7 @@ int tcpip_rcvconnect(COMSTACK h) #endif if (h->state != CS_ST_CONNECTING) { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE, 0); return -1; } if (sp->connect_host) @@ -829,13 +866,13 @@ int tcpip_rcvconnect(COMSTACK h) if (!yaz_decode_http_response_first(sp->connect_response_buf, sp->connect_response_len, &code, 0, 0, 0, 0)) { yaz_log(log_level, "tcpip_rcvconnect connect failed: bad response"); - h->cerrno = CSPROTERR; + cs_set_error(h, CSPROTERR, 0); return -1; } if (code != 200) { yaz_log(log_level, "tcpip_rcvconnect connect failed: status %d", code); - h->cerrno = CSDENY; + cs_set_error(h, CSDENY, 0); return -1; } sp->complete = cs_complete_auto; @@ -861,7 +898,7 @@ int tcpip_rcvconnect(COMSTACK h) if (r < 0) { yaz_log(log_level, "gnutls_certificate_set_x509_system_trust r=%d msg=%s", r, gnutls_strerror(r)); - h->cerrno = CSERRORSSL; + cs_set_error(h, CSERRORSSL, gnutls_strerror(r)); tcpip_release_cred(&sp->cred_ptr); return -1; } @@ -879,7 +916,7 @@ int tcpip_rcvconnect(COMSTACK h) res, gnutls_error_is_fatal(res), gnutls_strerror(res)); - h->cerrno = CSERRORSSL; + cs_set_error(h, CSERRORSSL, gnutls_strerror(res)); return -1; } } @@ -960,7 +997,7 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) res, gnutls_error_is_fatal(res), gnutls_strerror(res)); - h->cerrno = CSERRORSSL; + cs_set_error(h, CSERRORSSL, gnutls_strerror(res)); return -1; } } @@ -969,7 +1006,7 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one)) < 0) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } #endif @@ -978,13 +1015,13 @@ static int tcpip_bind(COMSTACK h, void *address, int mode) sp->ai = 0; if (r) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } /* Allow a maximum-sized backlog of waiting-to-connect clients */ if (mode == CS_SERVER && listen(h->iofile, SOMAXCONN) < 0) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } h->state = CS_ST_IDLE; @@ -1006,7 +1043,7 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, yaz_log(log_level, "tcpip_listen h=%p", h); if (h->state != CS_ST_IDLE) { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE, 0); return -1; } #ifdef WIN32 @@ -1028,12 +1065,12 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, #endif #endif ) - h->cerrno = CSNODATA; + cs_set_error(h, CSNODATA, 0); else { shutdown(h->iofile, 0); /* SHUT_RD/SHUT_RECEIVE */ listen(h->iofile, SOMAXCONN); - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); } return -1; } @@ -1048,7 +1085,7 @@ int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, if (check_ip && (*check_ip)(cd, (const char *) &addr, sizeof(addr), AF_INET)) { - h->cerrno = CSDENY; + cs_set_error(h, CSDENY, 0); #ifdef WIN32 closesocket(h->newfd); #else @@ -1085,7 +1122,7 @@ COMSTACK tcpip_accept(COMSTACK h) if (!tcpip_set_blocking(cnew, cnew->flags)) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); if (h->newfd != -1) { #ifdef WIN32 @@ -1095,7 +1132,7 @@ COMSTACK tcpip_accept(COMSTACK h) #endif h->newfd = -1; } - xfree(state); + tcpip_state_destroy(state); xfree(cnew); return 0; } @@ -1113,15 +1150,13 @@ COMSTACK tcpip_accept(COMSTACK h) gnutls_init(&state->session, GNUTLS_SERVER); if (!state->session) { - xfree(cnew); - xfree(state); + tcpip_close(cnew); return 0; } res = gnutls_set_default_priority(state->session); if (res != GNUTLS_E_SUCCESS) { - xfree(cnew); - xfree(state); + tcpip_close(cnew); return 0; } res = gnutls_credentials_set(state->session, @@ -1129,8 +1164,7 @@ COMSTACK tcpip_accept(COMSTACK h) st->cred_ptr->xcred); if (res != GNUTLS_E_SUCCESS) { - xfree(cnew); - xfree(state); + tcpip_close(cnew); return 0; } SET_GNUTLS_SOCKET(state->session, cnew->iofile); @@ -1161,7 +1195,7 @@ COMSTACK tcpip_accept(COMSTACK h) } else { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE, 0); return 0; } h->io_pending = 0; @@ -1203,7 +1237,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) { if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } } @@ -1215,12 +1249,12 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) *bufsize = *bufsize * 2; if (*bufsize - hasread < CS_TCPIP_BUFCHUNK) { - h->cerrno = CSBUFSIZE; + cs_set_error(h, CSBUFSIZE, 0); return -1; } if (!(*buf = (char *)xrealloc(*buf, *bufsize))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } } @@ -1267,7 +1301,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) } else { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } #else @@ -1290,7 +1324,7 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) continue; else { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } #endif @@ -1301,13 +1335,13 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) hasread += res; if (hasread > h->max_recv_bytes) { - h->cerrno = CSBUFSIZE; + cs_set_error(h, CSBUFSIZE, 0); return -1; } } if (berlen < 0) { - h->cerrno = CSPROTERR; + cs_set_error(h, CSPROTERR, 0); return -1; } yaz_log(log_level, " Out of read loop with hasread=%d, berlen=%d", @@ -1323,13 +1357,13 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize) { if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } } else if (sp->altsize < req) if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } yaz_log(log_level, " Moving %d bytes to altbuf(%p)", tomove, @@ -1362,7 +1396,7 @@ int tcpip_put(COMSTACK h, char *buf, int size) } else if (state->towrite != size) { - h->cerrno = CSWRONGBUF; + cs_set_error(h, CSWRONGBUF, 0); return -1; } while (state->towrite > state->written) @@ -1419,7 +1453,7 @@ int tcpip_put(COMSTACK h, char *buf, int size) } if (h->flags & CS_FLAGS_BLOCKING) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return -1; } else @@ -1440,7 +1474,6 @@ void tcpip_close(COMSTACK h) tcpip_state *sp = (struct tcpip_state *)h->cprivate; yaz_log(log_level, "tcpip_close: h=%p", h); - xfree(sp->bind_host); #if RESOLVER_THREAD if (sp->pipefd[0] != -1) { @@ -1465,22 +1498,7 @@ void tcpip_close(COMSTACK h) close(h->iofile); #endif } - if (sp->altbuf) - xfree(sp->altbuf); -#if HAVE_GNUTLS_H - if (sp->session) - { - gnutls_deinit(sp->session); - } - tcpip_release_cred(&sp->cred_ptr); -#endif - if (sp->ai) - freeaddrinfo(sp->ai); - xfree(sp->host_port); - xfree(sp->connect_host); - wrbuf_destroy(sp->connect_request); - xfree(sp->connect_response_buf); - xfree(sp); + tcpip_state_destroy(sp); xfree(h); } @@ -1495,7 +1513,7 @@ const char *tcpip_addrstr(COMSTACK h) if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR, 0); return 0; } if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1, @@ -1762,10 +1780,22 @@ int cs_set_head_only(COMSTACK cs, int head_only) sp->complete = cs_complete_auto; return 0; } - cs->cerrno = CS_ST_INCON; + cs_set_error(cs, CS_ST_INCON, 0); return -1; } +const char *yaz_tcpip_get_error_details(COMSTACK cs) +{ + if (cs->type == tcpip_type || cs->type == ssl_type) + { + tcpip_state *sp = (tcpip_state *)cs->cprivate; + if (wrbuf_len(sp->error_details)) + return wrbuf_cstr(sp->error_details); + } + return 0; +} + + /* * Local variables: * c-basic-offset: 4 @@ -1774,4 +1804,3 @@ int cs_set_head_only(COMSTACK cs, int head_only) * End: * vim: shiftwidth=4 tabstop=8 expandtab */ - diff --git a/src/unix.c b/src/unix.c index f155bac5..9dd58f51 100644 --- a/src/unix.c +++ b/src/unix.c @@ -102,6 +102,11 @@ static void unix_init (void) } } +static void cs_set_error(COMSTACK cs, int error) +{ + cs->cerrno = error; +} + /* * This function is always called through the cs_create() macro. * s >= 0: socket has already been established for us. @@ -326,7 +331,7 @@ static int unix_connect(COMSTACK h, void *address) h->io_pending = 0; if (h->state != CS_ST_UNBND) { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE); return -1; } for (i = 0; i<3; i++) @@ -356,7 +361,7 @@ static int unix_connect(COMSTACK h, void *address) h->io_pending = CS_WANT_WRITE; return 1; } - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } h->event = CS_CONNECT; @@ -376,7 +381,7 @@ static int unix_rcvconnect(COMSTACK h) return 0; if (h->state != CS_ST_CONNECTING) { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE); return -1; } h->event = CS_DATA; @@ -393,32 +398,39 @@ static int unix_bind(COMSTACK h, void *address, int mode) yaz_log(log_level, "unix_bind h=%p", h); - if (stat(path, &stat_buf) != -1) { + if (stat(path, &stat_buf) != -1) + { struct sockaddr_un socket_unix; int socket_out = -1; - if (!S_ISSOCK(stat_buf.st_mode)) { - h->cerrno = CSYSERR; + if (!S_ISSOCK(stat_buf.st_mode)) + { + cs_set_error(h, CSYSERR); yaz_set_errno(EEXIST); /* Not a socket (File exists) */ return -1; } - if ((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - h->cerrno = CSYSERR; + if ((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + { + cs_set_error(h, CSYSERR); return -1; } socket_unix.sun_family = AF_UNIX; strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path)-1); socket_unix.sun_path[sizeof(socket_unix.sun_path)-1] = 0; - if (connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) { - if (yaz_errno() == ECONNREFUSED) { + if (connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) + { + if (yaz_errno() == ECONNREFUSED) yaz_log(log_level, "unix_bind socket exists but nobody is listening"); - } else { - h->cerrno = CSYSERR; + else + { + cs_set_error(h, CSYSERR); return -1; } - } else { + } + else + { close(socket_out); - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); yaz_set_errno(EADDRINUSE); return -1; } @@ -427,22 +439,22 @@ static int unix_bind(COMSTACK h, void *address, int mode) if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } if (chown(path, sp->uid, sp->gid)) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } if (chmod(path, sp->umask != -1 ? sp->umask : 0666)) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } if (mode == CS_SERVER && listen(h->iofile, 100) < 0) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } h->state = CS_ST_IDLE; @@ -460,7 +472,7 @@ static int unix_listen(COMSTACK h, char *raddr, int *addrlen, yaz_log(log_level, "unix_listen h=%p", h); if (h->state != CS_ST_IDLE) { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE); return -1; } h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len); @@ -474,9 +486,9 @@ static int unix_listen(COMSTACK h, char *raddr, int *addrlen, #endif #endif ) - h->cerrno = CSNODATA; + cs_set_error(h, CSNODATA); else - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un)) @@ -497,7 +509,7 @@ static COMSTACK unix_accept(COMSTACK h) { if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew)))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); close(h->newfd); h->newfd = -1; return 0; @@ -508,19 +520,20 @@ static COMSTACK unix_accept(COMSTACK h) if (!(state = (unix_state *) (cnew->cprivate = xmalloc(sizeof(unix_state))))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); if (h->newfd != -1) { close(h->newfd); h->newfd = -1; } + xfree(cnew); return 0; } if (!(cnew->flags&CS_FLAGS_BLOCKING) && (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0) ) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); if (h->newfd != -1) { close(h->newfd); @@ -547,7 +560,7 @@ static COMSTACK unix_accept(COMSTACK h) } else { - h->cerrno = CSOUTSTATE; + cs_set_error(h, CSOUTSTATE); return 0; } h->io_pending = 0; @@ -600,12 +613,12 @@ static int unix_get(COMSTACK h, char **buf, int *bufsize) if (*bufsize - hasread < CS_UNIX_BUFCHUNK) { - h->cerrno = CSBUFSIZE; + cs_set_error(h, CSBUFSIZE); return -1; } if (!(*buf = (char *)xrealloc(*buf, *bufsize))) { - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } } @@ -635,13 +648,13 @@ static int unix_get(COMSTACK h, char **buf, int *bufsize) hasread += res; if (hasread > h->max_recv_bytes) { - h->cerrno = CSBUFSIZE; + cs_set_error(h, CSBUFSIZE); return -1; } } if (berlen < 0) { - h->cerrno = CSPROTERR; + cs_set_error(h, CSPROTERR); return -1; } yaz_log(log_level, " Out of read loop with hasread=%d, berlen=%d", @@ -691,7 +704,7 @@ static int unix_put(COMSTACK h, char *buf, int size) } else if (state->towrite != size) { - h->cerrno = CSWRONGBUF; + cs_set_error(h, CSWRONGBUF); return -1; } while (state->towrite > state->written) @@ -719,7 +732,7 @@ static int unix_put(COMSTACK h, char *buf, int size) h->io_pending = CS_WANT_WRITE; return 1; } - h->cerrno = CSYSERR; + cs_set_error(h, CSYSERR); return -1; } state->written += res; @@ -778,4 +791,3 @@ static int unix_set_blocking(COMSTACK p, int flags) * End: * vim: shiftwidth=4 tabstop=8 expandtab */ - diff --git a/src/url.c b/src/url.c index 2395c69b..bfcd6a77 100644 --- a/src/url.c +++ b/src/url.c @@ -147,6 +147,15 @@ static void log_warn(yaz_url_t p) yaz_log(YLOG_WARN, "yaz_url: %s", wrbuf_cstr(p->w_error)); } +static void add_comstack_details(yaz_url_t p, COMSTACK conn) +{ + const char *details; + int code = cs_get_error(conn, &details); + wrbuf_printf(p->w_error, ": cs=%d msg=%s", code, cs_errmsg(code)); + if (details) + wrbuf_printf(p->w_error, " details=%s", details); +} + Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri, const char *method, Z_HTTP_Header *user_headers, @@ -218,6 +227,7 @@ Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri, else if ((ret = cs_connect(conn, add)) < 0) { wrbuf_printf(p->w_error, "Can not connect to URL %s", uri); + add_comstack_details(p, conn); log_warn(p); } else @@ -263,8 +273,8 @@ Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri, ret = cs_rcvconnect(conn); if (ret < 0) { - wrbuf_printf(p->w_error, - "cs_rcvconnect failed for URL %s", uri); + wrbuf_printf(p->w_error, "cs_rcvconnect failed for URL %s", uri); + add_comstack_details(p, conn); log_warn(p); break; } @@ -277,6 +287,7 @@ Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri, if (ret < 0) { wrbuf_printf(p->w_error, "cs_put fail for URL %s", uri); + add_comstack_details(p, conn); log_warn(p); break; } @@ -288,10 +299,18 @@ Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri, else if (state == 2) /* read response phase */ { ret = cs_get(conn, &netbuffer, &netlen); - if (ret <= 0) + if (ret <= 0) { - wrbuf_printf(p->w_error, "cs_get failed for URL %s", - uri); + if (ret < 0) + { + wrbuf_printf(p->w_error, + "cs_get failed for URL %s", uri); + add_comstack_details(p, conn); + } + else + wrbuf_printf(p->w_error, + "Connection closed while reading" + " URL %s", uri); log_warn(p); break; } @@ -349,4 +368,3 @@ Z_HTTP_Response *yaz_url_exec(yaz_url_t p, const char *uri, * End: * vim: shiftwidth=4 tabstop=8 expandtab */ - diff --git a/src/yaz/comstack.h b/src/yaz/comstack.h index a1b79fd9..3009082c 100644 --- a/src/yaz/comstack.h +++ b/src/yaz/comstack.h @@ -117,6 +117,12 @@ struct comstack YAZ_EXPORT int cs_look (COMSTACK); YAZ_EXPORT const char *cs_strerror(COMSTACK h); YAZ_EXPORT const char *cs_errmsg(int n); +/** \brief returns COMSTACK error and additional information + \param cs COMSTACK handle + \param details additional error information (result), or NULL + \returns error code + */ +YAZ_EXPORT int cs_get_error(COMSTACK cs, const char **details); YAZ_EXPORT COMSTACK cs_create_host(const char *type_and_host, int blocking, void **vp); @@ -181,4 +187,3 @@ YAZ_END_CDECL * End: * vim: shiftwidth=4 tabstop=8 expandtab */ - diff --git a/test/test_comstack.c b/test/test_comstack.c index 66ae9d0b..18cf88ab 100644 --- a/test/test_comstack.c +++ b/test/test_comstack.c @@ -462,6 +462,43 @@ static void tst_cs_get_host_args(void) YAZ_CHECK(arg && !strcmp(arg, "x")); } +static void tst_cs_get_error(void) +{ + COMSTACK cs = cs_create(tcpip_type, CS_FLAGS_BLOCKING, PROTO_Z3950); + const char *details = "not set"; + + YAZ_CHECK(cs); + if (!cs) + return; + + YAZ_CHECK_EQ(cs_get_error(cs, &details), CSNONE); + YAZ_CHECK(!details); + +#if HAVE_GNUTLS_H + { + COMSTACK ssl_cs = + cs_create(ssl_type, CS_FLAGS_BLOCKING, PROTO_Z3950); + + YAZ_CHECK(ssl_cs); + if (ssl_cs) + { + void *ad = cs_straddr(ssl_cs, "localhost:0"); + YAZ_CHECK(ad); + YAZ_CHECK(cs_set_ssl_certificate_file(ssl_cs, "")); + if (ad) + { + YAZ_CHECK_EQ(cs_bind(ssl_cs, ad, CS_SERVER), -1); + YAZ_CHECK_EQ(cs_get_error(ssl_cs, &details), CSERRORSSL); + YAZ_CHECK(details && *details); + } + cs_close(ssl_cs); + } + } +#endif + + cs_close(cs); +} + int main (int argc, char **argv) { YAZ_CHECK_INIT(argc, argv); @@ -471,6 +508,7 @@ int main (int argc, char **argv) tst_http_request(); tst_http_response(); tst_cs_get_host_args(); + tst_cs_get_error(); YAZ_CHECK_TERM; } @@ -482,4 +520,3 @@ int main (int argc, char **argv) * End: * vim: shiftwidth=4 tabstop=8 expandtab */ -