41#include <netinet/in.h>
50#include <netinet/tcp.h>
53#include <sys/socket.h>
60#include <gnutls/x509.h>
61#include <gnutls/gnutls.h>
72#define RESOLVER_THREAD 1
76#if GNUTLS_VERSION_NUMBER >= 0x030109
77#define SET_GNUTLS_SOCKET(ses, socket) gnutls_transport_set_int(ses, socket)
79#define SET_GNUTLS_SOCKET(ses, socket) \
80 gnutls_transport_set_ptr(ses, (gnutls_transport_ptr_t) (size_t) socket)
92 int (*check_ip)(
void *cd,
const char *a,
int len,
int type),
104#define YAZ_SOCKLEN_T int
108struct tcpip_cred_ptr {
109 gnutls_certificate_credentials_t xcred;
112static void tcpip_release_cred(
struct tcpip_cred_ptr **ptr);
137 struct tcpip_cred_ptr *cred_ptr;
138 gnutls_session_t session;
140 char cert_fname[256];
156 static int log_level_set = 0;
158 static int initialized = 0;
167 requested = MAKEWORD(1, 1);
168 if (WSAStartup(requested, &wd))
194 sp->pipefd[0] = sp->pipefd[1] = -1;
201 sp->cert_fname[0] =
'\0';
202 sp->key_fname[0] =
'\0';
204 sp->yaz_pem_default = 1;
219 gnutls_deinit(sp->session);
220 tcpip_release_cred(&sp->cred_ptr);
223 freeaddrinfo(sp->
ai);
300 if (!cp || cp[1] ==
'\0')
317 char *basic_auth =
xmalloc(strlen(connect_auth) * 8/6 + 12);
318 strcpy(basic_auth,
"Basic ");
329 const char *connect_auth,
353static struct tcpip_cred_ptr *tcpip_create_cred(
void)
355 struct tcpip_cred_ptr *cred_ptr;
356 cred_ptr = (
struct tcpip_cred_ptr *)
xmalloc(
sizeof(*cred_ptr));
358 gnutls_certificate_allocate_credentials(&cred_ptr->xcred);
362static void tcpip_release_cred(
struct tcpip_cred_ptr **ptr)
366 assert((*ptr)->ref > 0);
368 if (--((*ptr)->ref) == 0)
372 gnutls_certificate_free_credentials((*ptr)->xcred);
392 sp->session = (gnutls_session_t) vp;
402 const char *connect_host,
403 const char *connect_auth,
404 const char *bind_host)
418 gnutls_error_is_fatal(res),
419 gnutls_strerror(res));
420 if (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED)
422 int dir = gnutls_record_get_direction(sp->session);
433 const char **host,
const char **port)
439 strncpy(tmp, host_port, tmp_sz-1);
457 cp1 = strchr(cp,
'/');
460 cp1 = strchr(cp,
'?');
463 cp1 = strrchr(cp,
':');
475 struct addrinfo hints, *res;
481 hints.ai_family = AF_UNSPEC;
482 hints.ai_socktype = SOCK_STREAM;
483 hints.ai_protocol = 0;
484 hints.ai_addrlen = 0;
485 hints.ai_addr = NULL;
486 hints.ai_canonname = NULL;
487 hints.ai_next = NULL;
492 if (!strcmp(
"@", host))
494 hints.ai_flags = AI_PASSIVE;
495 hints.ai_family = AF_UNSPEC;
496 error = getaddrinfo(0, port, &hints, &res);
499 else if (!strcmp(
"@4", host))
501 hints.ai_flags = AI_PASSIVE;
502 hints.ai_family = AF_INET;
503 error = getaddrinfo(0, port, &hints, &res);
506 else if (!strcmp(
"@6", host))
508 hints.ai_flags = AI_PASSIVE;
509 hints.ai_family = AF_INET6;
510 error = getaddrinfo(0, port, &hints, &res);
515 error = getaddrinfo(host, port, &hints, &res);
527 struct addrinfo *ai = 0;
530 for (ai = sp->
ai; ai; ai = ai->ai_next)
532 if (ai->ai_family == AF_INET6)
534 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
542 for (ai = sp->
ai; ai; ai = ai->ai_next)
544 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
554 if (ai->ai_family == AF_INET6 && sp->
ipv6_only >= 0 &&
567 if (setsockopt(h->
iofile, SOL_SOCKET, SO_REUSEADDR, (
char*)
568 &one,
sizeof(one)) < 0)
579 for (a = ai; a; a = a->ai_next)
581 r = bind(h->
iofile, a->ai_addr, a->ai_addrlen);
601void *resolver_thread(
void *arg)
608 freeaddrinfo(sp->
ai);
610 if (write(sp->pipefd[1],
"1", 1) != 1)
616static struct addrinfo *wait_resolver_thread(
COMSTACK h)
621 if (read(sp->pipefd[0], &buf, 1) == (ssize_t) -1)
624 close(sp->pipefd[0]);
625 close(sp->pipefd[1]);
636 const char *port =
"210";
653 if (sp->pipefd[0] != -1)
655 if (pipe(sp->pipefd) == -1)
664 freeaddrinfo(sp->
ai);
684 while (ai && (ai = ai->ai_next))
687 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
693 gnutls_bye(sp->session, GNUTLS_SHUT_WR);
694 gnutls_deinit(sp->session);
722 struct addrinfo *ai = (
struct addrinfo *) address;
733 if (sp->pipefd[0] != -1)
737 ai = wait_resolver_thread(h);
746 h->
iofile = sp->pipefd[0];
751 r = connect(h->
iofile, ai->ai_addr, ai->ai_addrlen);
756 if (WSAGetLastError() == WSAEWOULDBLOCK)
786 unsigned int status = 0;
787 const char *vhost = 0;
788 const char *vport = 0;
793 vr = gnutls_certificate_verify_peers3(sp->session, vhost, &status);
797 gnutls_strerror(vr));
806 " status=%u host=%s",
807 status, vhost ? vhost :
"");
809 "TLS certificate verification failed:"
810 " status=%u host=%s",
811 status, vhost ? vhost :
"");
828 if (sp->pipefd[0] != -1)
830 struct addrinfo *ai = wait_resolver_thread(h);
888 const char *host = 0;
889 const char *port = 0;
894 sp->cred_ptr = tcpip_create_cred();
897 r = gnutls_certificate_set_x509_system_trust(sp->cred_ptr->xcred);
900 yaz_log(
log_level,
"gnutls_certificate_set_x509_system_trust r=%d msg=%s", r, gnutls_strerror(r));
902 tcpip_release_cred(&sp->cred_ptr);
906 if (sp->cert_fname[0])
909 res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred,
911 sp->key_fname[0] ? sp->key_fname : sp->cert_fname,
912 GNUTLS_X509_FMT_PEM);
913 if (res != GNUTLS_E_SUCCESS)
915 yaz_log(
log_level,
"gnutls_certificate_set_x509_key_file r=%d fatal=%d msg=%s",
917 gnutls_error_is_fatal(res),
918 gnutls_strerror(res));
923 gnutls_init(&sp->session, GNUTLS_CLIENT);
925 gnutls_set_default_priority(sp->session);
926 gnutls_credentials_set(sp->session, GNUTLS_CRD_CERTIFICATE, sp->cred_ptr->xcred);
929 if (!strchr(host,
':'))
930 gnutls_server_name_set(sp->session, GNUTLS_NAME_DNS,
932 SET_GNUTLS_SOCKET(sp->session, h->
iofile);
936 int res = gnutls_handshake(sp->session);
937 if (res == GNUTLS_E_SUCCESS)
939 int ret = check_cert(h, sp);
944 if (ssl_check_again(h, sp, res))
964 struct addrinfo *ai = (
struct addrinfo *) address;
973 if (sp->pipefd[0] != -1)
975 ai = wait_resolver_thread(h);
986 if (sp->yaz_pem_default)
987 strcpy(sp->cert_fname,
"yaz.pem");
989 sp->cred_ptr = tcpip_create_cred();
990 res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred,
992 sp->key_fname[0] ? sp->key_fname : sp->cert_fname,
993 GNUTLS_X509_FMT_PEM);
994 if (res != GNUTLS_E_SUCCESS)
996 yaz_log(
log_level,
"gnutls_certificate_set_x509_key_file r=%d fatal=%d msg=%s",
998 gnutls_error_is_fatal(res),
999 gnutls_strerror(res));
1006 if (setsockopt(h->
iofile, SOL_SOCKET, SO_REUSEADDR, (
char*)
1007 &one,
sizeof(one)) < 0)
1013 r = bind(h->
iofile, ai->ai_addr, ai->ai_addrlen);
1014 freeaddrinfo(sp->
ai);
1033 int (*check_ip)(
void *cd,
const char *a,
int len,
int t),
1039 struct sockaddr_in addr;
1052 h->
newfd = accept(h->
iofile, (
struct sockaddr*)&addr, &len);
1058 WSAGetLastError() == WSAEWOULDBLOCK
1062#
if EAGAIN != EWOULDBLOCK
1072 listen(h->
iofile, SOMAXCONN);
1081 if (addrlen && (
size_t) (*addrlen) >=
sizeof(
struct sockaddr_in))
1082 memcpy(raddr, &addr, *addrlen =
sizeof(
struct sockaddr_in));
1085 if (check_ip && (*check_ip)(cd, (
const char *) &addr,
1086 sizeof(addr), AF_INET))
1090 closesocket(h->
newfd);
1106 unsigned long tru = 1;
1118 memcpy(cnew, h,
sizeof(*h));
1129 closesocket(h->
newfd);
1144 state->cred_ptr = st->cred_ptr;
1149 (state->cred_ptr->ref)++;
1150 gnutls_init(&state->session, GNUTLS_SERVER);
1151 if (!state->session)
1156 res = gnutls_set_default_priority(state->session);
1157 if (res != GNUTLS_E_SUCCESS)
1162 res = gnutls_credentials_set(state->session,
1163 GNUTLS_CRD_CERTIFICATE,
1164 st->cred_ptr->xcred);
1165 if (res != GNUTLS_E_SUCCESS)
1170 SET_GNUTLS_SOCKET(state->session, cnew->
iofile);
1181 int res = gnutls_handshake(state->session);
1182 if (res != GNUTLS_E_SUCCESS)
1184 if (ssl_check_again(h, state, res))
1207#define CS_TCPIP_BUFCHUNK 4096
1217 int tmpi, berlen, rest, req, tomove;
1218 int hasread = 0, res;
1234 while ((berlen = (*sp->
complete)(*buf, hasread)) == 0)
1249 *bufsize = *bufsize * 2;
1255 if (!(*buf = (
char *)
xrealloc(*buf, *bufsize)))
1264 res = gnutls_record_recv(sp->session, *buf + hasread,
1273 if (ssl_check_again(h, sp, res))
1297 if (WSAGetLastError() == WSAEWOULDBLOCK)
1310#
if EAGAIN != EWOULDBLOCK
1350 if (hasread > berlen)
1352 tomove = req = hasread - berlen;
1371 memcpy(sp->
altbuf, *buf + berlen, sp->
altlen = tomove);
1374 *(*buf + berlen) =
'\0';
1375 return berlen ? berlen : 1;
1397 else if (state->
towrite != size)
1407 res = gnutls_record_send(state->session,
buf + state->
written,
1411 if (ssl_check_again(h, state, res))
1435 WSAGetLastError() == WSAEWOULDBLOCK
1439#
if EAGAIN != EWOULDBLOCK
1478 if (sp->pipefd[0] != -1)
1481 close(sp->pipefd[0]);
1482 close(sp->pipefd[1]);
1489 if (sp->session && sp->use_bye)
1492 gnutls_bye(sp->session, GNUTLS_SHUT_WR);
1511 struct sockaddr_storage addr;
1514 if (getpeername(h->
iofile, (
struct sockaddr *)&addr, &len) < 0)
1519 if (getnameinfo((
struct sockaddr *) &addr, len, host,
sizeof(host)-1,
1550 if (ioctlsocket(p->
iofile, FIONBIO, &flag) < 0)
1553 flag = fcntl(p->
iofile, F_GETFL, 0);
1555 flag = flag & ~O_NONBLOCK;
1558 flag = flag | O_NONBLOCK;
1559 signal(SIGPIPE, SIG_IGN);
1561 if (fcntl(p->
iofile, F_SETFL, flag) < 0)
1572#if GNUTLS_VERSION_NUMBER >= 0x020400
1573#define USE_GNUTLS_X509_CRT_PRINT 1
1575#define USE_GNUTLS_X509_CRT_PRINT 0
1579#if USE_GNUTLS_X509_CRT_PRINT
1581static const char *bin2hex(
const void *bin,
size_t bin_size)
1583 static char printable[110];
1584 const unsigned char *_bin = bin;
1590 for (i = 0; i < bin_size; i++)
1598static void x509_crt_print(gnutls_x509_crt_t cert)
1600 time_t expiration_time, activation_time;
1604 unsigned int algo, bits;
1606 expiration_time = gnutls_x509_crt_get_expiration_time(cert);
1607 activation_time = gnutls_x509_crt_get_activation_time(cert);
1609 printf(
"\tCertificate is valid since: %s", ctime(&activation_time));
1610 printf(
"\tCertificate expires: %s", ctime(&expiration_time));
1613 size =
sizeof(serial);
1614 gnutls_x509_crt_get_serial(cert, serial, &size);
1616 printf(
"\tCertificate serial number: %s\n", bin2hex(serial, size));
1620 algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
1622 printf(
"Certificate public key: %s", gnutls_pk_algorithm_get_name(algo));
1625 printf(
"\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert));
1628 gnutls_x509_crt_get_dn(cert, dn, &size);
1629 printf(
"\tDN: %s\n", dn);
1632 gnutls_x509_crt_get_issuer_dn(cert, dn, &size);
1633 printf(
"\tIssuer's DN: %s\n", dn);
1644 const gnutls_datum_t *cert_list;
1645 unsigned i, cert_list_size;
1646 if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1648 printf(
"X509 certificate\n");
1649 cert_list = gnutls_certificate_get_peers(sp->session,
1651 printf(
"Peer provided %u certificates\n", cert_list_size);
1652 for (i = 0; i < cert_list_size; i++)
1654 gnutls_x509_crt_t cert;
1655#if USE_GNUTLS_X509_CRT_PRINT
1657 gnutls_datum_t cinfo;
1659 gnutls_x509_crt_init(&cert);
1660 gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER);
1661 printf(
"Certificate info %u:\n", i + 1);
1662#if USE_GNUTLS_X509_CRT_PRINT
1663 ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL,
1667 printf(
"\t%s\n", cinfo.data);
1668 gnutls_free(cinfo.data);
1671 x509_crt_print(cert);
1673 gnutls_x509_crt_deinit(cert);
1704 sp->yaz_pem_default = 0;
1705 if (fname == 0 || *fname ==
'\0')
1706 sp->cert_fname[0] =
'\0';
1709 const char *comma = strchr(fname,
',');
1712 size_t clen = comma - fname;
1713 if (clen >=
sizeof(sp->cert_fname))
1714 clen =
sizeof(sp->cert_fname) - 1;
1715 memcpy(sp->cert_fname, fname, clen);
1716 sp->cert_fname[clen] =
'\0';
1717 strncpy(sp->key_fname, comma + 1,
sizeof(sp->key_fname) - 1);
1718 sp->key_fname[
sizeof(sp->key_fname) - 1] =
'\0';
1722 strncpy(sp->cert_fname, fname,
sizeof(sp->cert_fname)-1);
1723 sp->cert_fname[
sizeof(sp->cert_fname)-1] =
'\0';
1724 sp->key_fname[0] =
'\0';
1737#if USE_GNUTLS_X509_CRT_PRINT
1741 const gnutls_datum_t *cert_list;
1742 unsigned cert_list_size;
1743 if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1745 cert_list = gnutls_certificate_get_peers(sp->session, &cert_list_size);
1746 if (cert_list_size > 0)
1748 gnutls_x509_crt_t cert;
1750 gnutls_datum_t cinfo;
1752 gnutls_x509_crt_init(&cert);
1753 gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
1755 ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &cinfo);
1759 *len = strlen(*
buf);
1760 gnutls_free(cinfo.data);
1761 gnutls_x509_crt_deinit(cert);
1764 gnutls_x509_crt_deinit(cert);
1774 int (*completer)(
const char *
buf,
int len) =
void yaz_base64encode(const char *in, char *out)
encodes Base64 string
Header for Base64 utilities.
Private Header for COMSTACK.
const char * yaz_tcpip_get_error_details(COMSTACK cs)
int yaz_tcpip_set_head_only(COMSTACK cs, int head_only)
int cs_complete_auto(const char *buf, int len)
Finds the length of a complete BER or HTTP message.
int cs_complete_auto_head(const char *buf, int len)
Finds the length of a BER message or HTTP headers.
#define cs_close(handle)
Closes the socket and frees the endpoint and its transport state.
#define CS_FLAGS_CHECK_CERT
struct comstack * COMSTACK
Communication endpoint; release with cs_close.
#define CS_FLAGS_DNS_NO_BLOCK
#define CS_FLAGS_BLOCKING
#define CS_FLAGS_NUMERICHOST
int yaz_errno(void)
returns errno
void yaz_set_errno(int v)
sets errno to value
Header for errno utilities.
int yaz_decode_http_response_first(const char *buf, int sz, int *code, const char **version, int *version_len, const char **msg, int *msg_len)
void yaz_init_globals(void)
void yaz_log(int level, const char *fmt,...)
Writes log message.
int yaz_log_module_level(const char *name)
returns level for module
#define YLOG_WARN
log level: warning
void yaz_snprintf(char *buf, size_t size, const char *fmt,...)
Header for config file reading utilities.
Public endpoint state and transport dispatch table.
COMSTACK(*) f_accept(COMSTACK handle)
const char *(*) f_addrstr(COMSTACK handle)
int(*) f_rcvconnect(COMSTACK handle)
int(*) f_bind(COMSTACK handle, void *address, int mode)
int(*) f_put(COMSTACK handle, char *buf, int size)
int(*) f_listen(COMSTACK h, char *raddr, int *addrlen, int(*check_ip)(void *cd, const char *a, int len, int type), void *cd)
int(*) f_connect(COMSTACK handle, void *address)
int(*) f_more(COMSTACK handle)
void *(*) f_straddr(COMSTACK handle, const char *str)
int(*) f_get(COMSTACK handle, char **buf, int *bufsize)
int(*) f_set_blocking(COMSTACK handle, int blocking)
void(*) f_close(COMSTACK handle)
struct addrinfo * ai_connect
char * connect_response_buf
int(*) complete(const char *buf, int len)
int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
Selects certificate and private key files for a TLS endpoint.
void cs_print_session_info(COMSTACK cs)
Prints TLS peer certificate information to standard output.
static struct addrinfo * create_net_socket(COMSTACK h)
int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
Gets a human-readable description of the first peer X.509 certificate.
static void connect_and_bind(COMSTACK p, const char *connect_host, const char *connect_auth, const char *bind_host)
struct addrinfo * tcpip_getaddrinfo(const char *str, const char *port, int *ipv6_only)
COMSTACK yaz_ssl_create(int s, int flags, int protocol, const char *connect_host, const char *connect_auth, const char *bind_host)
static void * tcpip_straddr(COMSTACK h, const char *str)
COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
Legacy OpenSSL context setter.
COMSTACK yaz_tcpip_create2(int s, int flags, int protocol, const char *connect_host, const char *bind_host)
static int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, int(*check_ip)(void *cd, const char *a, int len, int type), void *cd)
static const char * tcpip_addrstr(COMSTACK h)
COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
static int tcpip_more(COMSTACK h)
static int tcpip_get(COMSTACK h, char **buf, int *bufsize)
static void cs_set_error(COMSTACK cs, int error, const char *details)
#define CS_TCPIP_BUFCHUNK
static int cont_connect(COMSTACK h)
COMSTACK yaz_tcpip_create(int s, int flags, int protocol, const char *connect_host)
COMSTACK yaz_tcpip_create3(int s, int flags, int protocol, const char *connect_host, const char *connect_auth, const char *bind_host)
static void parse_host_port(const char *host_port, char *tmp, size_t tmp_sz, const char **host, const char **port)
static int tcpip_put(COMSTACK h, char *buf, int size)
static void tcpip_state_destroy(tcpip_state *sp)
static struct tcpip_state * tcpip_state_create(void)
static void tcpip_close(COMSTACK h)
static int tcpip_init(void)
void * cs_get_ssl(COMSTACK cs)
Legacy OpenSSL session accessor.
static COMSTACK tcpip_accept(COMSTACK h)
static int tcpip_connect(COMSTACK h, void *address)
static int tcpip_bind(COMSTACK h, void *address, int mode)
static int tcpip_set_blocking(COMSTACK p, int blocking)
static int tcpip_rcvconnect(COMSTACK h)
Header for TCP/IP + SSL COMSTACK.
yaz_thread_t yaz_thread_create(void *(*start_routine)(void *p), void *arg)
create thread
void yaz_thread_join(yaz_thread_t *tp, void **value_ptr)
join thread
Implements thread creation wrappers.
struct yaz_thread * yaz_thread_t
Thread Identifier opaque pointer.
void wrbuf_destroy(WRBUF b)
destroy WRBUF and its buffer
const char * wrbuf_cstr(WRBUF b)
returns WRBUF content as C-string
void wrbuf_rewind(WRBUF b)
empty WRBUF content (length of buffer set to 0)
WRBUF wrbuf_alloc(void)
construct WRBUF
void wrbuf_printf(WRBUF b, const char *fmt,...)
writes printf result to WRBUF
void wrbuf_puts(WRBUF b, const char *buf)
appends C-string to WRBUF
Header for WRBUF (growing buffer).
#define xstrdup(s)
utility macro which calls xstrdup_f
#define xfree(x)
utility macro which calls xfree_f
#define xrealloc(o, x)
utility macro which calls xrealloc_f
#define xmalloc(x)
utility macro which calls malloc_f
Header with fundamental macros.
Header for the Z_GDU (HTTP or Z39.50 package).