Add Ubuntu Vivid 15.04
[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         state->altlen = 0; /* reset input buf in case of excess bytes YAZ-830 */
1295     }
1296     else if (state->towrite != size)
1297     {
1298         h->cerrno = CSWRONGBUF;
1299         return -1;
1300     }
1301     while (state->towrite > state->written)
1302     {
1303 #if HAVE_GNUTLS_H
1304         if (state->session)
1305         {
1306             res = gnutls_record_send(state->session, buf + state->written,
1307                                      size - state->written);
1308             if (res <= 0)
1309             {
1310                 if (ssl_check_error(h, state, res))
1311                     return 1;
1312                 return -1;
1313             }
1314         }
1315         else
1316 #endif
1317         {
1318             if ((res =
1319                  send(h->iofile, buf + state->written, size -
1320                       state->written,
1321 #ifdef MSG_NOSIGNAL
1322                       MSG_NOSIGNAL
1323 #else
1324                       0
1325 #endif
1326                      )) < 0)
1327             {
1328                 if (
1329 #ifdef WIN32
1330                     WSAGetLastError() == WSAEWOULDBLOCK
1331 #else
1332                     yaz_errno() == EWOULDBLOCK
1333 #ifdef EAGAIN
1334 #if EAGAIN != EWOULDBLOCK
1335                     || yaz_errno() == EAGAIN
1336 #endif
1337 #endif
1338 #ifdef __sun__
1339                     || yaz_errno() == ENOENT /* Sun's sometimes set errno to this value! */
1340 #endif
1341                     || yaz_errno() == EINPROGRESS
1342 #endif
1343                     )
1344                 {
1345                     TRC(fprintf(stderr, "  Flow control stop\n"));
1346                     h->io_pending = CS_WANT_WRITE;
1347                     return 1;
1348                 }
1349                 if (h->flags & CS_FLAGS_BLOCKING)
1350                 {
1351                     h->cerrno = CSYSERR;
1352                     return -1;
1353                 }
1354                 else
1355                     return cont_connect(h);
1356             }
1357         }
1358         state->written += res;
1359         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
1360                     res, state->written, size));
1361     }
1362     state->towrite = state->written = -1;
1363     TRC(fprintf(stderr, "  Ok\n"));
1364     return 0;
1365 }
1366
1367 void tcpip_close(COMSTACK h)
1368 {
1369     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1370
1371     TRC(fprintf(stderr, "tcpip_close: h=%p pid=%d\n", h, getpid()));
1372 #if HAVE_GETADDRINFO
1373     xfree(sp->bind_host);
1374 #if RESOLVER_THREAD
1375     if (sp->pipefd[0] != -1)
1376     {
1377         yaz_thread_join(&sp->thread_id, 0);
1378         close(sp->pipefd[0]);
1379         close(sp->pipefd[1]);
1380         h->iofile = -1;
1381     }
1382 #endif
1383 #endif
1384     if (h->iofile != -1)
1385     {
1386 #if HAVE_GNUTLS_H
1387         if (sp->session)
1388             gnutls_bye(sp->session, GNUTLS_SHUT_WR);
1389 #endif
1390 #ifdef WIN32
1391         closesocket(h->iofile);
1392 #else
1393         close(h->iofile);
1394 #endif
1395     }
1396     if (sp->altbuf)
1397         xfree(sp->altbuf);
1398 #if HAVE_GNUTLS_H
1399     if (sp->session)
1400     {
1401         gnutls_deinit(sp->session);
1402     }
1403     if (sp->cred_ptr)
1404     {
1405         assert(sp->cred_ptr->ref > 0);
1406
1407         if (--(sp->cred_ptr->ref) == 0)
1408         {
1409             TRC(fprintf(stderr, "Removed credentials %p pid=%d\n",
1410                         sp->cred_ptr->xcred, getpid()));
1411             gnutls_certificate_free_credentials(sp->cred_ptr->xcred);
1412             xfree(sp->cred_ptr);
1413         }
1414         sp->cred_ptr = 0;
1415     }
1416 #endif
1417 #if HAVE_GETADDRINFO
1418     if (sp->ai)
1419         freeaddrinfo(sp->ai);
1420 #if RESOLVER_THREAD
1421     xfree(sp->hoststr);
1422 #endif
1423 #endif
1424     xfree(sp->connect_request_buf);
1425     xfree(sp->connect_response_buf);
1426     xfree(sp);
1427     xfree(h);
1428 }
1429
1430 const char *tcpip_addrstr(COMSTACK h)
1431 {
1432     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1433     char *r = 0, *buf = sp->buf;
1434
1435 #if HAVE_GETADDRINFO
1436     char host[120];
1437     struct sockaddr_storage addr;
1438     YAZ_SOCKLEN_T len = sizeof(addr);
1439
1440     if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0)
1441     {
1442         h->cerrno = CSYSERR;
1443         return 0;
1444     }
1445     if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1,
1446                     0, 0,
1447                     (h->flags & CS_FLAGS_NUMERICHOST) ? NI_NUMERICHOST : 0))
1448     {
1449         r = "unknown";
1450     }
1451     else
1452         r = host;
1453
1454 #else
1455
1456     struct sockaddr_in addr;
1457     YAZ_SOCKLEN_T len = sizeof(addr);
1458     struct hostent *host;
1459
1460     if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0)
1461     {
1462         h->cerrno = CSYSERR;
1463         return 0;
1464     }
1465     if (!(h->flags & CS_FLAGS_NUMERICHOST))
1466     {
1467         if ((host = gethostbyaddr((char*)&addr.sin_addr,
1468                                   sizeof(addr.sin_addr),
1469                                   AF_INET)))
1470             r = (char*) host->h_name;
1471     }
1472     if (!r)
1473         r = inet_ntoa(addr.sin_addr);
1474 #endif
1475
1476     if (h->protocol == PROTO_HTTP)
1477         sprintf(buf, "http:%s", r);
1478     else
1479         sprintf(buf, "tcp:%s", r);
1480 #if HAVE_GNUTLS_H
1481     if (sp->session)
1482     {
1483         if (h->protocol == PROTO_HTTP)
1484             sprintf(buf, "https:%s", r);
1485         else
1486             sprintf(buf, "ssl:%s", r);
1487     }
1488 #endif
1489     return buf;
1490 }
1491
1492 static int tcpip_set_blocking(COMSTACK p, int flags)
1493 {
1494     unsigned long flag;
1495
1496 #ifdef WIN32
1497     flag = (flags & CS_FLAGS_BLOCKING) ? 0 : 1;
1498     if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0)
1499         return 0;
1500 #else
1501     flag = fcntl(p->iofile, F_GETFL, 0);
1502     if (flags & CS_FLAGS_BLOCKING)
1503         flag = flag & ~O_NONBLOCK;  /* blocking */
1504     else
1505     {
1506         flag = flag | O_NONBLOCK;   /* non-blocking */
1507         signal(SIGPIPE, SIG_IGN);
1508     }
1509     if (fcntl(p->iofile, F_SETFL, flag) < 0)
1510         return 0;
1511 #endif
1512     p->flags = flags;
1513     return 1;
1514 }
1515
1516
1517 #if HAVE_GNUTLS_H
1518 /* gnutls_x509_crt_print appeared in 1.7.6. Memory leaks were fixed in 1.7.9.
1519    GNUTLS_CRT_PRINT_FULL appeared in 2.4.0. */
1520 #if GNUTLS_VERSION_NUMBER >= 0x020400
1521 #define USE_GNUTLS_X509_CRT_PRINT 1
1522 #else
1523 #define USE_GNUTLS_X509_CRT_PRINT 0
1524 #endif
1525
1526
1527 #if USE_GNUTLS_X509_CRT_PRINT
1528 #else
1529 static const char *bin2hex(const void *bin, size_t bin_size)
1530 {
1531     static char printable[110];
1532     const unsigned char *_bin = bin;
1533     char *print;
1534     size_t i;
1535     if (bin_size > 50)
1536         bin_size = 50;
1537     print = printable;
1538     for (i = 0; i < bin_size; i++)
1539     {
1540         sprintf(print, "%.2x ", _bin[i]);
1541         print += 2;
1542     }
1543     return printable;
1544 }
1545
1546 static void x509_crt_print(gnutls_x509_crt_t cert)
1547 {
1548     time_t expiration_time, activation_time;
1549     size_t size;
1550     char serial[40];
1551     char dn[256];
1552     unsigned int algo, bits;
1553
1554     expiration_time = gnutls_x509_crt_get_expiration_time(cert);
1555     activation_time = gnutls_x509_crt_get_activation_time(cert);
1556
1557     printf("\tCertificate is valid since: %s", ctime(&activation_time));
1558     printf("\tCertificate expires: %s", ctime(&expiration_time));
1559
1560     /* Print the serial number of the certificate. */
1561     size = sizeof(serial);
1562     gnutls_x509_crt_get_serial(cert, serial, &size);
1563     
1564     printf("\tCertificate serial number: %s\n", bin2hex(serial, size));
1565     
1566     /* Extract some of the public key algorithm's parameters
1567      */
1568     algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
1569     
1570     printf("Certificate public key: %s", gnutls_pk_algorithm_get_name(algo));
1571     
1572     /* Print the version of the X.509 certificate. */
1573     printf("\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert));
1574     
1575     size = sizeof(dn);
1576     gnutls_x509_crt_get_dn(cert, dn, &size);
1577     printf("\tDN: %s\n", dn);
1578     
1579     size = sizeof(dn);
1580     gnutls_x509_crt_get_issuer_dn(cert, dn, &size);
1581     printf("\tIssuer's DN: %s\n", dn);
1582 }
1583 #endif
1584 #endif
1585
1586 void cs_print_session_info(COMSTACK cs)
1587 {
1588 #if HAVE_GNUTLS_H
1589     struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1590     if (cs->type == ssl_type && sp->session)
1591     {
1592         const gnutls_datum_t *cert_list;
1593         unsigned i, cert_list_size;
1594         if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1595             return;
1596         printf("X509 certificate\n");
1597         cert_list = gnutls_certificate_get_peers(sp->session,
1598                                                  &cert_list_size);
1599         printf("Peer provided %u certificates\n", cert_list_size);
1600         for (i = 0; i < cert_list_size; i++)
1601         {
1602             gnutls_x509_crt_t cert;
1603 #if USE_GNUTLS_X509_CRT_PRINT
1604             int ret;
1605             gnutls_datum_t cinfo;
1606 #endif
1607             gnutls_x509_crt_init(&cert);
1608             gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER);
1609             printf("Certificate info %d:\n", i + 1);
1610 #if USE_GNUTLS_X509_CRT_PRINT
1611             ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL,
1612                                         &cinfo);
1613             if (ret == 0)
1614             {
1615                 printf("\t%s\n", cinfo.data);
1616                 gnutls_free(cinfo.data);
1617             }
1618 #else
1619             x509_crt_print(cert);
1620 #endif
1621             gnutls_x509_crt_deinit(cert);
1622
1623         }
1624     }
1625 #endif
1626 }
1627
1628 void *cs_get_ssl(COMSTACK cs)
1629 {
1630     /* doesn't do anything for GNUTLS */
1631     return 0;
1632 }
1633
1634 int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
1635 {
1636 #if HAVE_GNUTLS_H
1637     if (cs && cs->type == ssl_type)
1638     {
1639         /* doesn't do anything for GNUTLS */
1640         return 1;
1641     }
1642 #endif
1643     return 0;
1644 }
1645
1646 int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
1647 {
1648 #if HAVE_GNUTLS_H
1649     if (cs && cs->type == ssl_type)
1650     {
1651         struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1652         strncpy(sp->cert_fname, fname, sizeof(sp->cert_fname)-1);
1653         sp->cert_fname[sizeof(sp->cert_fname)-1] = '\0';
1654         return 1;
1655     }
1656 #endif
1657     return 0;
1658 }
1659
1660 int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
1661 {
1662
1663 #if HAVE_GNUTLS_H
1664 #if USE_GNUTLS_X509_CRT_PRINT
1665     struct tcpip_state *sp = (struct tcpip_state *) cs->cprivate;
1666     if (cs->type == ssl_type && sp->session)
1667     {
1668         const gnutls_datum_t *cert_list;
1669         unsigned cert_list_size;
1670         if (gnutls_certificate_type_get(sp->session) != GNUTLS_CRT_X509)
1671             return 0;
1672         cert_list = gnutls_certificate_get_peers(sp->session, &cert_list_size);
1673         if (cert_list_size > 0)
1674         {
1675             gnutls_x509_crt_t cert;
1676             int ret;
1677             gnutls_datum_t cinfo;
1678
1679             gnutls_x509_crt_init(&cert);
1680             gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
1681
1682             ret = gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &cinfo);
1683             if (ret == 0)
1684             {
1685                 *buf = xstrdup((char *) cinfo.data);
1686                 *len = strlen(*buf);
1687                 gnutls_free(cinfo.data);
1688                 gnutls_x509_crt_deinit(cert);
1689                 return 1;
1690             }
1691             gnutls_x509_crt_deinit(cert);
1692         }
1693     }
1694 #endif
1695 #endif
1696     return 0;
1697 }
1698
1699 /*
1700  * Local variables:
1701  * c-basic-offset: 4
1702  * c-file-style: "Stroustrup"
1703  * indent-tabs-mode: nil
1704  * End:
1705  * vim: shiftwidth=4 tabstop=8 expandtab
1706  */
1707