YAZ 5.38.1
tcpip.c
Go to the documentation of this file.
1/* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
4 */
9#if HAVE_CONFIG_H
10#include <config.h>
11#endif
12
13#include <yaz/yconfig.h>
14
15#include <stdio.h>
16#include <string.h>
17#include <assert.h>
18#include <stdlib.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <signal.h>
22#if HAVE_SYS_TYPES_H
23#include <sys/types.h>
24#endif
25#if HAVE_SYS_TIME_H
26#include <sys/time.h>
27#endif
28#if HAVE_UNISTD_H
29#include <unistd.h>
30#endif
31#include <yaz/thread_create.h>
32#include <yaz/log.h>
33#include <yaz/wrbuf.h>
34
35#ifdef WIN32
36#include <winsock2.h>
37#include <ws2tcpip.h>
38#endif
39
40#if HAVE_NETINET_IN_H
41#include <netinet/in.h>
42#endif
43#if HAVE_NETDB_H
44#include <netdb.h>
45#endif
46#if HAVE_ARPA_INET_H
47#include <arpa/inet.h>
48#endif
49#if HAVE_NETINET_TCP_H
50#include <netinet/tcp.h>
51#endif
52#if HAVE_SYS_SOCKET_H
53#include <sys/socket.h>
54#endif
55#if HAVE_SYS_WAIT_H
56#include <sys/wait.h>
57#endif
58
59#if HAVE_GNUTLS_H
60#include <gnutls/x509.h>
61#include <gnutls/gnutls.h>
62#endif
63
64#include <yaz/base64.h>
65#include "comstack-p.h"
66#include <yaz/errno.h>
67#include <yaz/tcpip.h>
68#include <yaz/snprintf.h>
69#include <yaz/zgdu.h>
70
71#ifndef WIN32
72#define RESOLVER_THREAD 1
73#endif
74
75#if HAVE_GNUTLS_H
76#if GNUTLS_VERSION_NUMBER >= 0x030109
77#define SET_GNUTLS_SOCKET(ses, socket) gnutls_transport_set_int(ses, socket)
78#else
79#define SET_GNUTLS_SOCKET(ses, socket) \
80 gnutls_transport_set_ptr(ses, (gnutls_transport_ptr_t) (size_t) socket)
81#endif
82#endif
83
84static void tcpip_close(COMSTACK h);
85static int tcpip_put(COMSTACK h, char *buf, int size);
86static int tcpip_get(COMSTACK h, char **buf, int *bufsize);
87static int tcpip_connect(COMSTACK h, void *address);
88static int tcpip_more(COMSTACK h);
89static int tcpip_rcvconnect(COMSTACK h);
90static int tcpip_bind(COMSTACK h, void *address, int mode);
91static int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
92 int (*check_ip)(void *cd, const char *a, int len, int type),
93 void *cd);
94static int tcpip_set_blocking(COMSTACK p, int blocking);
95
96struct addrinfo *tcpip_getaddrinfo(const char *str, const char *port,
97 int *ipv6_only);
98
100static const char *tcpip_addrstr(COMSTACK h);
101static void *tcpip_straddr(COMSTACK h, const char *str);
102
103#ifndef YAZ_SOCKLEN_T
104#define YAZ_SOCKLEN_T int
105#endif
106
107#if HAVE_GNUTLS_H
108struct tcpip_cred_ptr {
109 gnutls_certificate_credentials_t xcred;
110 int ref;
111};
112static void tcpip_release_cred(struct tcpip_cred_ptr **ptr);
113
114#endif
115/* this state is used for both SSL and straight TCP/IP */
116typedef struct tcpip_state
117{
118 char *altbuf; /* alternate buffer for surplus data */
119 int altsize; /* size as xmalloced */
120 int altlen; /* length of data or 0 if none */
121
122 int written; /* -1 if we aren't writing */
123 int towrite; /* to verify against user input */
124 int (*complete)(const char *buf, int len); /* length/complete. */
127 struct addrinfo *ai;
128 struct addrinfo *ai_connect;
130#if RESOLVER_THREAD
131 int pipefd[2];
132 const char *port;
133 yaz_thread_t thread_id;
134#endif
135 char buf[128]; /* returned by cs_addrstr */
136#if HAVE_GNUTLS_H
137 struct tcpip_cred_ptr *cred_ptr;
138 gnutls_session_t session;
139 int yaz_pem_default;
140 char cert_fname[256];
141 char key_fname[256];
142 int use_bye;
143#endif
149 WRBUF error_details; /* additional information for the current error */
151
152static int log_level = 0;
153
154static int tcpip_init(void)
155{
156 static int log_level_set = 0;
157#ifdef WIN32
158 static int initialized = 0;
159#endif
161#ifdef WIN32
162 if (!initialized)
163 {
164 WORD requested;
165 WSADATA wd;
166
167 requested = MAKEWORD(1, 1);
168 if (WSAStartup(requested, &wd))
169 return 0;
170 initialized = 1;
171 }
172#endif
173 if (!log_level_set)
174 {
175 log_level = yaz_log_module_level("comstack");
176 log_level_set = 1;
177 }
178 return 1;
179}
180
181static struct tcpip_state *tcpip_state_create(void)
182{
183 tcpip_state *sp = (struct tcpip_state *) xmalloc(sizeof(*sp));
184
185 sp->altbuf = 0;
186 sp->altsize = sp->altlen = 0;
187 sp->towrite = sp->written = -1;
189 sp->bind_host = 0;
190 sp->host_port = 0;
191 sp->ai = 0;
192 sp->ai_connect = 0;
193#if RESOLVER_THREAD
194 sp->pipefd[0] = sp->pipefd[1] = -1;
195 sp->port = 0;
196#endif
197
198#if HAVE_GNUTLS_H
199 sp->cred_ptr = 0;
200 sp->session = 0;
201 sp->cert_fname[0] = '\0';
202 sp->key_fname[0] = '\0';
203 sp->use_bye = 0;
204 sp->yaz_pem_default = 1;
205#endif
206 sp->connect_host = 0;
207 sp->connect_phase = 0;
208 sp->connect_request = 0;
209 sp->connect_response_buf = 0;
210 sp->connect_response_len = 0;
212 return sp;
213}
214
216{
217#if HAVE_GNUTLS_H
218 if (sp->session)
219 gnutls_deinit(sp->session);
220 tcpip_release_cred(&sp->cred_ptr);
221#endif
222 if (sp->ai)
223 freeaddrinfo(sp->ai);
224 xfree(sp->altbuf);
225 xfree(sp->bind_host);
226 xfree(sp->host_port);
227 xfree(sp->connect_host);
231 xfree(sp);
232}
233
234/*
235 * This function is always called through the cs_create() macro.
236 * s >= 0: socket has already been established for us.
237 */
238COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
239{
240 COMSTACK p;
241
242 if (!tcpip_init())
243 return 0;
244 if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
245 return 0;
246
248 p->flags = flags;
249
250 p->io_pending = 0;
251 p->iofile = s;
252 p->type = tcpip_type;
253 p->protocol = (enum oid_proto) protocol;
254
257 p->f_get = tcpip_get;
258 p->f_put = tcpip_put;
259 p->f_close = tcpip_close;
260 p->f_more = tcpip_more;
261 p->f_bind = tcpip_bind;
267 p->max_recv_bytes = 16777216;
268
269 p->state = s < 0 ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
270 p->event = CS_NONE;
271 p->cerrno = 0;
272 p->user = 0;
273
274 yaz_log(log_level, "Created TCP/SSL comstack h=%p", p);
275
276 return p;
277}
278
279static void cs_set_error(COMSTACK cs, int error, const char *details)
280{
281 struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
283 if (details)
284 wrbuf_puts(sp->error_details, details);
285 cs->cerrno = error;
286}
287
289 const char *connect_host, const char *connect_auth,
290 const char *bind_host)
291{
292 if (bind_host)
293 {
294 tcpip_state *sp = (tcpip_state *) p->cprivate;
295 char *cp;
296 sp->bind_host = xmalloc(strlen(bind_host) + 4);
297 strcpy(sp->bind_host, bind_host);
298 cp = strrchr(sp->bind_host, ':');
299
300 if (!cp || cp[1] == '\0')
301 strcat(sp->bind_host, ":0");
302 else
303 strcpy(cp, ":0");
304 }
305 if (connect_host)
306 {
307 char *cp;
308 tcpip_state *sp = (tcpip_state *) p->cprivate;
310 cp = strchr(sp->connect_host, '/');
311 if (cp)
312 *cp = '\0';
314 wrbuf_printf(sp->connect_request, "CONNECT %s HTTP/1.0\r\n", sp->connect_host);
315 if (connect_auth)
316 {
317 char *basic_auth = xmalloc(strlen(connect_auth) * 8/6 + 12);
318 strcpy(basic_auth, "Basic ");
319 yaz_base64encode(connect_auth, basic_auth + strlen(basic_auth));
320 wrbuf_printf(sp->connect_request, "Proxy-Authorization: %s\r\n", basic_auth);
321 xfree(basic_auth);
322 }
323 wrbuf_printf(sp->connect_request, "\r\n");
324 }
325}
326
327COMSTACK yaz_tcpip_create3(int s, int flags, int protocol,
328 const char *connect_host,
329 const char *connect_auth,
330 const char *bind_host)
331{
332 COMSTACK p = tcpip_type(s, flags, protocol, 0);
333 if (!p)
334 return 0;
335 connect_and_bind(p, connect_host, connect_auth, bind_host);
336 return p;
337}
338
339COMSTACK yaz_tcpip_create2(int s, int flags, int protocol,
340 const char *connect_host,
341 const char *bind_host)
342{
343 return yaz_tcpip_create3(s, flags, protocol, connect_host, 0, bind_host);
344}
345
346COMSTACK yaz_tcpip_create(int s, int flags, int protocol,
347 const char *connect_host)
348{
349 return yaz_tcpip_create2(s, flags, protocol, connect_host, 0);
350}
351
352#if HAVE_GNUTLS_H
353static struct tcpip_cred_ptr *tcpip_create_cred(void)
354{
355 struct tcpip_cred_ptr *cred_ptr;
356 cred_ptr = (struct tcpip_cred_ptr *) xmalloc(sizeof(*cred_ptr));
357 cred_ptr->ref = 1;
358 gnutls_certificate_allocate_credentials(&cred_ptr->xcred);
359 return cred_ptr;
360}
361
362static void tcpip_release_cred(struct tcpip_cred_ptr **ptr)
363{
364 if (*ptr)
365 {
366 assert((*ptr)->ref > 0);
367
368 if (--((*ptr)->ref) == 0)
369 {
370 yaz_log(log_level, "tcpip_release_cred: removed credentials h=%p",
371 (*ptr)->xcred);
372 gnutls_certificate_free_credentials((*ptr)->xcred);
373 xfree(*ptr);
374 }
375 *ptr = 0;
376 }
377}
378#endif
379
380COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
381{
382#if HAVE_GNUTLS_H
383 tcpip_state *sp;
384 COMSTACK p;
385
386 p = tcpip_type(s, flags, protocol, 0);
387 if (!p)
388 return 0;
389 p->type = ssl_type;
390 sp = (tcpip_state *) p->cprivate;
391
392 sp->session = (gnutls_session_t) vp;
393 /* note: we don't handle already opened socket in SSL mode - yet */
394
395 return p;
396#else
397 return 0;
398#endif
399}
400
401COMSTACK yaz_ssl_create(int s, int flags, int protocol,
402 const char *connect_host,
403 const char *connect_auth,
404 const char *bind_host)
405{
406 COMSTACK p = ssl_type(s, flags, protocol, 0);
407 if (!p)
408 return 0;
409 connect_and_bind(p, connect_host, connect_auth, bind_host);
410 return p;
411}
412
413#if HAVE_GNUTLS_H
414static int ssl_check_again(COMSTACK h, tcpip_state *sp, int res)
415{
416 yaz_log(log_level, "ssl_check_again error=%d fatal=%d msg=%s",
417 res,
418 gnutls_error_is_fatal(res),
419 gnutls_strerror(res));
420 if (res == GNUTLS_E_AGAIN || res == GNUTLS_E_INTERRUPTED)
421 {
422 int dir = gnutls_record_get_direction(sp->session);
423 yaz_log(log_level, " -> incomplete dir=%d", dir);
425 return 1;
426 }
427 cs_set_error(h, CSERRORSSL, gnutls_strerror(res));
428 return 0;
429}
430#endif
431
432static void parse_host_port(const char *host_port, char *tmp, size_t tmp_sz,
433 const char **host, const char **port)
434{
435 char *cp = tmp;
436 char *cp1;
437
438 *host = 0;
439 strncpy(tmp, host_port, tmp_sz-1);
440 tmp[tmp_sz-1] = 0;
441
442 if (*cp != '[')
443 *host = cp;
444 else
445 {
446 *host = ++cp;
447 while (*cp)
448 {
449 if (*cp == ']')
450 {
451 *cp++ = '\0';
452 break;
453 }
454 cp++;
455 }
456 }
457 cp1 = strchr(cp, '/');
458 if (cp1)
459 *cp1 = '\0';
460 cp1 = strchr(cp, '?');
461 if (cp1)
462 *cp1 = '\0';
463 cp1 = strrchr(cp, ':');
464 if (cp1)
465 {
466 *port = cp1 + 1;
467 *cp1 = '\0';
468 }
469}
470
471struct addrinfo *tcpip_getaddrinfo(const char *host_and_port,
472 const char *port,
473 int *ipv6_only)
474{
475 struct addrinfo hints, *res;
476 int error;
477 char tmp[512];
478 const char *host;
479
480 hints.ai_flags = 0;
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;
488
489 /* default port, might be changed below */
490 parse_host_port(host_and_port, tmp, sizeof tmp, &host, &port);
491
492 if (!strcmp("@", host))
493 {
494 hints.ai_flags = AI_PASSIVE;
495 hints.ai_family = AF_UNSPEC;
496 error = getaddrinfo(0, port, &hints, &res);
497 *ipv6_only = 0;
498 }
499 else if (!strcmp("@4", host))
500 {
501 hints.ai_flags = AI_PASSIVE;
502 hints.ai_family = AF_INET;
503 error = getaddrinfo(0, port, &hints, &res);
504 *ipv6_only = -1;
505 }
506 else if (!strcmp("@6", host))
507 {
508 hints.ai_flags = AI_PASSIVE;
509 hints.ai_family = AF_INET6;
510 error = getaddrinfo(0, port, &hints, &res);
511 *ipv6_only = 1;
512 }
513 else
514 {
515 error = getaddrinfo(host, port, &hints, &res);
516 *ipv6_only = -1;
517 }
518 if (error)
519 return 0;
520 return res;
521}
522
523static struct addrinfo *create_net_socket(COMSTACK h)
524{
525 tcpip_state *sp = (tcpip_state *)h->cprivate;
526 int s = -1;
527 struct addrinfo *ai = 0;
528 if (sp->ipv6_only >= 0)
529 {
530 for (ai = sp->ai; ai; ai = ai->ai_next)
531 {
532 if (ai->ai_family == AF_INET6)
533 {
534 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
535 if (s != -1)
536 break;
537 }
538 }
539 }
540 if (s == -1)
541 {
542 for (ai = sp->ai; ai; ai = ai->ai_next)
543 {
544 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
545 if (s != -1)
546 break;
547 }
548 }
549 if (s == -1)
550 return 0;
551 yaz_log(log_level, "First socket fd=%d", s);
552 assert(ai);
553 h->iofile = s;
554 if (ai->ai_family == AF_INET6 && sp->ipv6_only >= 0 &&
555 setsockopt(h->iofile,
556 IPPROTO_IPV6,
557 IPV6_V6ONLY, &sp->ipv6_only, sizeof(sp->ipv6_only)))
558 return 0;
559 if (sp->bind_host)
560 {
561 int r = -1;
562 int ipv6_only = 0;
563 struct addrinfo *ai;
564
565#ifndef WIN32
566 int one = 1;
567 if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*)
568 &one, sizeof(one)) < 0)
569 {
570 cs_set_error(h, CSYSERR, 0);
571 return 0;
572 }
573#endif
574 ai = tcpip_getaddrinfo(sp->bind_host, "0", &ipv6_only);
575 if (!ai)
576 return 0;
577 {
578 struct addrinfo *a;
579 for (a = ai; a; a = a->ai_next)
580 {
581 r = bind(h->iofile, a->ai_addr, a->ai_addrlen);
582 if (!r)
583 break;
584 }
585 }
586 if (r)
587 {
588 cs_set_error(h, CSYSERR, 0);
589 freeaddrinfo(ai);
590 return 0;
591 }
592 freeaddrinfo(ai);
593 }
594 if (!tcpip_set_blocking(h, h->flags))
595 return 0;
596 return ai;
597}
598
599#if RESOLVER_THREAD
600
601void *resolver_thread(void *arg)
602{
603 COMSTACK h = (COMSTACK) arg;
604 tcpip_state *sp = (tcpip_state *)h->cprivate;
605
606 sp->ipv6_only = 0;
607 if (sp->ai)
608 freeaddrinfo(sp->ai);
609 sp->ai = tcpip_getaddrinfo(sp->host_port, sp->port, &sp->ipv6_only);
610 if (write(sp->pipefd[1], "1", 1) != 1)
611 yaz_log(YLOG_WARN, "tcpip.c: resolver_thread: write error");
612
613 return 0;
614}
615
616static struct addrinfo *wait_resolver_thread(COMSTACK h)
617{
618 tcpip_state *sp = (tcpip_state *)h->cprivate;
619 char buf;
620
621 if (read(sp->pipefd[0], &buf, 1) == (ssize_t) -1)
622 return 0;
623 yaz_thread_join(&sp->thread_id, 0);
624 close(sp->pipefd[0]);
625 close(sp->pipefd[1]);
626 sp->pipefd[0] = -1;
627 h->iofile = -1;
628 return create_net_socket(h);
629}
630
631#endif
632
633void *tcpip_straddr(COMSTACK h, const char *str)
634{
635 tcpip_state *sp = (tcpip_state *)h->cprivate;
636 const char *port = "210";
637
638 if (!tcpip_init())
639 return 0;
640
641 if (h->protocol == PROTO_HTTP)
642 {
643 if (h->type == ssl_type)
644 port = "443";
645 else
646 port = "80";
647 }
648 xfree(sp->host_port);
649 sp->host_port = xstrdup(str);
650#if RESOLVER_THREAD
652 {
653 if (sp->pipefd[0] != -1)
654 return 0;
655 if (pipe(sp->pipefd) == -1)
656 return 0;
657
658 sp->port = port;
659 sp->thread_id = yaz_thread_create(resolver_thread, h);
660 return sp->host_port;
661 }
662#endif
663 if (sp->ai)
664 freeaddrinfo(sp->ai);
665 sp->ai = tcpip_getaddrinfo(sp->host_port, port, &sp->ipv6_only);
666 if (sp->ai && h->state == CS_ST_UNBND)
667 {
668 return create_net_socket(h);
669 }
670 return sp->ai;
671}
672
674{
675 tcpip_state *sp = (tcpip_state *)h->cprivate;
676
677 return sp->altlen && (*sp->complete)(sp->altbuf, sp->altlen) != 0;
678}
679
681{
682 tcpip_state *sp = (tcpip_state *)h->cprivate;
683 struct addrinfo *ai = sp->ai_connect;
684 while (ai && (ai = ai->ai_next))
685 {
686 int s;
687 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
688 if (s != -1)
689 {
690#if HAVE_GNUTLS_H
691 if (h->type == ssl_type && sp->session)
692 {
693 gnutls_bye(sp->session, GNUTLS_SHUT_WR);
694 gnutls_deinit(sp->session);
695 sp->session = 0;
696 }
697#endif
698#ifdef WIN32
699 closesocket(h->iofile);
700#else
701 close(h->iofile);
702#endif
703 yaz_log(log_level, "Other socket call fd=%d", s);
704 h->state = CS_ST_UNBND;
705 h->iofile = s;
707 return tcpip_connect(h, ai);
708 }
709 }
710 cs_set_error(h, CSYSERR, 0);
711 return -1;
712}
713
714
715/*
716 * connect(2) will block (sometimes) - nothing we can do short of doing
717 * weird things like spawning subprocesses or threading or some weird junk
718 * like that.
719 */
720int tcpip_connect(COMSTACK h, void *address)
721{
722 struct addrinfo *ai = (struct addrinfo *) address;
723 tcpip_state *sp = (tcpip_state *)h->cprivate;
724 int r;
725 yaz_log(log_level, "tcpip_connect h=%p", h);
726 h->io_pending = 0;
727 if (h->state != CS_ST_UNBND)
728 {
730 return -1;
731 }
732#if RESOLVER_THREAD
733 if (sp->pipefd[0] != -1)
734 {
735 if (h->flags & CS_FLAGS_BLOCKING)
736 {
737 ai = wait_resolver_thread(h);
738 if (!ai)
739 return -1;
740 }
741 else
742 {
743 h->event = CS_CONNECT;
746 h->iofile = sp->pipefd[0];
747 return 1;
748 }
749 }
750#endif
751 r = connect(h->iofile, ai->ai_addr, ai->ai_addrlen);
752 sp->ai_connect = ai;
753 if (r < 0)
754 {
755#ifdef WIN32
756 if (WSAGetLastError() == WSAEWOULDBLOCK)
757 {
758 h->event = CS_CONNECT;
761 return 1;
762 }
763#else
764 if (yaz_errno() == EINPROGRESS)
765 {
766 yaz_log(log_level, "Pending fd=%d", h->iofile);
767 h->event = CS_CONNECT;
770 return 1;
771 }
772#endif
773 return cont_connect(h);
774 }
775 h->event = CS_CONNECT;
777
778 return tcpip_rcvconnect(h);
779}
780
781#if HAVE_GNUTLS_H
782static int check_cert(COMSTACK h, tcpip_state *sp)
783{
784 if (h->flags & CS_FLAGS_CHECK_CERT)
785 {
786 unsigned int status = 0;
787 const char *vhost = 0;
788 const char *vport = 0;
789 char vtmp[512];
790 int vr;
791 const char *check_host = sp->connect_host ? sp->connect_host : sp->host_port;
792 parse_host_port(check_host, vtmp, sizeof vtmp, &vhost, &vport);
793 vr = gnutls_certificate_verify_peers3(sp->session, vhost, &status);
794 if (vr < 0)
795 {
796 yaz_log(log_level, "TLS certificate verification error: %s",
797 gnutls_strerror(vr));
798 cs_set_error(h, CSERRORSSL, gnutls_strerror(vr));
799 return -1;
800 }
801 if (status != 0)
802 {
803 char details[600];
804
805 yaz_log(log_level, "TLS certificate verification failed:"
806 " status=%u host=%s",
807 status, vhost ? vhost : "");
808 yaz_snprintf(details, sizeof(details),
809 "TLS certificate verification failed:"
810 " status=%u host=%s",
811 status, vhost ? vhost : "");
812 cs_set_error(h, CSERRORSSL, details);
813 return -1;
814 }
815 }
816 return 0;
817}
818#endif
819
821{
822 tcpip_state *sp = (tcpip_state *)h->cprivate;
823 yaz_log(log_level, "tcpip_rcvconnect h=%p", h);
824
825 if (h->state == CS_ST_DATAXFER)
826 return 0;
827#if RESOLVER_THREAD
828 if (sp->pipefd[0] != -1)
829 {
830 struct addrinfo *ai = wait_resolver_thread(h);
831 if (!ai)
832 return -1;
833 h->state = CS_ST_UNBND;
834 return tcpip_connect(h, ai);
835 }
836#endif
837 if (h->state != CS_ST_CONNECTING)
838 {
840 return -1;
841 }
842 if (sp->connect_host)
843 {
844 int r;
845
846 if (sp->connect_phase == 0)
847 {
850 yaz_log(log_level, "tcpip_rcvconnect connect put r=%d", r);
851 h->event = CS_CONNECT; /* because tcpip_put sets it */
852 if (r) /* < 0 is error, 1 is in-complete */
853 return r;
854 yaz_log(log_level, "tcpip_rcvconnect connect complete");
855 sp->connect_phase = 1;
856 }
857 if (sp->connect_phase == 1)
858 {
859 int code;
861 yaz_log(log_level, "tcpip_rcvconnect connect get r=%d", r);
862 if (r == 1)
863 return r;
864 if (r <= 0)
865 return -1;
867 {
868 yaz_log(log_level, "tcpip_rcvconnect connect failed: bad response");
869 cs_set_error(h, CSPROTERR, 0);
870 return -1;
871 }
872 if (code != 200)
873 {
874 yaz_log(log_level, "tcpip_rcvconnect connect failed: status %d", code);
875 cs_set_error(h, CSDENY, 0);
876 return -1;
877 }
879 sp->connect_phase = 2;
880 }
881 }
882#if HAVE_GNUTLS_H
883 if (h->type == ssl_type) {
884 yaz_log(log_level, "tcpip_rcvconnect SSL handshake");
885 }
886 if (h->type == ssl_type && !sp->session)
887 {
888 const char *host = 0;
889 const char *port = 0;
890 const char *check_host = sp->connect_host ? sp->connect_host : sp->host_port;
891 char tmp[512];
892 int r;
893
894 sp->cred_ptr = tcpip_create_cred();
895 if (h->flags & CS_FLAGS_CHECK_CERT)
896 {
897 r = gnutls_certificate_set_x509_system_trust(sp->cred_ptr->xcred);
898 if (r < 0)
899 {
900 yaz_log(log_level, "gnutls_certificate_set_x509_system_trust r=%d msg=%s", r, gnutls_strerror(r));
901 cs_set_error(h, CSERRORSSL, gnutls_strerror(r));
902 tcpip_release_cred(&sp->cred_ptr);
903 return -1;
904 }
905 }
906 if (sp->cert_fname[0])
907 {
908 int res;
909 res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred,
910 sp->cert_fname,
911 sp->key_fname[0] ? sp->key_fname : sp->cert_fname,
912 GNUTLS_X509_FMT_PEM);
913 if (res != GNUTLS_E_SUCCESS)
914 {
915 yaz_log(log_level, "gnutls_certificate_set_x509_key_file r=%d fatal=%d msg=%s",
916 res,
917 gnutls_error_is_fatal(res),
918 gnutls_strerror(res));
919 cs_set_error(h, CSERRORSSL, gnutls_strerror(res));
920 return -1;
921 }
922 }
923 gnutls_init(&sp->session, GNUTLS_CLIENT);
924 sp->use_bye = 1; /* only say goodbye in client */
925 gnutls_set_default_priority(sp->session);
926 gnutls_credentials_set(sp->session, GNUTLS_CRD_CERTIFICATE, sp->cred_ptr->xcred);
927 parse_host_port(check_host, tmp, sizeof tmp, &host, &port);
928 /* raw IPV6 seems to be rejected on the server */
929 if (!strchr(host, ':'))
930 gnutls_server_name_set(sp->session, GNUTLS_NAME_DNS,
931 host, strlen(host));
932 SET_GNUTLS_SOCKET(sp->session, h->iofile);
933 }
934 while (sp->session)
935 {
936 int res = gnutls_handshake(sp->session);
937 if (res == GNUTLS_E_SUCCESS)
938 {
939 int ret = check_cert(h, sp);
940 if (ret == 0)
941 break;
942 return ret;
943 }
944 if (ssl_check_again(h, sp, res))
945 {
946 if (!(h->flags & CS_FLAGS_BLOCKING))
947 return 1;
948 }
949 else
950 {
951 return cont_connect(h);
952 }
953 }
954#endif
955 h->event = CS_DATA;
957 return 0;
958}
959
960static int tcpip_bind(COMSTACK h, void *address, int mode)
961{
962 int r;
963 tcpip_state *sp = (tcpip_state *)h->cprivate;
964 struct addrinfo *ai = (struct addrinfo *) address;
965#ifdef WIN32
966 BOOL one = 1;
967#else
968 int one = 1;
969#endif
970
971 yaz_log(log_level, "tcpip_bind h=%p", h);
972#if RESOLVER_THREAD
973 if (sp->pipefd[0] != -1)
974 {
975 ai = wait_resolver_thread(h);
976 if (!ai)
977 return -1;
978 }
979#endif
980#if HAVE_GNUTLS_H
981 if (h->type == ssl_type && !sp->session)
982 {
983 int res;
984
985 /* yaz.pem is the default for server */
986 if (sp->yaz_pem_default)
987 strcpy(sp->cert_fname, "yaz.pem");
988
989 sp->cred_ptr = tcpip_create_cred();
990 res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred,
991 sp->cert_fname,
992 sp->key_fname[0] ? sp->key_fname : sp->cert_fname,
993 GNUTLS_X509_FMT_PEM);
994 if (res != GNUTLS_E_SUCCESS)
995 {
996 yaz_log(log_level, "gnutls_certificate_set_x509_key_file r=%d fatal=%d msg=%s",
997 res,
998 gnutls_error_is_fatal(res),
999 gnutls_strerror(res));
1000 cs_set_error(h, CSERRORSSL, gnutls_strerror(res));
1001 return -1;
1002 }
1003 }
1004#endif
1005#ifndef WIN32
1006 if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*)
1007 &one, sizeof(one)) < 0)
1008 {
1009 cs_set_error(h, CSYSERR, 0);
1010 return -1;
1011 }
1012#endif
1013 r = bind(h->iofile, ai->ai_addr, ai->ai_addrlen);
1014 freeaddrinfo(sp->ai);
1015 sp->ai = 0;
1016 if (r)
1017 {
1018 cs_set_error(h, CSYSERR, 0);
1019 return -1;
1020 }
1021 /* Allow a maximum-sized backlog of waiting-to-connect clients */
1022 if (mode == CS_SERVER && listen(h->iofile, SOMAXCONN) < 0)
1023 {
1024 cs_set_error(h, CSYSERR, 0);
1025 return -1;
1026 }
1027 h->state = CS_ST_IDLE;
1028 h->event = CS_LISTEN;
1029 return 0;
1030}
1031
1032int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
1033 int (*check_ip)(void *cd, const char *a, int len, int t),
1034 void *cd)
1035{
1036#ifdef WIN32
1037 /* we don't get peer address on Windows (via accept) */
1038#else
1039 struct sockaddr_in addr;
1040 YAZ_SOCKLEN_T len = sizeof(addr);
1041#endif
1042
1043 yaz_log(log_level, "tcpip_listen h=%p", h);
1044 if (h->state != CS_ST_IDLE)
1045 {
1046 cs_set_error(h, CSOUTSTATE, 0);
1047 return -1;
1048 }
1049#ifdef WIN32
1050 h->newfd = accept(h->iofile, 0, 0);
1051#else
1052 h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
1053#endif
1054 if (h->newfd < 0)
1055 {
1056 if (
1057#ifdef WIN32
1058 WSAGetLastError() == WSAEWOULDBLOCK
1059#else
1060 yaz_errno() == EWOULDBLOCK
1061#ifdef EAGAIN
1062#if EAGAIN != EWOULDBLOCK
1063 || yaz_errno() == EAGAIN
1064#endif
1065#endif
1066#endif
1067 )
1068 cs_set_error(h, CSNODATA, 0);
1069 else
1070 {
1071 shutdown(h->iofile, 0); /* SHUT_RD/SHUT_RECEIVE */
1072 listen(h->iofile, SOMAXCONN);
1073 cs_set_error(h, CSYSERR, 0);
1074 }
1075 return -1;
1076 }
1077#ifdef WIN32
1078 if (addrlen)
1079 *addrlen = 0;
1080#else
1081 if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_in))
1082 memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in));
1083 else if (addrlen)
1084 *addrlen = 0;
1085 if (check_ip && (*check_ip)(cd, (const char *) &addr,
1086 sizeof(addr), AF_INET))
1087 {
1088 cs_set_error(h, CSDENY, 0);
1089#ifdef WIN32
1090 closesocket(h->newfd);
1091#else
1092 close(h->newfd);
1093#endif
1094 h->newfd = -1;
1095 return -1;
1096 }
1097#endif
1098 h->state = CS_ST_INCON;
1099 return 0;
1100}
1101
1103{
1104 COMSTACK cnew;
1105#ifdef WIN32
1106 unsigned long tru = 1;
1107#endif
1108
1109 yaz_log(log_level, "tcpip_accept h=%p", h);
1110 if (h->state == CS_ST_INCON)
1111 {
1112#if HAVE_GNUTLS_H
1113 tcpip_state *st = (tcpip_state *)h->cprivate;
1114#endif
1116 cnew = (COMSTACK) xmalloc(sizeof(*cnew));
1117
1118 memcpy(cnew, h, sizeof(*h));
1119 cnew->iofile = h->newfd;
1120 cnew->io_pending = 0;
1121 cnew->cprivate = state;
1122
1123 if (!tcpip_set_blocking(cnew, cnew->flags))
1124 {
1125 cs_set_error(h, CSYSERR, 0);
1126 if (h->newfd != -1)
1127 {
1128#ifdef WIN32
1129 closesocket(h->newfd);
1130#else
1131 close(h->newfd);
1132#endif
1133 h->newfd = -1;
1134 }
1135 tcpip_state_destroy(state);
1136 xfree(cnew);
1137 return 0;
1138 }
1139 h->newfd = -1;
1140 cnew->state = CS_ST_ACCEPT;
1141 h->state = CS_ST_IDLE;
1142
1143#if HAVE_GNUTLS_H
1144 state->cred_ptr = st->cred_ptr;
1145 if (st->cred_ptr)
1146 {
1147 int res;
1148
1149 (state->cred_ptr->ref)++;
1150 gnutls_init(&state->session, GNUTLS_SERVER);
1151 if (!state->session)
1152 {
1153 tcpip_close(cnew);
1154 return 0;
1155 }
1156 res = gnutls_set_default_priority(state->session);
1157 if (res != GNUTLS_E_SUCCESS)
1158 {
1159 tcpip_close(cnew);
1160 return 0;
1161 }
1162 res = gnutls_credentials_set(state->session,
1163 GNUTLS_CRD_CERTIFICATE,
1164 st->cred_ptr->xcred);
1165 if (res != GNUTLS_E_SUCCESS)
1166 {
1167 tcpip_close(cnew);
1168 return 0;
1169 }
1170 SET_GNUTLS_SOCKET(state->session, cnew->iofile);
1171 }
1172#endif
1173 h = cnew;
1174 }
1175 if (h->state == CS_ST_ACCEPT)
1176 {
1177#if HAVE_GNUTLS_H
1178 tcpip_state *state = (tcpip_state *)h->cprivate;
1179 if (state->session)
1180 {
1181 int res = gnutls_handshake(state->session);
1182 if (res != GNUTLS_E_SUCCESS)
1183 {
1184 if (ssl_check_again(h, state, res))
1185 {
1186 yaz_log(log_level, "tcpip_accept gnutls_handshake interrupted");
1187 return h;
1188 }
1189 yaz_log(log_level, "tcpip_accept gnutls_handshake failed");
1190 cs_close(h);
1191 return 0;
1192 }
1193 }
1194#endif
1195 }
1196 else
1197 {
1198 cs_set_error(h, CSOUTSTATE, 0);
1199 return 0;
1200 }
1201 h->io_pending = 0;
1202 h->state = CS_ST_DATAXFER;
1203 h->event = CS_DATA;
1204 return h;
1205}
1206
1207#define CS_TCPIP_BUFCHUNK 4096
1208
1209/*
1210 * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
1211 * 0=connection closed.
1212 */
1213int tcpip_get(COMSTACK h, char **buf, int *bufsize)
1214{
1215 tcpip_state *sp = (tcpip_state *)h->cprivate;
1216 char *tmpc;
1217 int tmpi, berlen, rest, req, tomove;
1218 int hasread = 0, res;
1219
1220 yaz_log(log_level, "tcpip_get h=%p bufsize=%d", h, *bufsize);
1221 if (sp->altlen) /* switch buffers */
1222 {
1223 yaz_log(log_level, " %d bytes in altbuf (%p)", sp->altlen, sp->altbuf);
1224 tmpc = *buf;
1225 tmpi = *bufsize;
1226 *buf = sp->altbuf;
1227 *bufsize = sp->altsize;
1228 hasread = sp->altlen;
1229 sp->altlen = 0;
1230 sp->altbuf = tmpc;
1231 sp->altsize = tmpi;
1232 }
1233 h->io_pending = 0;
1234 while ((berlen = (*sp->complete)(*buf, hasread)) == 0)
1235 {
1236 if (!*bufsize)
1237 {
1238 if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
1239 {
1240 cs_set_error(h, CSYSERR, 0);
1241 return -1;
1242 }
1243 }
1244 else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
1245 {
1246 if (*bufsize > h->max_recv_bytes / 2)
1247 *bufsize = h->max_recv_bytes;
1248 else
1249 *bufsize = *bufsize * 2;
1250 if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
1251 {
1252 cs_set_error(h, CSBUFSIZE, 0);
1253 return -1;
1254 }
1255 if (!(*buf = (char *)xrealloc(*buf, *bufsize)))
1256 {
1257 cs_set_error(h, CSYSERR, 0);
1258 return -1;
1259 }
1260 }
1261#if HAVE_GNUTLS_H
1262 if (sp->session)
1263 {
1264 res = gnutls_record_recv(sp->session, *buf + hasread,
1266 if (res == 0)
1267 {
1268 yaz_log(log_level, "gnutls_record_recv returned 0");
1269 return 0;
1270 }
1271 else if (res < 0)
1272 {
1273 if (ssl_check_again(h, sp, res))
1274 {
1275 if (h->flags & CS_FLAGS_BLOCKING)
1276 continue;
1277 break;
1278 }
1279 return -1;
1280 }
1281 }
1282 else
1283#endif
1284 {
1285#ifdef __sun__
1286 yaz_set_errno(0);
1287 /* unfortunatly, sun sometimes forgets to set errno in recv
1288 when EWOULDBLOCK etc. would be required (res = -1) */
1289#endif
1290 res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0);
1291 yaz_log(log_level, " recv res=%d, hasread=%d", res, hasread);
1292 if (res < 0)
1293 {
1294 yaz_log(log_level, " recv errno=%d, (%s)", yaz_errno(),
1295 strerror(yaz_errno()));
1296#ifdef WIN32
1297 if (WSAGetLastError() == WSAEWOULDBLOCK)
1298 {
1300 break;
1301 }
1302 else
1303 {
1304 cs_set_error(h, CSYSERR, 0);
1305 return -1;
1306 }
1307#else
1308 if (yaz_errno() == EWOULDBLOCK
1309#ifdef EAGAIN
1310#if EAGAIN != EWOULDBLOCK
1311 || yaz_errno() == EAGAIN
1312#endif
1313#endif
1314 || yaz_errno() == EINPROGRESS
1315#ifdef __sun__
1316 || yaz_errno() == ENOENT /* Sun's sometimes set errno to this */
1317#endif
1318 )
1319 {
1321 break;
1322 }
1323 else if (yaz_errno() == 0)
1324 continue;
1325 else
1326 {
1327 cs_set_error(h, CSYSERR, 0);
1328 return -1;
1329 }
1330#endif
1331 }
1332 else if (!res)
1333 return hasread;
1334 }
1335 hasread += res;
1336 if (hasread > h->max_recv_bytes)
1337 {
1338 cs_set_error(h, CSBUFSIZE, 0);
1339 return -1;
1340 }
1341 }
1342 if (berlen < 0)
1343 {
1344 cs_set_error(h, CSPROTERR, 0);
1345 return -1;
1346 }
1347 yaz_log(log_level, " Out of read loop with hasread=%d, berlen=%d",
1348 hasread, berlen);
1349 /* move surplus buffer (or everything if we didn't get a BER rec.) */
1350 if (hasread > berlen)
1351 {
1352 tomove = req = hasread - berlen;
1353 rest = tomove % CS_TCPIP_BUFCHUNK;
1354 if (rest)
1355 req += CS_TCPIP_BUFCHUNK - rest;
1356 if (!sp->altbuf)
1357 {
1358 if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
1359 {
1360 cs_set_error(h, CSYSERR, 0);
1361 return -1;
1362 }
1363 } else if (sp->altsize < req)
1364 if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
1365 {
1366 cs_set_error(h, CSYSERR, 0);
1367 return -1;
1368 }
1369 yaz_log(log_level, " Moving %d bytes to altbuf(%p)", tomove,
1370 sp->altbuf);
1371 memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
1372 }
1373 if (berlen < CS_TCPIP_BUFCHUNK - 1)
1374 *(*buf + berlen) = '\0';
1375 return berlen ? berlen : 1;
1376}
1377
1378
1379/*
1380 * Returns 1, 0 or -1
1381 * In nonblocking mode, you must call again with same buffer while
1382 * return value is 1.
1383 */
1384int tcpip_put(COMSTACK h, char *buf, int size)
1385{
1386 int res;
1387 struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1388
1389 yaz_log(log_level, "tcpip_put h=%p size=%d", h, size);
1390 h->io_pending = 0;
1391 h->event = CS_DATA;
1392 if (state->towrite < 0)
1393 {
1394 state->towrite = size;
1395 state->written = 0;
1396 }
1397 else if (state->towrite != size)
1398 {
1399 cs_set_error(h, CSWRONGBUF, 0);
1400 return -1;
1401 }
1402 while (state->towrite > state->written)
1403 {
1404#if HAVE_GNUTLS_H
1405 if (state->session)
1406 {
1407 res = gnutls_record_send(state->session, buf + state->written,
1408 size - state->written);
1409 if (res < 0)
1410 {
1411 if (ssl_check_again(h, state, res))
1412 {
1413 if (h->flags & CS_FLAGS_BLOCKING)
1414 continue;
1415 return 1;
1416 }
1417 return -1;
1418 }
1419 }
1420 else
1421#endif
1422 {
1423 if ((res =
1424 send(h->iofile, buf + state->written, size -
1425 state->written,
1426#ifdef MSG_NOSIGNAL
1427 MSG_NOSIGNAL
1428#else
1429 0
1430#endif
1431 )) < 0)
1432 {
1433 if (
1434#ifdef WIN32
1435 WSAGetLastError() == WSAEWOULDBLOCK
1436#else
1437 yaz_errno() == EWOULDBLOCK
1438#ifdef EAGAIN
1439#if EAGAIN != EWOULDBLOCK
1440 || yaz_errno() == EAGAIN
1441#endif
1442#endif
1443#ifdef __sun__
1444 || yaz_errno() == ENOENT /* Sun's sometimes set errno to this value! */
1445#endif
1446 || yaz_errno() == EINPROGRESS
1447#endif
1448 )
1449 {
1450 yaz_log(log_level, " Flow control stop");
1452 return 1;
1453 }
1454 if (h->flags & CS_FLAGS_BLOCKING)
1455 {
1456 cs_set_error(h, CSYSERR, 0);
1457 return -1;
1458 }
1459 else
1460 return cont_connect(h);
1461 }
1462 }
1463 state->written += res;
1464 yaz_log(log_level, " Wrote %d, written=%d, nbytes=%d",
1465 res, state->written, size);
1466 }
1467 state->towrite = state->written = -1;
1468 yaz_log(log_level, " Ok");
1469 return 0;
1470}
1471
1473{
1474 tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1475
1476 yaz_log(log_level, "tcpip_close: h=%p", h);
1477#if RESOLVER_THREAD
1478 if (sp->pipefd[0] != -1)
1479 {
1480 yaz_thread_join(&sp->thread_id, 0);
1481 close(sp->pipefd[0]);
1482 close(sp->pipefd[1]);
1483 h->iofile = -1;
1484 }
1485#endif
1486 if (h->iofile != -1)
1487 {
1488#if HAVE_GNUTLS_H
1489 if (sp->session && sp->use_bye)
1490 {
1491 yaz_log(log_level, "tcpip_close: gnutls_bye");
1492 gnutls_bye(sp->session, GNUTLS_SHUT_WR);
1493 }
1494#endif
1495#ifdef WIN32
1496 closesocket(h->iofile);
1497#else
1498 close(h->iofile);
1499#endif
1500 }
1502 xfree(h);
1503}
1504
1506{
1507 tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1508 char *r = 0;
1509
1510 char host[120];
1511 struct sockaddr_storage addr;
1512 YAZ_SOCKLEN_T len = sizeof(addr);
1513
1514 if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0)
1515 {
1516 cs_set_error(h, CSYSERR, 0);
1517 return 0;
1518 }
1519 if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1,
1520 0, 0,
1521 (h->flags & CS_FLAGS_NUMERICHOST) ? NI_NUMERICHOST : 0))
1522 {
1523 r = "unknown";
1524 }
1525 else
1526 r = host;
1527
1528 if (h->protocol == PROTO_HTTP)
1529 yaz_snprintf(sp->buf, sizeof(sp->buf), "http:%s", r);
1530 else
1531 yaz_snprintf(sp->buf, sizeof(sp->buf), "tcp:%s", r);
1532#if HAVE_GNUTLS_H
1533 if (sp->session)
1534 {
1535 if (h->protocol == PROTO_HTTP)
1536 yaz_snprintf(sp->buf, sizeof(sp->buf), "https:%s", r);
1537 else
1538 yaz_snprintf(sp->buf, sizeof(sp->buf), "ssl:%s", r);
1539 }
1540#endif
1541 return sp->buf;
1542}
1543
1544static int tcpip_set_blocking(COMSTACK p, int flags)
1545{
1546 unsigned long flag;
1547
1548#ifdef WIN32
1549 flag = (flags & CS_FLAGS_BLOCKING) ? 0 : 1;
1550 if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0)
1551 return 0;
1552#else
1553 flag = fcntl(p->iofile, F_GETFL, 0);
1554 if (flags & CS_FLAGS_BLOCKING)
1555 flag = flag & ~O_NONBLOCK; /* blocking */
1556 else
1557 {
1558 flag = flag | O_NONBLOCK; /* non-blocking */
1559 signal(SIGPIPE, SIG_IGN);
1560 }
1561 if (fcntl(p->iofile, F_SETFL, flag) < 0)
1562 return 0;
1563#endif
1564 p->flags = flags;
1565 return 1;
1566}
1567
1568
1569#if HAVE_GNUTLS_H
1570/* gnutls_x509_crt_print appeared in 1.7.6. Memory leaks were fixed in 1.7.9.
1571 GNUTLS_CRT_PRINT_FULL appeared in 2.4.0. */
1572#if GNUTLS_VERSION_NUMBER >= 0x020400
1573#define USE_GNUTLS_X509_CRT_PRINT 1
1574#else
1575#define USE_GNUTLS_X509_CRT_PRINT 0
1576#endif
1577
1578
1579#if USE_GNUTLS_X509_CRT_PRINT
1580#else
1581static const char *bin2hex(const void *bin, size_t bin_size)
1582{
1583 static char printable[110];
1584 const unsigned char *_bin = bin;
1585 char *print;
1586 size_t i;
1587 if (bin_size > 50)
1588 bin_size = 50;
1589 print = printable;
1590 for (i = 0; i < bin_size; i++)
1591 {
1592 yaz_snprintf(print, 3, "%.2x", _bin[i]);
1593 print += 2;
1594 }
1595 return printable;
1596}
1597
1598static void x509_crt_print(gnutls_x509_crt_t cert)
1599{
1600 time_t expiration_time, activation_time;
1601 size_t size;
1602 char serial[40];
1603 char dn[256];
1604 unsigned int algo, bits;
1605
1606 expiration_time = gnutls_x509_crt_get_expiration_time(cert);
1607 activation_time = gnutls_x509_crt_get_activation_time(cert);
1608
1609 printf("\tCertificate is valid since: %s", ctime(&activation_time));
1610 printf("\tCertificate expires: %s", ctime(&expiration_time));
1611
1612 /* Print the serial number of the certificate. */
1613 size = sizeof(serial);
1614 gnutls_x509_crt_get_serial(cert, serial, &size);
1615
1616 printf("\tCertificate serial number: %s\n", bin2hex(serial, size));
1617
1618 /* Extract some of the public key algorithm's parameters
1619 */
1620 algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
1621
1622 printf("Certificate public key: %s", gnutls_pk_algorithm_get_name(algo));
1623
1624 /* Print the version of the X.509 certificate. */
1625 printf("\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert));
1626
1627 size = sizeof(dn);
1628 gnutls_x509_crt_get_dn(cert, dn, &size);
1629 printf("\tDN: %s\n", dn);
1630
1631 size = sizeof(dn);
1632 gnutls_x509_crt_get_issuer_dn(cert, dn, &size);
1633 printf("\tIssuer's DN: %s\n", dn);
1634}
1635#endif
1636#endif
1637
1639{
1640#if HAVE_GNUTLS_H
1641 struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1642 if (cs->type == ssl_type && sp->session)
1643 {
1644 const gnutls_datum_t *cert_list;
1645 unsigned i, cert_list_size;
1646 if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1647 return;
1648 printf("X509 certificate\n");
1649 cert_list = gnutls_certificate_get_peers(sp->session,
1650 &cert_list_size);
1651 printf("Peer provided %u certificates\n", cert_list_size);
1652 for (i = 0; i < cert_list_size; i++)
1653 {
1654 gnutls_x509_crt_t cert;
1655#if USE_GNUTLS_X509_CRT_PRINT
1656 int ret;
1657 gnutls_datum_t cinfo;
1658#endif
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,
1664 &cinfo);
1665 if (ret == 0)
1666 {
1667 printf("\t%s\n", cinfo.data);
1668 gnutls_free(cinfo.data);
1669 }
1670#else
1671 x509_crt_print(cert);
1672#endif
1673 gnutls_x509_crt_deinit(cert);
1674
1675 }
1676 }
1677#endif
1678}
1679
1681{
1682 /* doesn't do anything for GNUTLS */
1683 return 0;
1684}
1685
1686int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
1687{
1688#if HAVE_GNUTLS_H
1689 if (cs && cs->type == ssl_type)
1690 {
1691 /* doesn't do anything for GNUTLS */
1692 return 1;
1693 }
1694#endif
1695 return 0;
1696}
1697
1698int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
1699{
1700#if HAVE_GNUTLS_H
1701 if (cs && cs->type == ssl_type)
1702 {
1703 struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1704 sp->yaz_pem_default = 0;
1705 if (fname == 0 || *fname == '\0')
1706 sp->cert_fname[0] = '\0';
1707 else
1708 {
1709 const char *comma = strchr(fname, ',');
1710 if (comma)
1711 {
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';
1719 }
1720 else
1721 {
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';
1725 }
1726 }
1727 return 1;
1728 }
1729#endif
1730 return 0;
1731}
1732
1734{
1735
1736#if HAVE_GNUTLS_H
1737#if USE_GNUTLS_X509_CRT_PRINT
1738 struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1739 if (cs->type == ssl_type && sp->session)
1740 {
1741 const gnutls_datum_t *cert_list;
1742 unsigned cert_list_size;
1743 if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1744 return 0;
1745 cert_list = gnutls_certificate_get_peers(sp->session, &cert_list_size);
1746 if (cert_list_size > 0)
1747 {
1748 gnutls_x509_crt_t cert;
1749 int ret;
1750 gnutls_datum_t cinfo;
1751
1752 gnutls_x509_crt_init(&cert);
1753 gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
1754
1755 ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &cinfo);
1756 if (ret == 0)
1757 {
1758 *buf = xstrdup((char *) cinfo.data);
1759 *len = strlen(*buf);
1760 gnutls_free(cinfo.data);
1761 gnutls_x509_crt_deinit(cert);
1762 return 1;
1763 }
1764 gnutls_x509_crt_deinit(cert);
1765 }
1766 }
1767#endif
1768#endif
1769 return 0;
1770}
1771
1772int yaz_tcpip_set_head_only(COMSTACK cs, int head_only)
1773{
1774 int (*completer)(const char *buf, int len) =
1776
1777 if (cs->type == tcpip_type || cs->type == ssl_type)
1778 {
1779 tcpip_state *sp = (tcpip_state *)cs->cprivate;
1780 sp->complete = completer;
1781 return 0;
1782 }
1783 cs->cerrno = CSOUTSTATE;
1784 return -1;
1785}
1786
1788{
1789 if (cs->type == tcpip_type || cs->type == ssl_type)
1790 {
1791 tcpip_state *sp = (tcpip_state *)cs->cprivate;
1792 if (wrbuf_len(sp->error_details))
1793 return wrbuf_cstr(sp->error_details);
1794 }
1795 return 0;
1796}
1797
1798
1799/*
1800 * Local variables:
1801 * c-basic-offset: 4
1802 * c-file-style: "Stroustrup"
1803 * indent-tabs-mode: nil
1804 * End:
1805 * vim: shiftwidth=4 tabstop=8 expandtab
1806 */
void yaz_base64encode(const char *in, char *out)
encodes Base64 string
Definition base64.c:16
Header for Base64 utilities.
Private Header for COMSTACK.
const char * yaz_tcpip_get_error_details(COMSTACK cs)
Definition tcpip.c:1787
int yaz_tcpip_set_head_only(COMSTACK cs, int head_only)
Definition tcpip.c:1772
int cs_complete_auto(const char *buf, int len)
Finds the length of a complete BER or HTTP message.
Definition comstack.c:514
int cs_complete_auto_head(const char *buf, int len)
Finds the length of a BER message or HTTP headers.
Definition comstack.c:519
#define CS_ST_ACCEPT
Definition comstack.h:83
#define cs_close(handle)
Closes the socket and frees the endpoint and its transport state.
Definition comstack.h:223
#define CS_ST_CONNECTING
Definition comstack.h:85
#define CS_LISTEN
Definition comstack.h:97
#define CSWRONGBUF
Definition comstack.h:547
#define CS_DATA
Definition comstack.h:99
#define CSDENY
Definition comstack.h:549
#define CSOUTSTATE
Definition comstack.h:543
#define CSPROTERR
Definition comstack.h:555
#define CS_WANT_READ
Definition comstack.h:302
#define CS_NONE
Definition comstack.h:91
#define CSNODATA
Definition comstack.h:545
#define CS_CONNECT
Definition comstack.h:93
#define CS_FLAGS_CHECK_CERT
Definition comstack.h:566
#define CS_ST_UNBND
Definition comstack.h:73
struct comstack * COMSTACK
Communication endpoint; release with cs_close.
Definition comstack.h:48
#define CSBUFSIZE
Definition comstack.h:553
#define CS_FLAGS_DNS_NO_BLOCK
Definition comstack.h:564
#define CSERRORSSL
Definition comstack.h:551
#define CS_WANT_WRITE
Definition comstack.h:304
#define CS_SERVER
Definition comstack.h:116
#define CS_FLAGS_BLOCKING
Definition comstack.h:560
#define CS_FLAGS_NUMERICHOST
Definition comstack.h:562
#define CS_ST_DATAXFER
Definition comstack.h:81
#define CS_ST_INCON
Definition comstack.h:77
#define CS_ST_IDLE
Definition comstack.h:75
#define CSYSERR
Definition comstack.h:541
#define YAZ_SOCKLEN_T
Definition config.h:230
int yaz_errno(void)
returns errno
Definition errno.c:31
void yaz_set_errno(int v)
sets errno to value
Definition errno.c:36
Header for errno utilities.
static int log_level
Definition eventl.c:39
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)
Definition http.c:437
void yaz_init_globals(void)
void yaz_log(int level, const char *fmt,...)
Writes log message.
Definition log.c:487
int yaz_log_module_level(const char *name)
returns level for module
Definition log.c:586
Logging utility.
#define YLOG_WARN
log level: warning
Definition log.h:46
oid_proto
Definition oid_util.h:45
@ PROTO_HTTP
Definition oid_util.h:48
void yaz_snprintf(char *buf, size_t size, const char *fmt,...)
Definition snprintf.c:31
Header for config file reading utilities.
Public endpoint state and transport dispatch table.
Definition comstack.h:65
COMSTACK(*) f_accept(COMSTACK handle)
Definition comstack.h:122
int flags
Definition comstack.h:87
unsigned io_pending
Definition comstack.h:88
void * user
Definition comstack.h:131
const char *(*) f_addrstr(COMSTACK handle)
Definition comstack.h:126
int(*) f_rcvconnect(COMSTACK handle)
Definition comstack.h:110
int(*) f_bind(COMSTACK handle, void *address, int mode)
Definition comstack.h:112
CS_TYPE type
Definition comstack.h:66
int(*) f_put(COMSTACK handle, char *buf, int size)
Definition comstack.h:102
int(*) f_listen(COMSTACK h, char *raddr, int *addrlen, int(*check_ip)(void *cd, const char *a, int len, int type), void *cd)
Definition comstack.h:118
int state
Definition comstack.h:71
int event
Definition comstack.h:89
int newfd
Definition comstack.h:86
int(*) f_connect(COMSTACK handle, void *address)
Definition comstack.h:108
int(*) f_more(COMSTACK handle)
Definition comstack.h:106
int iofile
Definition comstack.h:68
int cerrno
Definition comstack.h:67
void *(*) f_straddr(COMSTACK handle, const char *str)
Definition comstack.h:128
enum oid_proto protocol
Definition comstack.h:100
void * cprivate
Definition comstack.h:69
int(*) f_get(COMSTACK handle, char **buf, int *bufsize)
Definition comstack.h:104
int(*) f_set_blocking(COMSTACK handle, int blocking)
Definition comstack.h:130
int max_recv_bytes
Definition comstack.h:70
void(*) f_close(COMSTACK handle)
Definition comstack.h:124
int connect_phase
Definition tcpip.c:145
struct addrinfo * ai_connect
Definition tcpip.c:128
int written
Definition tcpip.c:122
char * connect_response_buf
Definition tcpip.c:147
int connect_response_len
Definition tcpip.c:148
int(*) complete(const char *buf, int len)
Definition tcpip.c:124
int ipv6_only
Definition tcpip.c:129
char buf[128]
Definition tcpip.c:135
WRBUF error_details
Definition tcpip.c:149
char * host_port
Definition tcpip.c:126
char * bind_host
Definition tcpip.c:125
char * altbuf
Definition tcpip.c:118
int altlen
Definition tcpip.c:120
WRBUF connect_request
Definition tcpip.c:146
int towrite
Definition tcpip.c:123
int altsize
Definition tcpip.c:119
struct addrinfo * ai
Definition tcpip.c:127
char * connect_host
Definition tcpip.c:144
int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
Selects certificate and private key files for a TLS endpoint.
Definition tcpip.c:1698
void cs_print_session_info(COMSTACK cs)
Prints TLS peer certificate information to standard output.
Definition tcpip.c:1638
static struct addrinfo * create_net_socket(COMSTACK h)
Definition tcpip.c:523
int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
Gets a human-readable description of the first peer X.509 certificate.
Definition tcpip.c:1733
static void connect_and_bind(COMSTACK p, const char *connect_host, const char *connect_auth, const char *bind_host)
Definition tcpip.c:288
struct addrinfo * tcpip_getaddrinfo(const char *str, const char *port, int *ipv6_only)
Definition tcpip.c:471
COMSTACK yaz_ssl_create(int s, int flags, int protocol, const char *connect_host, const char *connect_auth, const char *bind_host)
Definition tcpip.c:401
static void * tcpip_straddr(COMSTACK h, const char *str)
Definition tcpip.c:633
COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
Definition tcpip.c:238
int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
Legacy OpenSSL context setter.
Definition tcpip.c:1686
COMSTACK yaz_tcpip_create2(int s, int flags, int protocol, const char *connect_host, const char *bind_host)
Definition tcpip.c:339
static int tcpip_listen(COMSTACK h, char *raddr, int *addrlen, int(*check_ip)(void *cd, const char *a, int len, int type), void *cd)
Definition tcpip.c:1032
static const char * tcpip_addrstr(COMSTACK h)
Definition tcpip.c:1505
COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
Definition tcpip.c:380
static int tcpip_more(COMSTACK h)
Definition tcpip.c:673
static int tcpip_get(COMSTACK h, char **buf, int *bufsize)
Definition tcpip.c:1213
static void cs_set_error(COMSTACK cs, int error, const char *details)
Definition tcpip.c:279
#define CS_TCPIP_BUFCHUNK
Definition tcpip.c:1207
static int cont_connect(COMSTACK h)
Definition tcpip.c:680
COMSTACK yaz_tcpip_create(int s, int flags, int protocol, const char *connect_host)
Definition tcpip.c:346
COMSTACK yaz_tcpip_create3(int s, int flags, int protocol, const char *connect_host, const char *connect_auth, const char *bind_host)
Definition tcpip.c:327
static void parse_host_port(const char *host_port, char *tmp, size_t tmp_sz, const char **host, const char **port)
Definition tcpip.c:432
static int tcpip_put(COMSTACK h, char *buf, int size)
Definition tcpip.c:1384
static void tcpip_state_destroy(tcpip_state *sp)
Definition tcpip.c:215
static struct tcpip_state * tcpip_state_create(void)
Definition tcpip.c:181
static void tcpip_close(COMSTACK h)
Definition tcpip.c:1472
static int tcpip_init(void)
Definition tcpip.c:154
void * cs_get_ssl(COMSTACK cs)
Legacy OpenSSL session accessor.
Definition tcpip.c:1680
static COMSTACK tcpip_accept(COMSTACK h)
Definition tcpip.c:1102
static int tcpip_connect(COMSTACK h, void *address)
Definition tcpip.c:720
static int tcpip_bind(COMSTACK h, void *address, int mode)
Definition tcpip.c:960
static int tcpip_set_blocking(COMSTACK p, int blocking)
Definition tcpip.c:1544
static int tcpip_rcvconnect(COMSTACK h)
Definition tcpip.c:820
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
Definition wrbuf.c:38
const char * wrbuf_cstr(WRBUF b)
returns WRBUF content as C-string
Definition wrbuf.c:299
void wrbuf_rewind(WRBUF b)
empty WRBUF content (length of buffer set to 0)
Definition wrbuf.c:47
WRBUF wrbuf_alloc(void)
construct WRBUF
Definition wrbuf.c:25
void wrbuf_printf(WRBUF b, const char *fmt,...)
writes printf result to WRBUF
Definition wrbuf.c:189
void wrbuf_puts(WRBUF b, const char *buf)
appends C-string to WRBUF
Definition wrbuf.c:89
Header for WRBUF (growing buffer).
#define wrbuf_buf(b)
Definition wrbuf.h:270
#define wrbuf_len(b)
Definition wrbuf.h:269
struct wrbuf * WRBUF
#define xstrdup(s)
utility macro which calls xstrdup_f
Definition xmalloc.h:55
#define xfree(x)
utility macro which calls xfree_f
Definition xmalloc.h:53
#define xrealloc(o, x)
utility macro which calls xrealloc_f
Definition xmalloc.h:47
#define xmalloc(x)
utility macro which calls malloc_f
Definition xmalloc.h:49
Header with fundamental macros.
Header for the Z_GDU (HTTP or Z39.50 package).