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