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