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