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