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
*/
-