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