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