Check for socklen_t type for accept, getpeername
[yaz-moved-to-github.git] / comstack / unix.c
1 /*
2  * Copyright (c) 1995-2002, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: unix.c,v 1.5 2002-09-10 20:56:34 adam Exp $
6  * UNIX socket COMSTACK. By Morten Bøgeskov.
7  */
8 #ifndef WIN32
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <signal.h>
17
18 #include <sys/socket.h>
19 #include <sys/stat.h>
20 #include <sys/un.h>
21
22 #include <yaz/comstack.h>
23 #include <yaz/unix.h>
24 #include <yaz/log.h>
25
26 /* Chas added the following, so we get the definition of completeBER */
27 #include <yaz/odr.h>
28
29 #if HAVE_SOCKLEN_T
30 #define NET_LEN_T socklen_t
31 #else
32 #if GETPEERNAME_ACCEPTS_SIZE_T_FOR_THIRD_ARGUMENT
33 #define NET_LEN_T size_t
34 #else
35 #define NET_LEN_T int
36 #endif
37 #endif
38
39 int unix_close(COMSTACK h);
40 int unix_put(COMSTACK h, char *buf, int size);
41 int unix_get(COMSTACK h, char **buf, int *bufsize);
42 int unix_connect(COMSTACK h, void *address);
43 int unix_more(COMSTACK h);
44 int unix_rcvconnect(COMSTACK h);
45 int unix_bind(COMSTACK h, void *address, int mode);
46 int unix_listen(COMSTACK h, char *raddr, int *addrlen,
47                 int (*check_ip)(void *cd, const char *a, int len, int type),
48                 void *cd);
49 int static unix_set_blocking(COMSTACK p, int blocking);
50
51
52 COMSTACK unix_accept(COMSTACK h);
53 char *unix_addrstr(COMSTACK h);
54 void *unix_straddr(COMSTACK h, const char *str);
55
56 #ifndef SUN_LEN
57 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
58                       + strlen ((ptr)->sun_path))
59 #endif
60 #if 0
61 #define TRC(x) x
62 #else
63 #define TRC(X)
64 #endif
65
66 /* this state is used for both SSL and straight TCP/IP */
67 typedef struct unix_state
68 {
69     char *altbuf; /* alternate buffer for surplus data */
70     int altsize;  /* size as xmalloced */
71     int altlen;   /* length of data or 0 if none */
72
73     int written;  /* -1 if we aren't writing */
74     int towrite;  /* to verify against user input */
75     int (*complete)(const unsigned char *buf, int len); /* length/comple. */
76     struct sockaddr_un addr;  /* returned by cs_straddr */
77     char buf[128]; /* returned by cs_addrstr */
78 } unix_state;
79
80 static int unix_init (void)
81 {
82     return 1;
83 }
84
85 /*
86  * This function is always called through the cs_create() macro.
87  * s >= 0: socket has already been established for us.
88  */
89 COMSTACK unix_type(int s, int blocking, int protocol, void *vp)
90 {
91     COMSTACK p;
92     unix_state *state;
93     int new_socket;
94
95     if (!unix_init ())
96         return 0;
97     if (s < 0)
98     {
99         if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
100             return 0;
101         new_socket = 1;
102     }
103     else
104         new_socket = 0;
105     if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
106         return 0;
107     if (!(state = (struct unix_state *)(p->cprivate =
108                                         xmalloc(sizeof(unix_state)))))
109         return 0;
110
111     if (!(p->blocking = blocking))
112     {
113         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
114             return 0;
115 #ifndef MSG_NOSIGNAL
116         signal (SIGPIPE, SIG_IGN);
117 #endif
118     }
119
120     p->io_pending = 0;
121     p->iofile = s;
122     p->type = unix_type;
123     p->protocol = (enum oid_proto) protocol;
124
125     p->f_connect = unix_connect;
126     p->f_rcvconnect = unix_rcvconnect;
127     p->f_get = unix_get;
128     p->f_put = unix_put;
129     p->f_close = unix_close;
130     p->f_more = unix_more;
131     p->f_bind = unix_bind;
132     p->f_listen = unix_listen;
133     p->f_accept = unix_accept;
134     p->f_addrstr = unix_addrstr;
135     p->f_straddr = unix_straddr;
136     p->f_set_blocking = unix_set_blocking;
137
138     p->state = new_socket ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
139     p->event = CS_NONE;
140     p->cerrno = 0;
141     p->stackerr = 0;
142
143     state->altbuf = 0;
144     state->altsize = state->altlen = 0;
145     state->towrite = state->written = -1;
146     if (protocol == PROTO_WAIS)
147         state->complete = completeWAIS;
148     else
149         state->complete = completeBER;
150
151     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
152     TRC(fprintf(stderr, "Created new UNIX comstack\n"));
153
154     return p;
155 }
156
157
158 int unix_strtoaddr_ex(const char *str, struct sockaddr_un *add)
159 {
160     char *cp;
161     if (!unix_init ())
162         return 0;
163     TRC(fprintf(stderr, "unix_strtoaddress: %s\n", str ? str : "NULL"));
164     add->sun_family = AF_UNIX;
165     strncpy(add->sun_path, str, sizeof(add->sun_path));
166     cp = strchr (add->sun_path, ':');
167     if (cp)
168         *cp = '\0';
169     return 1;
170 }
171
172 void *unix_straddr(COMSTACK h, const char *str)
173 {
174     unix_state *sp = (unix_state *)h->cprivate;
175
176     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
177
178     if (!unix_strtoaddr_ex (str, &sp->addr))
179         return 0;
180     return &sp->addr;
181 }
182
183 struct sockaddr_un *unix_strtoaddr(const char *str)
184 {
185     static struct sockaddr_un add;
186
187     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
188
189     if (!unix_strtoaddr_ex (str, &add))
190         return 0;
191     return &add;
192 }
193
194 int unix_more(COMSTACK h)
195 {
196     unix_state *sp = (unix_state *)h->cprivate;
197
198     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
199                                          sp->altlen);
200 }
201
202 /*
203  * connect(2) will block (sometimes) - nothing we can do short of doing
204  * weird things like spawning subprocesses or threading or some weird junk
205  * like that.
206  */
207 int unix_connect(COMSTACK h, void *address)
208 {
209     struct sockaddr_un *add = (struct sockaddr_un *)address;
210     int r;
211
212     TRC(fprintf(stderr, "unix_connect\n"));
213     h->io_pending = 0;
214     if (h->state != CS_ST_UNBND)
215     {
216         h->cerrno = CSOUTSTATE;
217         return -1;
218     }
219     r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add));
220     if (r < 0)
221     {
222         if (errno == EINPROGRESS)
223         {
224             h->event = CS_CONNECT;
225             h->state = CS_ST_CONNECTING;
226             h->io_pending = CS_WANT_WRITE|CS_WANT_READ;
227             return 1;
228         }
229         h->cerrno = CSYSERR;
230         return -1;
231     }
232     h->event = CS_CONNECT;
233     h->state = CS_ST_CONNECTING;
234
235     return unix_rcvconnect (h);
236 }
237
238 /*
239  * nop
240  */
241 int unix_rcvconnect(COMSTACK h)
242 {
243     TRC(fprintf(stderr, "unix_rcvconnect\n"));
244
245     if (h->state == CS_ST_DATAXFER)
246         return 0;
247     if (h->state != CS_ST_CONNECTING)
248     {
249         h->cerrno = CSOUTSTATE;
250         return -1;
251     }
252     h->event = CS_DATA;
253     h->state = CS_ST_DATAXFER;
254     return 0;
255 }
256
257 #define CERTF "ztest.pem"
258 #define KEYF "ztest.pem"
259
260 int unix_bind(COMSTACK h, void *address, int mode)
261 {
262     struct sockaddr *addr = (struct sockaddr *)address;
263     const char * path = ((struct sockaddr_un *)addr)->sun_path;
264     struct stat stat_buf;
265
266     TRC (fprintf (stderr, "unix_bind\n"));
267
268     if(stat(path, &stat_buf) != -1) {
269         struct sockaddr_un socket_unix;
270         int socket_out = -1;
271         if(! S_ISSOCK(stat_buf.st_mode)) {
272             h->cerrno = CSYSERR;
273             errno = EEXIST; /* Not a socket (File exists) */
274             return -1;
275         }
276         if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
277             h->cerrno = CSYSERR;
278             return -1;
279         }
280         socket_unix.sun_family = AF_UNIX;
281         strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path));
282         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
283             if(errno == ECONNREFUSED) {
284                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
285             } else {
286                 h->cerrno = CSYSERR;
287                 return -1;
288             }
289         } else {
290             close(socket_out);
291             h->cerrno = CSYSERR;
292             errno = EADDRINUSE;
293             return -1;
294         }
295         unlink(path);
296     }
297
298     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
299     {
300         h->cerrno = CSYSERR;
301         return -1;
302     }
303     if (mode == CS_SERVER && listen(h->iofile, 3) < 0)
304     {
305         h->cerrno = CSYSERR;
306         return -1;
307     }
308     h->state = CS_ST_IDLE;
309     h->event = CS_LISTEN;
310     return 0;
311 }
312
313 int unix_listen(COMSTACK h, char *raddr, int *addrlen,
314                 int (*check_ip)(void *cd, const char *a, int len, int t),
315                 void *cd)
316 {
317     struct sockaddr_un addr;
318     NET_LEN_T len = SUN_LEN(&addr);
319
320     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
321     if (h->state != CS_ST_IDLE)
322     {
323         h->cerrno = CSOUTSTATE;
324         return -1;
325     }
326     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
327     if (h->newfd < 0)
328     {
329         if (
330             errno == EWOULDBLOCK
331 #ifdef EAGAIN
332 #if EAGAIN != EWOULDBLOCK
333             || errno == EAGAIN
334 #endif
335 #endif
336             )
337             h->cerrno = CSNODATA;
338         else
339             h->cerrno = CSYSERR;
340         return -1;
341     }
342     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
343         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
344     else if (addrlen)
345         *addrlen = 0;
346     h->state = CS_ST_INCON;
347     return 0;
348 }
349
350 COMSTACK unix_accept(COMSTACK h)
351 {
352     COMSTACK cnew;
353     unix_state *state, *st = (unix_state *)h->cprivate;
354
355     TRC(fprintf(stderr, "unix_accept\n"));
356     if (h->state == CS_ST_INCON)
357     {
358         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
359         {
360             h->cerrno = CSYSERR;
361             close(h->newfd);
362             h->newfd = -1;
363             return 0;
364         }
365         memcpy(cnew, h, sizeof(*h));
366         cnew->iofile = h->newfd;
367         cnew->io_pending = 0;
368         if (!(state = (unix_state *)
369               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
370         {
371             h->cerrno = CSYSERR;
372             if (h->newfd != -1)
373             {
374                 close(h->newfd);
375                 h->newfd = -1;
376             }
377             return 0;
378         }
379         if (!cnew->blocking &&
380             (!cnew->blocking && fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
381             )
382         {
383             h->cerrno = CSYSERR;
384             if (h->newfd != -1)
385             {
386                 close(h->newfd);
387                 h->newfd = -1;
388             }
389             xfree (cnew);
390             xfree (state);
391             return 0;
392         }
393         h->newfd = -1;
394         state->altbuf = 0;
395         state->altsize = state->altlen = 0;
396         state->towrite = state->written = -1;
397         state->complete = st->complete;
398         cnew->state = CS_ST_ACCEPT;
399         cnew->event = CS_NONE;
400         h->state = CS_ST_IDLE;
401
402         h = cnew;
403     }
404     if (h->state == CS_ST_ACCEPT)
405     {
406     }
407     else
408     {
409         h->cerrno = CSOUTSTATE;
410         return 0;
411     }
412     h->io_pending = 0;
413     h->state = CS_ST_DATAXFER;
414     h->event = CS_DATA;
415     return h;
416 }
417
418 #define CS_UNIX_BUFCHUNK 4096
419
420 /*
421  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
422  * 0=connection closed.
423  */
424 int unix_get(COMSTACK h, char **buf, int *bufsize)
425 {
426     unix_state *sp = (unix_state *)h->cprivate;
427     char *tmpc;
428     int tmpi, berlen, rest, req, tomove;
429     int hasread = 0, res;
430
431     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
432     if (sp->altlen) /* switch buffers */
433     {
434         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
435                     (unsigned) sp->altbuf));
436         tmpc = *buf;
437         tmpi = *bufsize;
438         *buf = sp->altbuf;
439         *bufsize = sp->altsize;
440         hasread = sp->altlen;
441         sp->altlen = 0;
442         sp->altbuf = tmpc;
443         sp->altsize = tmpi;
444     }
445     h->io_pending = 0;
446     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
447     {
448         if (!*bufsize)
449         {
450             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
451                 return -1;
452         }
453         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
454             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
455                 return -1;
456         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
457         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
458         if (res < 0)
459         {
460             if (errno == EWOULDBLOCK
461 #ifdef EAGAIN
462 #if EAGAIN != EWOULDBLOCK
463                 || errno == EAGAIN
464 #endif
465 #endif
466                 || errno == EINPROGRESS
467                 )
468             {
469                 h->io_pending = CS_WANT_READ;
470                 break;
471             }
472             else if (errno == 0)
473                 continue;
474             else
475                 return -1;
476         }
477         else if (!res)
478             return 0;
479         hasread += res;
480     }
481     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
482                   hasread, berlen));
483     /* move surplus buffer (or everything if we didn't get a BER rec.) */
484     if (hasread > berlen)
485     {
486         tomove = req = hasread - berlen;
487         rest = tomove % CS_UNIX_BUFCHUNK;
488         if (rest)
489             req += CS_UNIX_BUFCHUNK - rest;
490         if (!sp->altbuf)
491         {
492             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
493                 return -1;
494         } else if (sp->altsize < req)
495             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
496                 return -1;
497         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
498                     (unsigned) sp->altbuf));
499         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
500     }
501     if (berlen < CS_UNIX_BUFCHUNK - 1)
502         *(*buf + berlen) = '\0';
503     return berlen ? berlen : 1;
504 }
505
506
507
508 /*
509  * Returns 1, 0 or -1
510  * In nonblocking mode, you must call again with same buffer while
511  * return value is 1.
512  */
513 int unix_put(COMSTACK h, char *buf, int size)
514 {
515     int res;
516     struct unix_state *state = (struct unix_state *)h->cprivate;
517
518     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
519     h->io_pending = 0;
520     h->event = CS_DATA;
521     if (state->towrite < 0)
522     {
523         state->towrite = size;
524         state->written = 0;
525     }
526     else if (state->towrite != size)
527     {
528         h->cerrno = CSWRONGBUF;
529         return -1;
530     }
531     while (state->towrite > state->written)
532     {
533         if ((res =
534              send(h->iofile, buf + state->written, size -
535                   state->written,
536 #ifdef MSG_NOSIGNAL
537                   MSG_NOSIGNAL
538 #else
539                   0
540 #endif
541                  )) < 0)
542         {
543             if (
544                 errno == EWOULDBLOCK
545 #ifdef EAGAIN
546 #if EAGAIN != EWOULDBLOCK
547                 || errno == EAGAIN
548 #endif
549 #endif
550                 )
551             {
552                 TRC(fprintf(stderr, "  Flow control stop\n"));
553                 h->io_pending = CS_WANT_WRITE;
554                 return 1;
555             }
556             h->cerrno = CSYSERR;
557             return -1;
558         }
559         state->written += res;
560         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
561                     res, state->written, size));
562     }
563     state->towrite = state->written = -1;
564     TRC(fprintf(stderr, "  Ok\n"));
565     return 0;
566 }
567
568
569
570 int unix_close(COMSTACK h)
571 {
572     unix_state *sp = (struct unix_state *)h->cprivate;
573
574     TRC(fprintf(stderr, "unix_close\n"));
575     if (h->iofile != -1)
576     {
577         close(h->iofile);
578     }
579     if (sp->altbuf)
580         xfree(sp->altbuf);
581     xfree(sp);
582     xfree(h);
583     return 0;
584 }
585
586 char *unix_addrstr(COMSTACK h)
587 {
588     unix_state *sp = (struct unix_state *)h->cprivate;
589     char *buf = sp->buf;
590     sprintf(buf, "unix:%s", sp->addr.sun_path);
591     return buf;
592 }
593
594 int static unix_set_blocking(COMSTACK p, int blocking)
595 {
596     unsigned long flag;
597
598     if (p->blocking == blocking)
599         return 1;
600     flag = fcntl(p->iofile, F_GETFL, 0);
601     if(!blocking)
602         flag = flag & ~O_NONBLOCK;
603     else
604         flag = flag | O_NONBLOCK;
605     if (fcntl(p->iofile, F_SETFL, flag) < 0)
606         return 0;
607     p->blocking = blocking;
608     return 1;
609 }
610 #endif