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