Minor formatting changes.
[yaz-moved-to-github.git] / src / tcpip.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2008 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file tcpip.c
7  * \brief Implements TCP/IP + SSL COMSTACK.
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #if HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif
19 #if HAVE_SYS_TIME_H
20 #include <sys/time.h>
21 #endif
22 #if HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25
26 #ifdef WIN32
27
28 /* VS 2003 or later has getaddrinfo; older versions do not */
29 #include <winsock2.h>
30 #if _MSC_VER >= 1300
31 #include <ws2tcpip.h>
32 #define HAVE_GETADDRINFO 1
33 #else
34 #define HAVE_GETADDRINFO 0
35 #endif
36
37 #else
38 #include <netinet/in.h>
39 #include <netdb.h>
40 #include <arpa/inet.h>
41 #include <netinet/tcp.h>
42 #endif
43
44 #if HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
46 #endif
47 #if HAVE_SYS_WAIT_H
48 #include <sys/wait.h>
49 #endif
50
51 #if HAVE_GNUTLS_H
52 #include <gnutls/openssl.h>
53 #define ENABLE_SSL 1
54 #endif
55
56 #if HAVE_OPENSSL_SSL_H
57 #include <openssl/ssl.h>
58 #include <openssl/err.h>
59 #define ENABLE_SSL 1
60 #endif
61
62 #include <yaz/comstack.h>
63 #include <yaz/tcpip.h>
64 #include <yaz/nmem.h>
65
66 static int tcpip_close(COMSTACK h);
67 static int tcpip_put(COMSTACK h, char *buf, int size);
68 static int tcpip_get(COMSTACK h, char **buf, int *bufsize);
69 static int tcpip_put_connect(COMSTACK h, char *buf, int size);
70 static int tcpip_get_connect(COMSTACK h, char **buf, int *bufsize);
71 static int tcpip_connect(COMSTACK h, void *address);
72 static int tcpip_more(COMSTACK h);
73 static int tcpip_rcvconnect(COMSTACK h);
74 static int tcpip_bind(COMSTACK h, void *address, int mode);
75 static int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
76                  int (*check_ip)(void *cd, const char *a, int len, int type),
77                  void *cd);
78 static int tcpip_set_blocking(COMSTACK p, int blocking);
79
80 #if ENABLE_SSL
81 static int ssl_get(COMSTACK h, char **buf, int *bufsize);
82 static int ssl_put(COMSTACK h, char *buf, int size);
83 #endif
84
85 static COMSTACK tcpip_accept(COMSTACK h);
86 static char *tcpip_addrstr(COMSTACK h);
87 static void *tcpip_straddr(COMSTACK h, const char *str);
88
89 #if 0
90 #define TRC(x) x
91 #else
92 #define TRC(X)
93 #endif
94
95 #ifndef YAZ_SOCKLEN_T
96 #define YAZ_SOCKLEN_T int
97 #endif
98
99 /* this state is used for both SSL and straight TCP/IP */
100 typedef struct tcpip_state
101 {
102     char *altbuf; /* alternate buffer for surplus data */
103     int altsize;  /* size as xmalloced */
104     int altlen;   /* length of data or 0 if none */
105
106     int written;  /* -1 if we aren't writing */
107     int towrite;  /* to verify against user input */
108     int (*complete)(const char *buf, int len); /* length/complete. */
109 #if HAVE_GETADDRINFO
110     struct addrinfo *ai;
111 #else
112     struct sockaddr_in addr;  /* returned by cs_straddr */
113 #endif
114     char buf[128]; /* returned by cs_addrstr */
115 #if ENABLE_SSL
116     SSL_CTX *ctx;       /* current CTX. */
117     SSL_CTX *ctx_alloc; /* If =ctx it is owned by CS. If 0 it is not owned */
118     SSL *ssl;
119     char cert_fname[256];
120 #endif
121     char *connect_request_buf;
122     int connect_request_len;
123     char *connect_response_buf;
124     int connect_response_len;
125 } tcpip_state;
126
127 #ifdef WIN32
128 static int tcpip_init(void)
129 {
130     static int initialized = 0;
131     if (!initialized)
132     {
133         WORD requested;
134         WSADATA wd;
135
136         requested = MAKEWORD(1, 1);
137         if (WSAStartup(requested, &wd))
138             return 0;
139         initialized = 1;
140     }
141     return 1;
142 }
143 #else
144 static int tcpip_init(void)
145 {
146     return 1;
147 }
148 #endif
149
150 /*
151  * This function is always called through the cs_create() macro.
152  * s >= 0: socket has already been established for us.
153  */
154 COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
155 {
156     COMSTACK p;
157     tcpip_state *sp;
158
159     if (!tcpip_init())
160         return 0;
161     if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
162         return 0;
163     if (!(sp = (struct tcpip_state *)(p->cprivate =
164                                          xmalloc(sizeof(tcpip_state)))))
165         return 0;
166
167     p->flags = flags;
168
169     p->io_pending = 0;
170     p->iofile = s;
171     p->type = tcpip_type;
172     p->protocol = (enum oid_proto) protocol;
173
174     p->f_connect = tcpip_connect;
175     p->f_rcvconnect = tcpip_rcvconnect;
176     p->f_get = tcpip_get;
177     p->f_put = tcpip_put;
178     p->f_close = tcpip_close;
179     p->f_more = tcpip_more;
180     p->f_bind = tcpip_bind;
181     p->f_listen = tcpip_listen;
182     p->f_accept = tcpip_accept;
183     p->f_addrstr = tcpip_addrstr;
184     p->f_straddr = tcpip_straddr;
185     p->f_set_blocking = tcpip_set_blocking;
186     p->max_recv_bytes = 5000000;
187
188     p->state = s < 0 ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
189     p->event = CS_NONE;
190     p->cerrno = 0;
191     p->stackerr = 0;
192     p->user = 0;
193
194 #if ENABLE_SSL
195     sp->ctx = sp->ctx_alloc = 0;
196     sp->ssl = 0;
197     strcpy(sp->cert_fname, "yaz.pem");
198 #endif
199
200 #if HAVE_GETADDRINFO
201     sp->ai = 0;
202 #endif
203     sp->altbuf = 0;
204     sp->altsize = sp->altlen = 0;
205     sp->towrite = sp->written = -1;
206     if (protocol == PROTO_WAIS)
207         sp->complete = completeWAIS;
208     else
209         sp->complete = cs_complete_auto;
210
211     sp->connect_request_buf = 0;
212     sp->connect_request_len = 0;
213     sp->connect_response_buf = 0;
214     sp->connect_response_len = 0;
215
216     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
217     TRC(fprintf(stderr, "Created new TCPIP comstack\n"));
218
219     return p;
220 }
221
222 COMSTACK yaz_tcpip_create(int s, int flags, int protocol,
223                           const char *connect_host)
224 {
225     COMSTACK p = tcpip_type(s, flags, protocol, 0);
226     if (!p)
227         return 0;
228     if (connect_host)
229     {
230         tcpip_state *sp = (tcpip_state *) p->cprivate;
231         sp->connect_request_buf = (char *) xmalloc(strlen(connect_host) + 30);
232         sprintf(sp->connect_request_buf, "CONNECT %s HTTP/1.0\r\n\r\n",
233                 connect_host);
234         sp->connect_request_len = strlen(sp->connect_request_buf);
235         p->f_put = tcpip_put_connect;
236         p->f_get = tcpip_get_connect;
237         sp->complete = cs_complete_auto_head; /* only want HTTP header */
238     }
239     return p;
240 }
241
242
243 #if ENABLE_SSL
244
245 COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
246 {
247     tcpip_state *sp;
248     COMSTACK p;
249
250     p = tcpip_type(s, flags, protocol, 0);
251     if (!p)
252         return 0;
253     p->f_get = ssl_get;
254     p->f_put = ssl_put;
255     p->type = ssl_type;
256     sp = (tcpip_state *) p->cprivate;
257
258     sp->ctx = (SSL_CTX *) vp;  /* may be NULL */
259
260     /* note: we don't handle already opened socket in SSL mode - yet */
261     return p;
262 }
263
264 int ssl_check_error(COMSTACK h, tcpip_state *sp, int res)
265 {
266 #if HAVE_OPENSSL_SSL_H
267     int err = SSL_get_error(sp->ssl, res);
268     TRC(fprintf(stderr, "got err=%d\n", err));
269     if (err == SSL_ERROR_WANT_READ)
270     {
271         TRC(fprintf(stderr, " -> SSL_ERROR_WANT_READ\n"));
272         h->io_pending = CS_WANT_READ;
273         return 1;
274     }
275     if (err == SSL_ERROR_WANT_WRITE)
276     {
277         TRC(fprintf(stderr, " -> SSL_ERROR_WANT_WRITE\n"));
278         h->io_pending = CS_WANT_WRITE;
279         return 1;
280     }
281 #else
282     int tls_error = sp->ssl->last_error;
283     TRC(fprintf(stderr, "ssl_check_error error=%d fatal=%d msg=%s\n",
284                 sp->ssl->last_error,
285                 gnutls_error_is_fatal(tls_error),
286                 gnutls_strerror(tls_error)));
287     if (tls_error == GNUTLS_E_AGAIN || tls_error == GNUTLS_E_INTERRUPTED)
288     {
289         int dir = gnutls_record_get_direction(sp->ssl->gnutls_state);
290         TRC(fprintf(stderr, " -> incomplete dir=%d\n", dir));
291         h->io_pending = dir ? CS_WANT_WRITE : CS_WANT_READ;
292         return 1;
293     }
294 #endif
295     h->cerrno = CSERRORSSL;
296     return 0;
297 }
298 #endif
299
300 #if HAVE_GETADDRINFO
301 /* resolve using getaddrinfo */
302 struct addrinfo *tcpip_getaddrinfo(const char *str, const char *port)
303 {
304     struct addrinfo hints, *res;
305     int error;
306     char host[512], *p;
307
308     hints.ai_flags = 0;
309     hints.ai_family = AF_UNSPEC;
310     hints.ai_socktype = SOCK_STREAM;
311     hints.ai_protocol = 0;
312     hints.ai_addrlen        = 0;
313     hints.ai_addr           = NULL;
314     hints.ai_canonname      = NULL;
315     hints.ai_next           = NULL;
316
317     strncpy(host, str, sizeof(host)-1);
318     host[sizeof(host)-1] = 0;
319     if ((p = strchr(host, '/')))
320         *p = 0;
321     if ((p = strrchr(host, ':')))
322     {
323         *p = '\0';
324         port = p+1;
325     }
326
327     if (!strcmp("@", host))
328     {
329         hints.ai_flags = AI_PASSIVE;
330         error = getaddrinfo(0, port, &hints, &res);
331     }
332     else
333     {
334         error = getaddrinfo(host, port, &hints, &res);
335     }
336     if (error)
337         return 0;
338     return res;
339 }
340
341 #endif
342 /* gethostbyname .. old systems */
343 int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add,
344                        int default_port)
345 {
346     struct hostent *hp;
347     char *p, buf[512];
348     short int port = default_port;
349 #ifdef WIN32
350     unsigned long tmpadd;
351 #else
352     in_addr_t tmpadd;
353 #endif
354     TRC(fprintf(stderr, "tcpip_strtoaddress: %s\n", str ? str : "NULL"));
355     add->sin_family = AF_INET;
356     strncpy(buf, str, sizeof(buf)-1);
357     buf[sizeof(buf)-1] = 0;
358     if ((p = strchr(buf, '/')))
359         *p = 0;
360     if ((p = strrchr(buf, ':')))
361     {
362         *p = 0;
363         port = atoi(p + 1);
364     }
365     add->sin_port = htons(port);
366     if (!strcmp("@", buf))
367     {
368         add->sin_addr.s_addr = INADDR_ANY;
369     }
370     else if ((tmpadd = inet_addr(buf)) != -1)
371     {
372         memcpy(&add->sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
373     }
374     else if ((hp = gethostbyname(buf)))
375     {
376         memcpy(&add->sin_addr.s_addr, *hp->h_addr_list,
377                sizeof(struct in_addr));
378     }
379     else
380         return 0;
381     return 1;
382 }
383
384
385 #if HAVE_GETADDRINFO
386 void *tcpip_straddr(COMSTACK h, const char *str)
387 {
388     tcpip_state *sp = (tcpip_state *)h->cprivate;
389     const char *port = "210";
390     if (h->protocol == PROTO_HTTP)
391         port = "80";
392     if (!tcpip_init())
393         return 0;
394
395     if (sp->ai)
396         freeaddrinfo(sp->ai);
397     sp->ai = tcpip_getaddrinfo(str, port);
398     if (sp->ai && h->state == CS_ST_UNBND)
399     {
400         int s = -1;
401         struct addrinfo *ai = sp->ai;
402         for (; ai; ai = ai->ai_next)
403         {
404             s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
405             if (s != -1)
406                 break;
407         }
408         if (s == -1)
409             return 0;
410         h->iofile = s;
411         
412         if (!tcpip_set_blocking(h, h->flags))
413             return 0;
414     }
415     return sp->ai;
416 }
417 #else
418 void *tcpip_straddr(COMSTACK h, const char *str)
419 {
420     tcpip_state *sp = (tcpip_state *)h->cprivate;
421     int port = 210;
422     if (h->protocol == PROTO_HTTP)
423         port = 80;
424
425     if (!tcpip_init())
426         return 0;
427     if (!tcpip_strtoaddr_ex(str, &sp->addr, port))
428         return 0;
429     if (h->state == CS_ST_UNBND)
430     {
431         int s;
432         s = socket(AF_INET, SOCK_STREAM, 0);
433         if (s < 0)
434             return 0;
435         h->iofile = s;
436
437         if (!tcpip_set_blocking(h, h->flags))
438             return 0;
439     }
440     return &sp->addr;
441 }
442 #endif
443
444 int tcpip_more(COMSTACK h)
445 {
446     tcpip_state *sp = (tcpip_state *)h->cprivate;
447     
448     return sp->altlen && (*sp->complete)(sp->altbuf, sp->altlen);
449 }
450
451 /*
452  * connect(2) will block (sometimes) - nothing we can do short of doing
453  * weird things like spawning subprocesses or threading or some weird junk
454  * like that.
455  */
456 int tcpip_connect(COMSTACK h, void *address)
457 {
458 #if HAVE_GETADDRINFO
459     tcpip_state *sp = (tcpip_state *)h->cprivate;
460 #else
461     struct sockaddr_in *add = (struct sockaddr_in *) address;
462 #endif
463     int r;
464 #ifdef __sun__
465     int recbuflen;
466     YAZ_SOCKLEN_T rbufsize = sizeof(recbuflen);
467 #endif
468     TRC(fprintf(stderr, "tcpip_connect\n"));
469     h->io_pending = 0;
470     if (h->state != CS_ST_UNBND)
471     {
472         h->cerrno = CSOUTSTATE;
473         return -1;
474     }
475 #if HAVE_GETADDRINFO
476     if (sp->ai != (struct addrinfo *) address)
477     {
478         h->cerrno = CSOUTSTATE;
479         return -1;
480     }
481 #endif
482 #ifdef __sun__
483     /* On Suns, you must set a bigger Receive Buffer BEFORE a call to connect
484      * This gives the connect a chance to negotiate with the other side
485      * (see 'man tcp') 
486      */
487     if (getsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, &rbufsize ) < 0 )
488     {
489         h->cerrno = CSYSERR;
490         return -1;
491     }
492     TRC(fprintf( stderr, "Current Size of TCP Receive Buffer= %d\n",
493                  recbuflen ));
494     recbuflen *= 10; /* lets be optimistic */
495     if (setsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, rbufsize ) < 0 )
496     {
497         h->cerrno = CSYSERR;
498         return -1;
499     }
500     if (getsockopt(h->iofile, SOL_SOCKET, SO_RCVBUF, (void *)&recbuflen, &rbufsize ) )
501     {
502         h->cerrno = CSYSERR;
503         return -1;
504     }
505     TRC(fprintf(stderr, "New Size of TCP Receive Buffer = %d\n",
506                 recbuflen ));
507 #endif
508
509 #if HAVE_GETADDRINFO
510     r = connect(h->iofile, sp->ai->ai_addr, sp->ai->ai_addrlen);
511     freeaddrinfo(sp->ai);
512     sp->ai = 0;
513 #else
514     r = connect(h->iofile, (struct sockaddr *) add, sizeof(*add));
515 #endif
516     if (r < 0)
517     {
518 #ifdef WIN32
519         if (WSAGetLastError() == WSAEWOULDBLOCK)
520         {
521             h->event = CS_CONNECT;
522             h->state = CS_ST_CONNECTING;
523             h->io_pending = CS_WANT_WRITE;
524             return 1;
525         }
526 #else
527         if (yaz_errno() == EINPROGRESS)
528         {
529             h->event = CS_CONNECT;
530             h->state = CS_ST_CONNECTING;
531             h->io_pending = CS_WANT_WRITE|CS_WANT_READ;
532             return 1;
533         }
534 #endif
535         h->cerrno = CSYSERR;
536         return -1;
537     }
538     h->event = CS_CONNECT;
539     h->state = CS_ST_CONNECTING;
540
541     return tcpip_rcvconnect(h);
542 }
543
544 /*
545  * nop
546  */
547 int tcpip_rcvconnect(COMSTACK h)
548 {
549 #if ENABLE_SSL
550     tcpip_state *sp = (tcpip_state *)h->cprivate;
551 #endif
552     TRC(fprintf(stderr, "tcpip_rcvconnect\n"));
553
554     if (h->state == CS_ST_DATAXFER)
555         return 0;
556     if (h->state != CS_ST_CONNECTING)
557     {
558         h->cerrno = CSOUTSTATE;
559         return -1;
560     }
561 #if ENABLE_SSL
562     if (h->type == ssl_type && !sp->ctx)
563     {
564         SSL_library_init();
565         SSL_load_error_strings();
566
567         sp->ctx = sp->ctx_alloc = SSL_CTX_new(SSLv23_client_method());
568         if (!sp->ctx)
569         {
570             h->cerrno = CSERRORSSL;
571             return -1;
572         }
573     }
574     if (sp->ctx)
575     {
576         int res;
577
578         if (!sp->ssl)
579         {
580             sp->ssl = SSL_new(sp->ctx);
581             SSL_set_fd(sp->ssl, h->iofile);
582         }
583         res = SSL_connect(sp->ssl);
584         if (res <= 0)
585         {
586             if (ssl_check_error(h, sp, res))
587                 return 1;
588             return -1;
589         }
590     }
591 #endif
592     h->event = CS_DATA;
593     h->state = CS_ST_DATAXFER;
594     return 0;
595 }
596
597 #define CERTF "ztest.pem"
598 #define KEYF "ztest.pem"
599
600 static void tcpip_setsockopt(int fd)
601 {
602 #if 0
603     int len = 4096;
604     int set = 1;
605     
606     if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&set, sizeof(int)))
607     {
608         yaz_log(LOG_WARN|LOG_ERRNO, "setsockopt TCP_NODELAY");
609     }
610     if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&len, sizeof(int)))
611     {
612         yaz_log(LOG_WARN|LOG_ERRNO, "setsockopt SNDBUF");
613     }
614     if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, sizeof(int)))
615     {
616         yaz_log(LOG_WARN|LOG_ERRNO, "setsockopt RCVBUF");
617     }
618 #endif
619 }
620
621 static int tcpip_bind(COMSTACK h, void *address, int mode)
622 {
623     int r;
624     tcpip_state *sp = (tcpip_state *)h->cprivate;
625 #if HAVE_GETADDRINFO
626 #else
627     struct sockaddr *addr = (struct sockaddr *)address;
628 #endif
629 #ifdef WIN32
630     BOOL one = 1;
631 #else
632     int one = 1;
633 #endif
634
635 #if HAVE_GETADDRINFO
636     if (sp->ai != (struct addrinfo *) address)
637     {
638         h->cerrno = CSOUTSTATE;
639         return -1;
640     }
641 #endif
642
643 #if ENABLE_SSL
644     if (h->type == ssl_type && !sp->ctx)
645     {
646         SSL_library_init();
647         SSL_load_error_strings();
648
649         sp->ctx = sp->ctx_alloc = SSL_CTX_new(SSLv23_server_method());
650         if (!sp->ctx)
651         {
652             h->cerrno = CSERRORSSL;
653             return -1;
654         }
655     }
656     if (sp->ctx)
657     {
658         if (sp->ctx_alloc)
659         {
660             int res;
661             res = SSL_CTX_use_certificate_file(sp->ctx, sp->cert_fname,
662                                                SSL_FILETYPE_PEM);
663             if (res <= 0)
664             {
665 #if HAVE_OPENSSL_SSL_H
666                 ERR_print_errors_fp(stderr);
667 #else
668                 fprintf(stderr, " SSL_CTX_use_certificate_file %s failed\n",
669                         sp->cert_fname);
670 #endif
671                 exit(2);
672             }
673             res = SSL_CTX_use_PrivateKey_file(sp->ctx, sp->cert_fname,
674                                                SSL_FILETYPE_PEM);
675             if (res <= 0)
676             {
677 #if HAVE_OPENSSL_SSL_H
678                 ERR_print_errors_fp(stderr);
679 #else
680                 fprintf(stderr, " SSL_CTX_use_certificate_file %s failed\n",
681                         sp->cert_fname);
682 #endif
683                 exit(3);
684             }
685 #if HAVE_OPENSSL_SSL_H
686             res = SSL_CTX_check_private_key(sp->ctx);
687             if (res <= 0)
688             {
689                 ERR_print_errors_fp(stderr);
690                 exit(5);
691             }
692 #endif
693         }
694         TRC(fprintf(stderr, "ssl_bind\n"));
695     }
696     else
697     {
698         TRC(fprintf(stderr, "tcpip_bind\n"));
699     }
700 #else
701     TRC(fprintf(stderr, "tcpip_bind\n"));
702 #endif
703 #ifndef WIN32
704     if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) 
705         &one, sizeof(one)) < 0)
706     {
707         h->cerrno = CSYSERR;
708         return -1;
709     }
710 #endif
711     tcpip_setsockopt(h->iofile);
712 #if HAVE_GETADDRINFO
713     r = bind(h->iofile, sp->ai->ai_addr, sp->ai->ai_addrlen);
714     freeaddrinfo(sp->ai);
715     sp->ai = 0;
716 #else
717     r = bind(h->iofile, addr, sizeof(struct sockaddr_in));
718 #endif
719     if (r)
720     {
721         h->cerrno = CSYSERR;
722         return -1;
723     }
724     /* Allow a maximum-sized backlog of waiting-to-connect clients */
725     if (mode == CS_SERVER && listen(h->iofile, SOMAXCONN) < 0)
726     {
727         h->cerrno = CSYSERR;
728         return -1;
729     }
730     h->state = CS_ST_IDLE;
731     h->event = CS_LISTEN;
732     return 0;
733 }
734
735 int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
736                  int (*check_ip)(void *cd, const char *a, int len, int t),
737                  void *cd)
738 {
739     struct sockaddr_in addr;
740     YAZ_SOCKLEN_T len = sizeof(addr);
741
742     TRC(fprintf(stderr, "tcpip_listen pid=%d\n", getpid()));
743     if (h->state != CS_ST_IDLE)
744     {
745         h->cerrno = CSOUTSTATE;
746         return -1;
747     }
748     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
749     if (h->newfd < 0)
750     {
751         if (
752 #ifdef WIN32
753             WSAGetLastError() == WSAEWOULDBLOCK
754 #else
755             yaz_errno() == EWOULDBLOCK 
756 #ifdef EAGAIN
757 #if EAGAIN != EWOULDBLOCK
758             || yaz_errno() == EAGAIN
759 #endif
760 #endif
761 #endif
762             )
763             h->cerrno = CSNODATA;
764         else
765         {
766 #ifdef WIN32
767             shutdown(h->iofile, SD_RECEIVE);
768 #else
769             shutdown(h->iofile, SHUT_RD);
770 #endif
771             listen(h->iofile, SOMAXCONN);
772             h->cerrno = CSYSERR;
773         }
774         return -1;
775     }
776     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_in))
777         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in));
778     else if (addrlen)
779         *addrlen = 0;
780     if (check_ip && (*check_ip)(cd, (const char *) &addr,
781         sizeof(addr), AF_INET))
782     {
783         h->cerrno = CSDENY;
784 #ifdef WIN32
785         closesocket(h->newfd);
786 #else
787         close(h->newfd);
788 #endif
789         h->newfd = -1;
790         return -1;
791     }
792     h->state = CS_ST_INCON;
793     tcpip_setsockopt(h->newfd);
794     return 0;
795 }
796
797 COMSTACK tcpip_accept(COMSTACK h)
798 {
799     COMSTACK cnew;
800     tcpip_state *state, *st = (tcpip_state *)h->cprivate;
801 #ifdef WIN32
802     unsigned long tru = 1;
803 #endif
804
805     TRC(fprintf(stderr, "tcpip_accept\n"));
806     if (h->state == CS_ST_INCON)
807     {
808         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
809         {
810             h->cerrno = CSYSERR;
811 #ifdef WIN32
812             closesocket(h->newfd);
813 #else
814             close(h->newfd);
815 #endif
816             h->newfd = -1;
817             return 0;
818         }
819         memcpy(cnew, h, sizeof(*h));
820         cnew->iofile = h->newfd;
821         cnew->io_pending = 0;
822         if (!(state = (tcpip_state *)
823               (cnew->cprivate = xmalloc(sizeof(tcpip_state)))))
824         {
825             h->cerrno = CSYSERR;
826             if (h->newfd != -1)
827             {
828 #ifdef WIN32
829                 closesocket(h->newfd);
830 #else
831                 close(h->newfd);
832 #endif
833                 h->newfd = -1;
834             }
835             return 0;
836         }
837         if (!tcpip_set_blocking(cnew, cnew->flags))
838         {
839             h->cerrno = CSYSERR;
840             if (h->newfd != -1)
841             {
842 #ifdef WIN32
843                 closesocket(h->newfd);
844 #else
845                 close(h->newfd);
846 #endif
847                 h->newfd = -1;
848             }
849             xfree(cnew);
850             xfree(state);
851             return 0;
852         }
853         h->newfd = -1;
854         state->altbuf = 0;
855         state->altsize = state->altlen = 0;
856         state->towrite = state->written = -1;
857         state->complete = st->complete;
858 #if HAVE_GETADDRINFO
859         state->ai = 0;
860 #endif
861         cnew->state = CS_ST_ACCEPT;
862         h->state = CS_ST_IDLE;
863         
864 #if ENABLE_SSL
865         state->ctx = st->ctx;
866         state->ctx_alloc = 0;
867         state->ssl = st->ssl;
868         if (state->ctx)
869         {
870             state->ssl = SSL_new(state->ctx);
871             SSL_set_fd(state->ssl, cnew->iofile);
872         }
873 #endif
874         state->connect_request_buf = 0;
875         state->connect_response_buf = 0;
876         h = cnew;
877     }
878     if (h->state == CS_ST_ACCEPT)
879     {
880
881 #if ENABLE_SSL
882         tcpip_state *state = (tcpip_state *)h->cprivate;
883         if (state->ctx)
884         {
885             int res;
886             errno = 0;
887             res = SSL_accept(state->ssl);
888             TRC(fprintf(stderr, "SSL_accept res=%d\n", res));
889             if (res <= 0)
890             {
891                 if (ssl_check_error(h, state, res))
892                 {
893                     return h;
894                 }
895                 cs_close(h);
896                 return 0;
897             }
898             TRC(fprintf(stderr, "SSL_accept complete\n"));
899         }
900 #endif
901     }
902     else
903     {
904         h->cerrno = CSOUTSTATE;
905         return 0;
906     }
907     h->io_pending = 0;
908     h->state = CS_ST_DATAXFER;
909     h->event = CS_DATA;
910     return h;
911 }
912
913 #define CS_TCPIP_BUFCHUNK 4096
914
915 /*
916  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
917  * 0=connection closed.
918  */
919 int tcpip_get(COMSTACK h, char **buf, int *bufsize)
920 {
921     tcpip_state *sp = (tcpip_state *)h->cprivate;
922     char *tmpc;
923     int tmpi, berlen, rest, req, tomove;
924     int hasread = 0, res;
925
926     TRC(fprintf(stderr, "tcpip_get: bufsize=%d\n", *bufsize));
927     if (sp->altlen) /* switch buffers */
928     {
929         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
930             (unsigned) sp->altbuf));
931         tmpc = *buf;
932         tmpi = *bufsize;
933         *buf = sp->altbuf;
934         *bufsize = sp->altsize;
935         hasread = sp->altlen;
936         sp->altlen = 0;
937         sp->altbuf = tmpc;
938         sp->altsize = tmpi;
939     }
940     h->io_pending = 0;
941     while (!(berlen = (*sp->complete)(*buf, hasread)))
942     {
943         if (!*bufsize)
944         {
945             if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
946             {
947                 h->cerrno = CSYSERR;
948                 return -1;
949             }
950         }
951         else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
952             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
953             {
954                 h->cerrno = CSYSERR;
955                 return -1;
956             }
957 #ifdef __sun__
958         yaz_set_errno( 0 );
959         /* unfortunatly, sun sometimes forgets to set errno in recv
960            when EWOULDBLOCK etc. would be required (res = -1) */
961 #endif
962         res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0);
963         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
964         if (res < 0)
965         {
966             TRC(fprintf(stderr, "  recv errno=%d, (%s)\n", yaz_errno(), 
967                       strerror(yaz_errno())));
968 #ifdef WIN32
969             if (WSAGetLastError() == WSAEWOULDBLOCK)
970             {
971                 h->io_pending = CS_WANT_READ;
972                 break;
973             }
974             else
975             {
976                 h->cerrno = CSYSERR;
977                 return -1;
978             }
979 #else
980             if (yaz_errno() == EWOULDBLOCK 
981 #ifdef EAGAIN   
982 #if EAGAIN != EWOULDBLOCK
983                 || yaz_errno() == EAGAIN
984 #endif
985 #endif
986                 || yaz_errno() == EINPROGRESS
987 #ifdef __sun__
988                 || yaz_errno() == ENOENT /* Sun's sometimes set errno to this */
989 #endif
990                 )
991             {
992                 h->io_pending = CS_WANT_READ;
993                 break;
994             }
995             else if (yaz_errno() == 0)
996                 continue;
997             else
998             {
999                 h->cerrno = CSYSERR;
1000                 return -1;
1001             }
1002 #endif
1003         }
1004         else if (!res)
1005             return hasread;
1006         hasread += res;
1007         if (hasread > h->max_recv_bytes)
1008         {
1009             h->cerrno = CSBUFSIZE;
1010             return -1;
1011         }
1012     }
1013     TRC(fprintf(stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
1014                 hasread, berlen));
1015     /* move surplus buffer (or everything if we didn't get a BER rec.) */
1016     if (hasread > berlen)
1017     {
1018         tomove = req = hasread - berlen;
1019         rest = tomove % CS_TCPIP_BUFCHUNK;
1020         if (rest)
1021             req += CS_TCPIP_BUFCHUNK - rest;
1022         if (!sp->altbuf)
1023         {
1024             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
1025             {
1026                 h->cerrno = CSYSERR;
1027                 return -1;
1028             }
1029         } else if (sp->altsize < req)
1030             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
1031             {
1032                 h->cerrno = CSYSERR;
1033                 return -1;
1034             }
1035         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
1036             (unsigned) sp->altbuf));
1037         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
1038     }
1039     if (berlen < CS_TCPIP_BUFCHUNK - 1)
1040         *(*buf + berlen) = '\0';
1041     return berlen ? berlen : 1;
1042 }
1043
1044
1045 #if ENABLE_SSL
1046 /*
1047  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
1048  * 0=connection closed.
1049  */
1050 int ssl_get(COMSTACK h, char **buf, int *bufsize)
1051 {
1052     tcpip_state *sp = (tcpip_state *)h->cprivate;
1053     char *tmpc;
1054     int tmpi, berlen, rest, req, tomove;
1055     int hasread = 0, res;
1056
1057     TRC(fprintf(stderr, "ssl_get: bufsize=%d\n", *bufsize));
1058     if (sp->altlen) /* switch buffers */
1059     {
1060         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
1061             (unsigned) sp->altbuf));
1062         tmpc = *buf;
1063         tmpi = *bufsize;
1064         *buf = sp->altbuf;
1065         *bufsize = sp->altsize;
1066         hasread = sp->altlen;
1067         sp->altlen = 0;
1068         sp->altbuf = tmpc;
1069         sp->altsize = tmpi;
1070     }
1071     h->io_pending = 0;
1072     while (!(berlen = (*sp->complete)(*buf, hasread)))
1073     {
1074         if (!*bufsize)
1075         {
1076             if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
1077                 return -1;
1078         }
1079         else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
1080             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
1081                 return -1;
1082         res = SSL_read(sp->ssl, *buf + hasread, CS_TCPIP_BUFCHUNK);
1083         TRC(fprintf(stderr, "  SSL_read res=%d, hasread=%d\n", res, hasread));
1084         if (res <= 0)
1085         {
1086             if (ssl_check_error(h, sp, res))
1087                 break;
1088             return -1;
1089         }
1090         hasread += res;
1091     }
1092     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
1093         hasread, berlen));
1094     /* move surplus buffer (or everything if we didn't get a BER rec.) */
1095     if (hasread > berlen)
1096     {
1097         tomove = req = hasread - berlen;
1098         rest = tomove % CS_TCPIP_BUFCHUNK;
1099         if (rest)
1100             req += CS_TCPIP_BUFCHUNK - rest;
1101         if (!sp->altbuf)
1102         {
1103             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
1104                 return -1;
1105         } else if (sp->altsize < req)
1106             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
1107                 return -1;
1108         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
1109             (unsigned) sp->altbuf));
1110         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
1111     }
1112     if (berlen < CS_TCPIP_BUFCHUNK - 1)
1113         *(*buf + berlen) = '\0';
1114     return berlen ? berlen : 1;
1115 }
1116 #endif
1117
1118 /*
1119  * Returns 1, 0 or -1
1120  * In nonblocking mode, you must call again with same buffer while
1121  * return value is 1.
1122  */
1123 int tcpip_put(COMSTACK h, char *buf, int size)
1124 {
1125     int res;
1126     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1127
1128     TRC(fprintf(stderr, "tcpip_put: size=%d\n", size));
1129     h->io_pending = 0;
1130     h->event = CS_DATA;
1131     if (state->towrite < 0)
1132     {
1133         state->towrite = size;
1134         state->written = 0;
1135     }
1136     else if (state->towrite != size)
1137     {
1138         h->cerrno = CSWRONGBUF;
1139         return -1;
1140     }
1141     while (state->towrite > state->written)
1142     {
1143         if ((res =
1144              send(h->iofile, buf + state->written, size -
1145                   state->written, 
1146 #ifdef MSG_NOSIGNAL
1147                   MSG_NOSIGNAL
1148 #else
1149                   0
1150 #endif
1151                  )) < 0)
1152         {
1153             if (
1154 #ifdef WIN32
1155                 WSAGetLastError() == WSAEWOULDBLOCK
1156 #else
1157                 yaz_errno() == EWOULDBLOCK 
1158 #ifdef EAGAIN
1159 #if EAGAIN != EWOULDBLOCK
1160              || yaz_errno() == EAGAIN
1161 #endif
1162 #endif
1163 #ifdef __sun__
1164                 || yaz_errno() == ENOENT /* Sun's sometimes set errno to this value! */
1165 #endif
1166                 || yaz_errno() == EINPROGRESS
1167 #endif
1168                 )
1169             {
1170                 TRC(fprintf(stderr, "  Flow control stop\n"));
1171                 h->io_pending = CS_WANT_WRITE;
1172                 return 1;
1173             }
1174             h->cerrno = CSYSERR;
1175             return -1;
1176         }
1177         state->written += res;
1178         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
1179                     res, state->written, size));
1180     }
1181     state->towrite = state->written = -1;
1182     TRC(fprintf(stderr, "  Ok\n"));
1183     return 0;
1184 }
1185
1186
1187 #if ENABLE_SSL
1188 /*
1189  * Returns 1, 0 or -1
1190  * In nonblocking mode, you must call again with same buffer while
1191  * return value is 1.
1192  */
1193 int ssl_put(COMSTACK h, char *buf, int size)
1194 {
1195     int res;
1196     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1197
1198     TRC(fprintf(stderr, "ssl_put: size=%d\n", size));
1199     h->io_pending = 0;
1200     h->event = CS_DATA;
1201     if (state->towrite < 0)
1202     {
1203         state->towrite = size;
1204         state->written = 0;
1205     }
1206     else if (state->towrite != size)
1207     {
1208         h->cerrno = CSWRONGBUF;
1209         return -1;
1210     }
1211     while (state->towrite > state->written)
1212     {
1213         res = SSL_write(state->ssl, buf + state->written, 
1214                         size - state->written);
1215         if (res <= 0)
1216         {
1217             if (ssl_check_error(h, state, res))
1218                 return 1;
1219             return -1;
1220         }
1221         state->written += res;
1222         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
1223                     res, state->written, size));
1224     }
1225     state->towrite = state->written = -1;
1226     TRC(fprintf(stderr, "  Ok\n"));
1227     return 0;
1228 }
1229 #endif
1230
1231 int tcpip_close(COMSTACK h)
1232 {
1233     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1234
1235     TRC(fprintf(stderr, "tcpip_close\n"));
1236     if (h->iofile != -1)
1237     {
1238 #if ENABLE_SSL
1239         if (sp->ssl)
1240         {
1241             SSL_shutdown(sp->ssl);
1242         }
1243 #endif
1244 #ifdef WIN32
1245         closesocket(h->iofile);
1246 #else
1247         close(h->iofile);
1248 #endif
1249     }
1250     if (sp->altbuf)
1251         xfree(sp->altbuf);
1252 #if ENABLE_SSL
1253     if (sp->ssl)
1254     {
1255         TRC(fprintf(stderr, "SSL_free\n"));
1256         SSL_free(sp->ssl);
1257     }
1258     sp->ssl = 0;
1259     if (sp->ctx_alloc)
1260         SSL_CTX_free(sp->ctx_alloc);
1261 #endif
1262 #if HAVE_GETADDRINFO
1263     if (sp->ai)
1264         freeaddrinfo(sp->ai);
1265 #endif
1266     xfree(sp->connect_request_buf);
1267     xfree(sp->connect_response_buf);
1268     xfree(sp);
1269     xfree(h);
1270     return 0;
1271 }
1272
1273 char *tcpip_addrstr(COMSTACK h)
1274 {
1275     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
1276     char *r = 0, *buf = sp->buf;
1277
1278 #if HAVE_GETADDRINFO
1279     char host[120];
1280     struct sockaddr_storage addr;
1281     YAZ_SOCKLEN_T len = sizeof(addr);
1282     
1283     if (getpeername(h->iofile, (struct sockaddr *)&addr, &len) < 0)
1284     {
1285         h->cerrno = CSYSERR;
1286         return 0;
1287     }
1288     if (getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host)-1, 
1289                     0, 0, 
1290                     (h->flags & CS_FLAGS_NUMERICHOST) ? NI_NUMERICHOST : 0))
1291     {
1292         r = "unknown";
1293     }
1294     else
1295         r = host;
1296     
1297 #else
1298
1299     struct sockaddr_in addr;
1300     YAZ_SOCKLEN_T len = sizeof(addr);
1301     struct hostent *host;
1302     
1303     if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0)
1304     {
1305         h->cerrno = CSYSERR;
1306         return 0;
1307     }
1308     if (!(h->flags & CS_FLAGS_NUMERICHOST))
1309     {
1310         if ((host = gethostbyaddr((char*)&addr.sin_addr,
1311                                   sizeof(addr.sin_addr),
1312                                   AF_INET)))
1313             r = (char*) host->h_name;
1314     }
1315     if (!r)
1316         r = inet_ntoa(addr.sin_addr);        
1317 #endif
1318
1319     if (h->protocol == PROTO_HTTP)
1320         sprintf(buf, "http:%s", r);
1321     else
1322         sprintf(buf, "tcp:%s", r);
1323 #if ENABLE_SSL
1324     if (sp->ctx)
1325     {
1326         if (h->protocol == PROTO_HTTP)
1327             sprintf(buf, "https:%s", r);
1328         else
1329             sprintf(buf, "ssl:%s", r);
1330     }
1331 #endif
1332     return buf;
1333 }
1334
1335 int static tcpip_set_blocking(COMSTACK p, int flags)
1336 {
1337     unsigned long flag;
1338     
1339 #ifdef WIN32
1340     flag = (flags & CS_FLAGS_BLOCKING) ? 0 : 1;
1341     if (ioctlsocket(p->iofile, FIONBIO, &flag) < 0)
1342         return 0;
1343 #else
1344     flag = fcntl(p->iofile, F_GETFL, 0);
1345     if (flags & CS_FLAGS_BLOCKING)
1346         flag = flag & ~O_NONBLOCK;  /* blocking */
1347     else
1348     {
1349         flag = flag | O_NONBLOCK;   /* non-blocking */
1350         signal(SIGPIPE, SIG_IGN);
1351     }
1352     if (fcntl(p->iofile, F_SETFL, flag) < 0)
1353         return 0;
1354 #endif
1355     p->flags = flags;
1356     return 1;
1357 }
1358
1359 #if ENABLE_SSL
1360 int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
1361 {
1362     struct tcpip_state *sp;
1363     if (!cs || cs->type != ssl_type)
1364         return 0;
1365     sp = (struct tcpip_state *) cs->cprivate;
1366     if (sp->ctx_alloc)
1367         return 0;
1368     sp->ctx = (SSL_CTX *) ctx;
1369     return 1;
1370 }
1371
1372 void *cs_get_ssl(COMSTACK cs)
1373 {
1374     struct tcpip_state *sp;
1375     if (!cs || cs->type != ssl_type)
1376         return 0;
1377     sp = (struct tcpip_state *) cs->cprivate;
1378     return sp->ssl;  
1379 }
1380
1381 int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
1382 {
1383     struct tcpip_state *sp;
1384     if (!cs || cs->type != ssl_type)
1385         return 0;
1386     sp = (struct tcpip_state *) cs->cprivate;
1387     strncpy(sp->cert_fname, fname, sizeof(sp->cert_fname)-1);
1388     sp->cert_fname[sizeof(sp->cert_fname)-1] = '\0';
1389     return 1;
1390 }
1391
1392 int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
1393 {
1394     SSL *ssl = (SSL *) cs_get_ssl(cs);
1395     if (ssl)
1396     {
1397 #if HAVE_OPENSSL_SSL_H
1398         X509 *server_cert = SSL_get_peer_certificate(ssl);
1399         if (server_cert)
1400         {
1401             BIO *bio = BIO_new(BIO_s_mem());
1402             char *pem_buf;
1403             /* get PEM buffer in memory */
1404             PEM_write_bio_X509(bio, server_cert);
1405             *len = BIO_get_mem_data(bio, &pem_buf);
1406             *buf = (char *) xmalloc(*len);
1407             memcpy(*buf, pem_buf, *len);
1408             BIO_free(bio);
1409             return 1;
1410         }
1411 #endif
1412     }
1413     return 0;
1414 }
1415 #else
1416 int cs_set_ssl_ctx(COMSTACK cs, void *ctx)
1417 {
1418     return 0;
1419 }
1420
1421 void *cs_get_ssl(COMSTACK cs)
1422 {
1423     return 0;
1424 }
1425
1426 int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
1427 {
1428     return 0;
1429 }
1430
1431 int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
1432 {
1433     return 0;
1434 }
1435 #endif
1436
1437
1438 static int tcpip_put_connect(COMSTACK h, char *buf, int size)
1439 {
1440     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1441
1442     int r = tcpip_put(h, state->connect_request_buf,
1443                       state->connect_request_len);
1444     if (r == 0)
1445     {
1446         /* it's sent */
1447         h->f_put = tcpip_put; /* switch to normal tcpip put */
1448         r = tcpip_put(h, buf, size);
1449     }
1450     return r;
1451 }
1452
1453 static int tcpip_get_connect(COMSTACK h, char **buf, int *bufsize)
1454 {
1455     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
1456     int r;
1457
1458     r = tcpip_get(h, &state->connect_response_buf, 
1459                   &state->connect_response_len);
1460     if (r < 1)
1461         return r;
1462     /* got the connect response completely */
1463     state->complete = cs_complete_auto; /* switch to normal tcpip get */
1464     h->f_get = tcpip_get;
1465     return tcpip_get(h, buf, bufsize);
1466 }
1467
1468
1469 /*
1470  * Local variables:
1471  * c-basic-offset: 4
1472  * indent-tabs-mode: nil
1473  * End:
1474  * vim: shiftwidth=4 tabstop=8 expandtab
1475  */
1476