connect works with both SSL and TCP connections YAZ-825
[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             if (r) /* < 0 is error, 1 is in-complete */
822                 return r;
823             TRC(fprintf(stderr, "tcpip_put CONNECT complete\n"));
824             TRC(fwrite(sp->connect_request_buf, 1, sp->connect_request_len, stderr));
825         }
826         sp->connect_request_len = 0;
827
828         r = tcpip_get(h, &sp->connect_response_buf, &sp->connect_response_len);
829         TRC(fprintf(stderr, "tcpip_get CONNECT r=%d\n", r));
830         if (r == 1)
831             return r;
832         if (r <= 0)
833             return -1;
834         TRC(fwrite(sp->connect_response_buf, 1, r, stderr));
835         xfree(sp->connect_request_buf);
836         sp->connect_request_buf = 0;
837         sp->complete = cs_complete_auto;
838     }
839 #if HAVE_GNUTLS_H
840     if (h->type == ssl_type && !sp->session)
841     {
842         tcpip_create_cred(h);
843         gnutls_init(&sp->session, GNUTLS_CLIENT);
844         gnutls_set_default_priority(sp->session);
845         gnutls_credentials_set (sp->session, GNUTLS_CRD_CERTIFICATE,
846                                 sp->cred_ptr->xcred);
847         /* cast to intermediate size_t to avoid GCC warning. */
848         gnutls_transport_set_ptr(sp->session,
849                                  (gnutls_transport_ptr_t)
850                                  (size_t) h->iofile);
851     }
852     if (sp->session)
853     {
854         int res = gnutls_handshake(sp->session);
855         if (res < 0)
856         {
857             if (ssl_check_error(h, sp, res))
858                 return 1;
859             return cont_connect(h);
860         }
861     }
862 #endif
863     h->event = CS_DATA;
864     h->state = CS_ST_DATAXFER;
865     return 0;
866 }
867
868 #define CERTF "ztest.pem"
869 #define KEYF "ztest.pem"
870
871 static int tcpip_bind(COMSTACK h, void *address, int mode)
872 {
873     int r;
874     tcpip_state *sp = (tcpip_state *)h->cprivate;
875 #if HAVE_GETADDRINFO
876     struct addrinfo *ai = (struct addrinfo *) address;
877 #else
878     struct sockaddr *addr = (struct sockaddr *)address;
879 #endif
880 #ifdef WIN32
881     BOOL one = 1;
882 #else
883     int one = 1;
884 #endif
885
886 #if HAVE_GETADDRINFO
887 #if RESOLVER_THREAD
888     if (sp->pipefd[0] != -1)
889     {
890         ai = wait_resolver_thread(h);
891         if (!ai)
892             return -1;
893     }
894 #endif
895 #endif
896 #if HAVE_GNUTLS_H
897     if (h->type == ssl_type && !sp->session)
898     {
899         int res;
900         tcpip_create_cred(h);
901         res = gnutls_certificate_set_x509_key_file(sp->cred_ptr->xcred,
902                                                    sp->cert_fname,
903                                                    sp->cert_fname,
904                                                    GNUTLS_X509_FMT_PEM);
905         if (res != GNUTLS_E_SUCCESS)
906         {
907             h->cerrno = CSERRORSSL;
908             return -1;
909         }
910     }
911 #else
912     TRC(fprintf(stderr, "tcpip_bind\n"));
913 #endif
914 #ifndef WIN32
915     if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*)
916         &one, sizeof(one)) < 0)
917     {
918         h->cerrno = CSYSERR;
919         return -1;
920     }
921 #endif
922 #if HAVE_GETADDRINFO
923     r = bind(h->iofile, ai->ai_addr, ai->ai_addrlen);
924     freeaddrinfo(sp->ai);
925     sp->ai = 0;
926 #else
927     r = bind(h->iofile, addr, sizeof(struct sockaddr_in));
928 #endif
929     if (r)
930     {
931         h->cerrno = CSYSERR;
932         return -1;
933     }
934     /* Allow a maximum-sized backlog of waiting-to-connect clients */
935     if (mode == CS_SERVER && listen(h->iofile, SOMAXCONN) < 0)
936     {
937         h->cerrno = CSYSERR;
938         return -1;
939     }
940     h->state = CS_ST_IDLE;
941     h->event = CS_LISTEN;
942     return 0;
943 }
944
945 int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
946                  int (*check_ip)(void *cd, const char *a, int len, int t),
947                  void *cd)
948 {
949 #ifdef WIN32
950     /* we don't get peer address on Windows (via accept) */
951 #else
952     struct sockaddr_in addr;
953     YAZ_SOCKLEN_T len = sizeof(addr);
954 #endif
955
956     TRC(fprintf(stderr, "tcpip_listen pid=%d\n", getpid()));
957     if (h->state != CS_ST_IDLE)
958     {
959         h->cerrno = CSOUTSTATE;
960         return -1;
961     }
962 #ifdef WIN32
963     h->newfd = accept(h->iofile, 0, 0);
964 #else
965     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
966 #endif
967     if (h->newfd < 0)
968     {
969         if (
970 #ifdef WIN32
971             WSAGetLastError() == WSAEWOULDBLOCK
972 #else
973             yaz_errno() == EWOULDBLOCK
974 #ifdef EAGAIN
975 #if EAGAIN != EWOULDBLOCK
976             || yaz_errno() == EAGAIN
977 #endif
978 #endif
979 #endif
980             )
981             h->cerrno = CSNODATA;
982         else
983         {
984 #ifdef WIN32
985             shutdown(h->iofile, SD_RECEIVE);
986 #else
987             shutdown(h->iofile, SHUT_RD);
988 #endif
989             listen(h->iofile, SOMAXCONN);
990             h->cerrno = CSYSERR;
991         }
992         return -1;
993     }
994 #ifdef WIN32
995     if (addrlen)
996         *addrlen = 0;
997 #else
998     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_in))
999         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in));
1000     else if (addrlen)
1001         *addrlen = 0;
1002     if (check_ip && (*check_ip)(cd, (const char *) &addr,
1003         sizeof(addr), AF_INET))
1004     {
1005         h->cerrno = CSDENY;
1006 #ifdef WIN32
1007         closesocket(h->newfd);
1008 #else
1009         close(h->newfd);
1010 #endif
1011         h->newfd = -1;
1012         return -1;
1013     }
1014 #endif
1015     h->state = CS_ST_INCON;
1016     return 0;
1017 }
1018
1019 COMSTACK tcpip_accept(COMSTACK h)
1020 {
1021     COMSTACK cnew;
1022 #ifdef WIN32
1023     unsigned long tru = 1;
1024 #endif
1025
1026     TRC(fprintf(stderr, "tcpip_accept h=%p pid=%d\n", h, getpid()));
1027     if (h->state == CS_ST_INCON)
1028     {
1029         tcpip_state *st = (tcpip_state *)h->cprivate;
1030         tcpip_state *state = tcpip_state_create();
1031         cnew = (COMSTACK) xmalloc(sizeof(*cnew));
1032
1033         memcpy(cnew, h, sizeof(*h));
1034         cnew->iofile = h->newfd;
1035         cnew->io_pending = 0;
1036         cnew->cprivate = state;
1037
1038         if (!tcpip_set_blocking(cnew, cnew->flags))
1039         {
1040             h->cerrno = CSYSERR;
1041             if (h->newfd != -1)
1042             {
1043 #ifdef WIN32
1044                 closesocket(h->newfd);
1045 #else
1046                 close(h->newfd);
1047 #endif
1048                 h->newfd = -1;
1049             }
1050             xfree(state);
1051             xfree(cnew);
1052             return 0;
1053         }
1054         h->newfd = -1;
1055         cnew->state = CS_ST_ACCEPT;
1056         h->state = CS_ST_IDLE;
1057
1058 #if HAVE_GNUTLS_H
1059         state->cred_ptr = st->cred_ptr;
1060         if (st->cred_ptr)
1061         {
1062             int res;
1063
1064             (state->cred_ptr->ref)++;
1065             gnutls_init(&state->session, GNUTLS_SERVER);
1066             if (!state->session)
1067             {
1068                 xfree(cnew);
1069                 xfree(state);
1070                 return 0;
1071             }
1072             res = gnutls_set_default_priority(state->session);
1073             if (res != GNUTLS_E_SUCCESS)
1074             {
1075                 xfree(cnew);
1076                 xfree(state);
1077                 return 0;
1078             }
1079             res = gnutls_credentials_set(state->session,
1080                                          GNUTLS_CRD_CERTIFICATE,
1081                                          st->cred_ptr->xcred);
1082             if (res != GNUTLS_E_SUCCESS)
1083             {
1084                 xfree(cnew);
1085                 xfree(state);
1086                 return 0;
1087             }
1088             /* cast to intermediate size_t to avoid GCC warning. */
1089             gnutls_transport_set_ptr(state->session,
1090                                      (gnutls_transport_ptr_t)
1091                                      (size_t) cnew->iofile);
1092         }
1093 #endif
1094         h = cnew;
1095     }
1096     if (h->state == CS_ST_ACCEPT)
1097     {
1098 #if HAVE_GNUTLS_H
1099         tcpip_state *state = (tcpip_state *)h->cprivate;
1100         if (state->session)
1101         {
1102             int res = gnutls_handshake(state->session);
1103             if (res < 0)
1104             {
1105                 if (ssl_check_error(h, state, res))
1106                 {
1107                     TRC(fprintf(stderr, "gnutls_handshake int in tcpip_accept\n"));
1108                     return h;
1109                 }
1110                 TRC(fprintf(stderr, "gnutls_handshake failed in tcpip_accept\n"));
1111                 cs_close(h);
1112                 return 0;
1113             }
1114             TRC(fprintf(stderr, "SSL_accept complete. gnutls\n"));
1115         }
1116 #endif
1117     }
1118     else
1119     {
1120         h->cerrno = CSOUTSTATE;
1121         return 0;
1122     }
1123     h->io_pending = 0;
1124     h->state = CS_ST_DATAXFER;
1125     h->event = CS_DATA;
1126     return h;
1127 }
1128
1129 #define CS_TCPIP_BUFCHUNK 4096
1130
1131 /*
1132  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
1133  * 0=connection closed.
1134  */
1135 int tcpip_get(COMSTACK h, char **buf, int *bufsize)
1136 {
1137     tcpip_state *sp = (tcpip_state *)h->cprivate;
1138     char *tmpc;
1139     int tmpi, berlen, rest, req, tomove;
1140     int hasread = 0, res;
1141
1142     TRC(fprintf(stderr, "tcpip_get: h=%p bufsize=%d\n", h, *bufsize));
1143     if (sp->altlen) /* switch buffers */
1144     {
1145         TRC(fprintf(stderr, "  %d bytes in altbuf (%p)\n", sp->altlen,
1146                     sp->altbuf));
1147         tmpc = *buf;
1148         tmpi = *bufsize;
1149         *buf = sp->altbuf;
1150         *bufsize = sp->altsize;
1151         hasread = sp->altlen;
1152         sp->altlen = 0;
1153         sp->altbuf = tmpc;
1154         sp->altsize = tmpi;
1155     }
1156     h->io_pending = 0;
1157     while (!(berlen = (*sp->complete)(*buf, hasread)))
1158     {
1159         if (!*bufsize)
1160         {
1161             if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
1162             {
1163                 h->cerrno = CSYSERR;
1164                 return -1;
1165             }
1166         }
1167         else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
1168             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
1169             {
1170                 h->cerrno = CSYSERR;
1171                 return -1;
1172             }
1173 #ifdef __sun__
1174         yaz_set_errno( 0 );
1175         /* unfortunatly, sun sometimes forgets to set errno in recv
1176            when EWOULDBLOCK etc. would be required (res = -1) */
1177 #endif
1178         res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0);
1179         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
1180         if (res < 0)
1181         {
1182             TRC(fprintf(stderr, "  recv errno=%d, (%s)\n", yaz_errno(),
1183                       strerror(yaz_errno())));
1184 #ifdef WIN32
1185             if (WSAGetLastError() == WSAEWOULDBLOCK)
1186             {
1187                 h->io_pending = CS_WANT_READ;
1188                 break;
1189             }
1190             else
1191             {
1192                 h->cerrno = CSYSERR;
1193                 return -1;
1194             }
1195 #else
1196             if (yaz_errno() == EWOULDBLOCK
1197 #ifdef EAGAIN
1198 #if EAGAIN != EWOULDBLOCK
1199                 || yaz_errno() == EAGAIN
1200 #endif
1201 #endif
1202                 || yaz_errno() == EINPROGRESS
1203 #ifdef __sun__
1204                 || yaz_errno() == ENOENT /* Sun's sometimes set errno to this */
1205 #endif
1206                 )
1207             {
1208                 h->io_pending = CS_WANT_READ;
1209                 break;
1210             }
1211             else if (yaz_errno() == 0)
1212                 continue;
1213             else
1214             {
1215                 h->cerrno = CSYSERR;
1216                 return -1;
1217             }
1218 #endif
1219         }
1220         else if (!res)
1221             return hasread;
1222         hasread += res;
1223         if (hasread > h->max_recv_bytes)
1224         {
1225             h->cerrno = CSBUFSIZE;
1226             return -1;
1227         }
1228     }
1229     TRC(fprintf(stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
1230                 hasread, berlen));
1231     /* move surplus buffer (or everything if we didn't get a BER rec.) */
1232     if (hasread > berlen)
1233     {
1234         tomove = req = hasread - berlen;
1235         rest = tomove % CS_TCPIP_BUFCHUNK;
1236         if (rest)
1237             req += CS_TCPIP_BUFCHUNK - rest;
1238         if (!sp->altbuf)
1239         {
1240             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
1241             {
1242                 h->cerrno = CSYSERR;
1243                 return -1;
1244             }
1245         } else if (sp->altsize < req)
1246             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
1247             {
1248                 h->cerrno = CSYSERR;
1249                 return -1;
1250             }
1251         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(%p)\n", tomove,
1252                     sp->altbuf));
1253         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
1254     }
1255     if (berlen < CS_TCPIP_BUFCHUNK - 1)
1256         *(*buf + berlen) = '\0';
1257     return berlen ? berlen : 1;
1258 }
1259
1260
1261 #if HAVE_GNUTLS_H
1262 /*
1263  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
1264  * 0=connection closed.
1265  */
1266 int ssl_get(COMSTACK h, char **buf, int *bufsize)
1267 {
1268     tcpip_state *sp = (tcpip_state *)h->cprivate;
1269     char *tmpc;
1270     int tmpi, berlen, rest, req, tomove;
1271     int hasread = 0, res;
1272
1273     TRC(fprintf(stderr, "ssl_get: bufsize=%d\n", *bufsize));
1274     if (sp->altlen) /* switch buffers */
1275     {
1276         TRC(fprintf(stderr, "  %d bytes in altbuf (%p)\n", sp->altlen,
1277                     sp->altbuf));
1278         tmpc = *buf;
1279         tmpi = *bufsize;
1280         *buf = sp->altbuf;
1281         *bufsize = sp->altsize;
1282         hasread = sp->altlen;
1283         sp->altlen = 0;
1284         sp->altbuf = tmpc;
1285         sp->altsize = tmpi;
1286     }
1287     h->io_pending = 0;
1288     while (!(berlen = (*sp->complete)(*buf, hasread)))
1289     {
1290         if (!*bufsize)
1291         {
1292             if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
1293                 return -1;
1294         }
1295         else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
1296             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
1297                 return -1;
1298         res = gnutls_record_recv(sp->session, *buf + hasread,
1299                                  CS_TCPIP_BUFCHUNK);
1300         if (res == 0)
1301         {
1302             TRC(fprintf(stderr, "gnutls_record_recv returned 0\n"));
1303             return 0;
1304         }
1305         else if (res < 0)
1306         {
1307             if (ssl_check_error(h, sp, res))
1308                 break;
1309             return -1;
1310         }
1311         hasread += res;
1312     }
1313     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
1314         hasread, berlen));
1315     /* move surplus buffer (or everything if we didn't get a BER rec.) */
1316     if (hasread > berlen)
1317     {
1318         tomove = req = hasread - berlen;
1319         rest = tomove % CS_TCPIP_BUFCHUNK;
1320         if (rest)
1321             req += CS_TCPIP_BUFCHUNK - rest;
1322         if (!sp->altbuf)
1323         {
1324             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
1325                 return -1;
1326         } else if (sp->altsize < req)
1327             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
1328                 return -1;
1329         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(%p)\n", tomove,
1330                     sp->altbuf));
1331         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
1332     }
1333     if (berlen < CS_TCPIP_BUFCHUNK - 1)
1334         *(*buf + berlen) = '\0';
1335     return berlen ? berlen : 1;
1336 }
1337 #endif
1338
1339 /*
1340  * Returns 1, 0 or -1
1341  * In nonblocking mode, you must call again with same buffer while
1342  * return value is 1.
1343  */
1344 int tcpip_put(COMSTACK h, char *buf, int size)
1345 {
1346     int res;
1347     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1348
1349     TRC(fprintf(stderr, "tcpip_put: h=%p size=%d\n", h, size));
1350     h->io_pending = 0;
1351     h->event = CS_DATA;
1352     if (state->towrite < 0)
1353     {
1354         state->towrite = size;
1355         state->written = 0;
1356     }
1357     else if (state->towrite != size)
1358     {
1359         h->cerrno = CSWRONGBUF;
1360         return -1;
1361     }
1362     while (state->towrite > state->written)
1363     {
1364         if ((res =
1365              send(h->iofile, buf + state->written, size -
1366                   state->written,
1367 #ifdef MSG_NOSIGNAL
1368                   MSG_NOSIGNAL
1369 #else
1370                   0
1371 #endif
1372                  )) < 0)
1373         {
1374             if (
1375 #ifdef WIN32
1376                 WSAGetLastError() == WSAEWOULDBLOCK
1377 #else
1378                 yaz_errno() == EWOULDBLOCK
1379 #ifdef EAGAIN
1380 #if EAGAIN != EWOULDBLOCK
1381              || yaz_errno() == EAGAIN
1382 #endif
1383 #endif
1384 #ifdef __sun__
1385                 || yaz_errno() == ENOENT /* Sun's sometimes set errno to this value! */
1386 #endif
1387                 || yaz_errno() == EINPROGRESS
1388 #endif
1389                 )
1390             {
1391                 TRC(fprintf(stderr, "  Flow control stop\n"));
1392                 h->io_pending = CS_WANT_WRITE;
1393                 return 1;
1394             }
1395             if (h->flags & CS_FLAGS_BLOCKING)
1396             {
1397                 h->cerrno = CSYSERR;
1398                 return -1;
1399             }
1400             else
1401                 return cont_connect(h);
1402         }
1403         state->written += res;
1404         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
1405                     res, state->written, size));
1406     }
1407     state->towrite = state->written = -1;
1408     TRC(fprintf(stderr, "  Ok\n"));
1409     return 0;
1410 }
1411
1412
1413 #if HAVE_GNUTLS_H
1414 /*
1415  * Returns 1, 0 or -1
1416  * In nonblocking mode, you must call again with same buffer while
1417  * return value is 1.
1418  */
1419 int ssl_put(COMSTACK h, char *buf, int size)
1420 {
1421     int res;
1422     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1423
1424     TRC(fprintf(stderr, "ssl_put: size=%d\n", size));
1425     h->io_pending = 0;
1426     h->event = CS_DATA;
1427     if (state->towrite < 0)
1428     {
1429         state->towrite = size;
1430         state->written = 0;
1431     }
1432     else if (state->towrite != size)
1433     {
1434         h->cerrno = CSWRONGBUF;
1435         return -1;
1436     }
1437     while (state->towrite > state->written)
1438     {
1439         res = gnutls_record_send(state->session, buf + state->written,
1440                                  size - state->written);
1441         if (res <= 0)
1442         {
1443             if (ssl_check_error(h, state, res))
1444                 return 1;
1445             return -1;
1446         }
1447         state->written += res;
1448         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
1449                     res, state->written, size));
1450     }
1451     state->towrite = state->written = -1;
1452     TRC(fprintf(stderr, "  Ok\n"));
1453     return 0;
1454 }
1455 #endif
1456
1457 void tcpip_close(COMSTACK h)
1458 {
1459     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1460
1461     TRC(fprintf(stderr, "tcpip_close: h=%p pid=%d\n", h, getpid()));
1462 #if HAVE_GETADDRINFO
1463     xfree(sp->bind_host);
1464 #if RESOLVER_THREAD
1465     if (sp->pipefd[0] != -1)
1466     {
1467         yaz_thread_join(&sp->thread_id, 0);
1468         close(sp->pipefd[0]);
1469         close(sp->pipefd[1]);
1470         h->iofile = -1;
1471     }
1472 #endif
1473 #endif
1474     if (h->iofile != -1)
1475     {
1476 #if HAVE_GNUTLS_H
1477         if (sp->session)
1478             gnutls_bye(sp->session, GNUTLS_SHUT_WR);
1479 #endif
1480 #ifdef WIN32
1481         closesocket(h->iofile);
1482 #else
1483         close(h->iofile);
1484 #endif
1485     }
1486     if (sp->altbuf)
1487         xfree(sp->altbuf);
1488 #if HAVE_GNUTLS_H
1489     if (sp->session)
1490     {
1491         gnutls_deinit(sp->session);
1492     }
1493     if (sp->cred_ptr)
1494     {
1495         assert(sp->cred_ptr->ref > 0);
1496
1497         if (--(sp->cred_ptr->ref) == 0)
1498         {
1499             TRC(fprintf(stderr, "Removed credentials %p pid=%d\n",
1500                         sp->cred_ptr->xcred, getpid()));
1501             gnutls_certificate_free_credentials(sp->cred_ptr->xcred);
1502             xfree(sp->cred_ptr);
1503         }
1504         sp->cred_ptr = 0;
1505     }
1506 #endif
1507 #if HAVE_GETADDRINFO
1508     if (sp->ai)
1509         freeaddrinfo(sp->ai);
1510 #if RESOLVER_THREAD
1511     xfree(sp->hoststr);
1512 #endif
1513 #endif
1514     xfree(sp->connect_request_buf);
1515     xfree(sp->connect_response_buf);
1516     xfree(sp);
1517     xfree(h);
1518 }
1519
1520 const char *tcpip_addrstr(COMSTACK h)
1521 {
1522     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1523     char *r = 0, *buf = sp->buf;
1524
1525 #if HAVE_GETADDRINFO
1526     char host[120];
1527     struct sockaddr_storage addr;
1528     YAZ_SOCKLEN_T len = sizeof(addr);
1529
1530     if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0)
1531     {
1532         h->cerrno = CSYSERR;
1533         return 0;
1534     }
1535     if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1,
1536                     0, 0,
1537                     (h->flags & CS_FLAGS_NUMERICHOST) ? NI_NUMERICHOST : 0))
1538     {
1539         r = "unknown";
1540     }
1541     else
1542         r = host;
1543
1544 #else
1545
1546     struct sockaddr_in addr;
1547     YAZ_SOCKLEN_T len = sizeof(addr);
1548     struct hostent *host;
1549
1550     if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0)
1551     {
1552         h->cerrno = CSYSERR;
1553         return 0;
1554     }
1555     if (!(h->flags & CS_FLAGS_NUMERICHOST))
1556     {
1557         if ((host = gethostbyaddr((char*)&addr.sin_addr,
1558                                   sizeof(addr.sin_addr),
1559                                   AF_INET)))
1560             r = (char*) host->h_name;
1561     }
1562     if (!r)
1563         r = inet_ntoa(addr.sin_addr);
1564 #endif
1565
1566     if (h->protocol == PROTO_HTTP)
1567         sprintf(buf, "http:%s", r);
1568     else
1569         sprintf(buf, "tcp:%s", r);
1570 #if HAVE_GNUTLS_H
1571     if (sp->session)
1572     {
1573         if (h->protocol == PROTO_HTTP)
1574             sprintf(buf, "https:%s", r);
1575         else
1576             sprintf(buf, "ssl:%s", r);
1577     }
1578 #endif
1579     return buf;
1580 }
1581
1582 static int tcpip_set_blocking(COMSTACK p, int flags)
1583 {
1584     unsigned long flag;
1585
1586 #ifdef WIN32
1587     flag = (flags & CS_FLAGS_BLOCKING) ? 0 : 1;
1588     if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0)
1589         return 0;
1590 #else
1591     flag = fcntl(p->iofile, F_GETFL, 0);
1592     if (flags & CS_FLAGS_BLOCKING)
1593         flag = flag & ~O_NONBLOCK;  /* blocking */
1594     else
1595     {
1596         flag = flag | O_NONBLOCK;   /* non-blocking */
1597         signal(SIGPIPE, SIG_IGN);
1598     }
1599     if (fcntl(p->iofile, F_SETFL, flag) < 0)
1600         return 0;
1601 #endif
1602     p->flags = flags;
1603     return 1;
1604 }
1605
1606
1607 #if HAVE_GNUTLS_H
1608 /* gnutls_x509_crt_print appeared in 1.7.6. Memory leaks were fixed in 1.7.9.
1609    GNUTLS_CRT_PRINT_FULL appeared in 2.4.0. */
1610 #if GNUTLS_VERSION_NUMBER >= 0x020400
1611 #define USE_GNUTLS_X509_CRT_PRINT 1
1612 #else
1613 #define USE_GNUTLS_X509_CRT_PRINT 0
1614 #endif
1615
1616
1617 #if USE_GNUTLS_X509_CRT_PRINT
1618 #else
1619 static const char *bin2hex(const void *bin, size_t bin_size)
1620 {
1621     static char printable[110];
1622     const unsigned char *_bin = bin;
1623     char *print;
1624     size_t i;
1625     if (bin_size > 50)
1626         bin_size = 50;
1627     print = printable;
1628     for (i = 0; i < bin_size; i++)
1629     {
1630         sprintf(print, "%.2x ", _bin[i]);
1631         print += 2;
1632     }
1633     return printable;
1634 }
1635
1636 static void x509_crt_print(gnutls_x509_crt_t cert)
1637 {
1638     time_t expiration_time, activation_time;
1639     size_t size;
1640     char serial[40];
1641     char dn[256];
1642     unsigned int algo, bits;
1643
1644     expiration_time = gnutls_x509_crt_get_expiration_time(cert);
1645     activation_time = gnutls_x509_crt_get_activation_time(cert);
1646
1647     printf("\tCertificate is valid since: %s", ctime(&activation_time));
1648     printf("\tCertificate expires: %s", ctime(&expiration_time));
1649
1650     /* Print the serial number of the certificate. */
1651     size = sizeof(serial);
1652     gnutls_x509_crt_get_serial(cert, serial, &size);
1653     
1654     printf("\tCertificate serial number: %s\n", bin2hex(serial, size));
1655     
1656     /* Extract some of the public key algorithm's parameters
1657      */
1658     algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
1659     
1660     printf("Certificate public key: %s", gnutls_pk_algorithm_get_name(algo));
1661     
1662     /* Print the version of the X.509 certificate. */
1663     printf("\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert));
1664     
1665     size = sizeof(dn);
1666     gnutls_x509_crt_get_dn(cert, dn, &size);
1667     printf("\tDN: %s\n", dn);
1668     
1669     size = sizeof(dn);
1670     gnutls_x509_crt_get_issuer_dn(cert, dn, &size);
1671     printf("\tIssuer's DN: %s\n", dn);
1672 }
1673 #endif
1674 #endif
1675
1676 void cs_print_session_info(COMSTACK cs)
1677 {
1678 #if HAVE_GNUTLS_H
1679     struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1680     if (cs->type == ssl_type && sp->session)
1681     {
1682         const gnutls_datum_t *cert_list;
1683         unsigned i, cert_list_size;
1684         if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1685             return;
1686         printf("X509 certificate\n");
1687         cert_list = gnutls_certificate_get_peers(sp->session,
1688                                                  &cert_list_size);
1689         printf("Peer provided %u certificates\n", cert_list_size);
1690         for (i = 0; i < cert_list_size; i++)
1691         {
1692             gnutls_x509_crt_t cert;
1693 #if USE_GNUTLS_X509_CRT_PRINT
1694             int ret;
1695             gnutls_datum_t cinfo;
1696 #endif
1697             gnutls_x509_crt_init(&cert);
1698             gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER);
1699             printf("Certificate info %d:\n", i + 1);
1700 #if USE_GNUTLS_X509_CRT_PRINT
1701             ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL,
1702                                         &cinfo);
1703             if (ret == 0)
1704             {
1705                 printf("\t%s\n", cinfo.data);
1706                 gnutls_free(cinfo.data);
1707             }
1708 #else
1709             x509_crt_print(cert);
1710 #endif
1711             gnutls_x509_crt_deinit(cert);
1712
1713         }
1714     }
1715 #endif
1716 }
1717
1718 void *cs_get_ssl(COMSTACK cs)
1719 {
1720     /* doesn't do anything for GNUTLS */
1721     return 0;
1722 }
1723
1724 int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
1725 {
1726 #if HAVE_GNUTLS_H
1727     if (cs && cs->type == ssl_type)
1728     {
1729         /* doesn't do anything for GNUTLS */
1730         return 1;
1731     }
1732 #endif
1733     return 0;
1734 }
1735
1736 int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
1737 {
1738 #if HAVE_GNUTLS_H
1739     if (cs && cs->type == ssl_type)
1740     {
1741         struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1742         strncpy(sp->cert_fname, fname, sizeof(sp->cert_fname)-1);
1743         sp->cert_fname[sizeof(sp->cert_fname)-1] = '\0';
1744         return 1;
1745     }
1746 #endif
1747     return 0;
1748 }
1749
1750 int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
1751 {
1752
1753 #if HAVE_GNUTLS_H
1754 #if USE_GNUTLS_X509_CRT_PRINT
1755     struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1756     if (cs->type == ssl_type && sp->session)
1757     {
1758         const gnutls_datum_t *cert_list;
1759         unsigned cert_list_size;
1760         if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1761             return 0;
1762         cert_list = gnutls_certificate_get_peers(sp->session, &cert_list_size);
1763         if (cert_list_size > 0)
1764         {
1765             gnutls_x509_crt_t cert;
1766             int ret;
1767             gnutls_datum_t cinfo;
1768
1769             gnutls_x509_crt_init(&cert);
1770             gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
1771
1772             ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &cinfo);
1773             if (ret == 0)
1774             {
1775                 *buf = xstrdup((char *) cinfo.data);
1776                 *len = strlen(*buf);
1777                 gnutls_free(cinfo.data);
1778                 gnutls_x509_crt_deinit(cert);
1779                 return 1;
1780             }
1781             gnutls_x509_crt_deinit(cert);
1782         }
1783     }
1784 #endif
1785 #endif
1786     return 0;
1787 }
1788
1789 /*
1790  * Local variables:
1791  * c-basic-offset: 4
1792  * c-file-style: "Stroustrup"
1793  * indent-tabs-mode: nil
1794  * End:
1795  * vim: shiftwidth=4 tabstop=8 expandtab
1796  */
1797