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