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