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