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