Properly null-terminate Unix-domain socket path. Thanks to Marc Espie
[yaz-moved-to-github.git] / src / unix.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: unix.c,v 1.19 2007-06-05 05:42:07 adam Exp $
6  * UNIX socket COMSTACK. By Morten Bøgeskov.
7  */
8 /**
9  * \file unix.c
10  * \brief Implements UNIX domain socket COMSTACK
11  */
12
13 #ifndef WIN32
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <errno.h>
19 #if HAVE_SYS_TYPES_H
20 #include <sys/types.h>
21 #endif
22 #if HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif
25 #if HAVE_SYS_SOCKET_H
26 #include <sys/socket.h>
27 #endif
28 #include <fcntl.h>
29 #include <signal.h>
30
31 #include <grp.h>
32 #if HAVE_PWD_H
33 #include <pwd.h>
34 #endif
35
36 #if HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39 #if HAVE_SYS_UN_H
40 #include <sys/un.h>
41 #endif
42
43 #include <yaz/unix.h>
44 #include <yaz/nmem.h>
45
46 #ifndef YAZ_SOCKLEN_T
47 #define YAZ_SOCKLEN_T int
48 #endif
49
50 /* stat(2) masks: S_IFMT and S_IFSOCK may not be defined in gcc -ansi mode */
51 #if __STRICT_ANSI__
52 #ifndef S_IFSOCK
53 #define S_IFMT   0170000
54 #define S_IFSOCK 0140000
55 #endif
56 #endif
57
58 static int unix_close(COMSTACK h);
59 static int unix_put(COMSTACK h, char *buf, int size);
60 static int unix_get(COMSTACK h, char **buf, int *bufsize);
61 static int unix_connect(COMSTACK h, void *address);
62 static int unix_more(COMSTACK h);
63 static int unix_rcvconnect(COMSTACK h);
64 static int unix_bind(COMSTACK h, void *address, int mode);
65 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
66                 int (*check_ip)(void *cd, const char *a, int len, int type),
67                 void *cd);
68 static int unix_set_blocking(COMSTACK p, int blocking);
69
70 static COMSTACK unix_accept(COMSTACK h);
71 static char *unix_addrstr(COMSTACK h);
72 static void *unix_straddr(COMSTACK h, const char *str);
73
74 #ifndef SUN_LEN
75 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
76                       + strlen ((ptr)->sun_path))
77 #endif
78 #if 0
79 #define TRC(x) x
80 #else
81 #define TRC(X)
82 #endif
83
84 /* this state is used for both SSL and straight TCP/IP */
85 typedef struct unix_state
86 {
87     char *altbuf; /* alternate buffer for surplus data */
88     int altsize;  /* size as xmalloced */
89     int altlen;   /* length of data or 0 if none */
90
91     int written;  /* -1 if we aren't writing */
92     int towrite;  /* to verify against user input */
93     int (*complete)(const unsigned char *buf, int len); /* length/comple. */
94     struct sockaddr_un addr;  /* returned by cs_straddr */
95     int uid;
96     int gid;
97     int umask;
98     char buf[128]; /* returned by cs_addrstr */
99 } unix_state;
100
101 static int unix_init (void)
102 {
103     return 1;
104 }
105
106 /*
107  * This function is always called through the cs_create() macro.
108  * s >= 0: socket has already been established for us.
109  */
110 COMSTACK unix_type(int s, int flags, int protocol, void *vp)
111 {
112     COMSTACK p;
113     unix_state *state;
114     int new_socket;
115
116     if (!unix_init ())
117         return 0;
118     if (s < 0)
119     {
120         if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
121             return 0;
122         new_socket = 1;
123     }
124     else
125         new_socket = 0;
126     if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
127         return 0;
128     if (!(state = (struct unix_state *)(p->cprivate =
129                                         xmalloc(sizeof(unix_state)))))
130         return 0;
131
132     p->flags = flags;
133     if (!(p->flags&CS_FLAGS_BLOCKING))
134     {
135         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
136             return 0;
137 #ifndef MSG_NOSIGNAL
138         signal (SIGPIPE, SIG_IGN);
139 #endif
140     }
141
142     p->io_pending = 0;
143     p->iofile = s;
144     p->type = unix_type;
145     p->protocol = (enum oid_proto) protocol;
146
147     p->f_connect = unix_connect;
148     p->f_rcvconnect = unix_rcvconnect;
149     p->f_get = unix_get;
150     p->f_put = unix_put;
151     p->f_close = unix_close;
152     p->f_more = unix_more;
153     p->f_bind = unix_bind;
154     p->f_listen = unix_listen;
155     p->f_accept = unix_accept;
156     p->f_addrstr = unix_addrstr;
157     p->f_straddr = unix_straddr;
158     p->f_set_blocking = unix_set_blocking;
159
160     p->state = new_socket ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
161     p->event = CS_NONE;
162     p->cerrno = 0;
163     p->stackerr = 0;
164     p->user = 0;
165
166     state->altbuf = 0;
167     state->altsize = state->altlen = 0;
168     state->towrite = state->written = -1;
169     if (protocol == PROTO_WAIS)
170         state->complete = completeWAIS;
171     else
172         state->complete = cs_complete_auto;
173
174     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
175     TRC(fprintf(stderr, "Created new UNIX comstack\n"));
176
177     return p;
178 }
179
180
181 static int unix_strtoaddr_ex(const char *str, struct sockaddr_un *add)
182 {
183     char *cp;
184     if (!unix_init ())
185         return 0;
186     TRC(fprintf(stderr, "unix_strtoaddress: %s\n", str ? str : "NULL"));
187     add->sun_family = AF_UNIX;
188     strncpy(add->sun_path, str, sizeof(add->sun_path)-1);
189     add->sun_path[sizeof(add->sun_path)-1] = 0;
190     cp = strchr (add->sun_path, ':');
191     if (cp)
192         *cp = '\0';
193     return 1;
194 }
195
196 static void *unix_straddr1(COMSTACK h, const char *str, char *f)
197 {
198     unix_state *sp = (unix_state *)h->cprivate;
199     char * s = f;
200     const char * file = NULL;
201     char * eol;
202
203     sp->uid = sp->gid = sp->umask = -1;
204
205     if ((eol = strchr(s, ',')))
206     {
207         do
208         {
209             if ((eol = strchr(s, ',')))
210                 *eol++ = '\0';
211             if (sp->uid  == -1 && strncmp(s, "user=",  5) == 0)
212             {
213                 char * arg = s + 5;
214                 if (strspn(arg, "0123456789") == strlen(arg))
215                 {
216                     sp->uid = atoi(arg);
217                 }
218                 else
219                 {
220                     struct passwd * pw = getpwnam(arg);
221                     if(pw == NULL)
222                     {
223                         printf("No such user\n");
224                         return 0;
225                     }
226                     sp->uid = pw->pw_uid;
227                 }
228             }
229             else if (sp->gid == -1 && strncmp(s, "group=", 6) == 0)
230             {
231                 char * arg = s + 6;
232                 if (strspn(arg, "0123456789") == strlen(arg))
233                 {
234                     sp->gid = atoi(arg);
235                 }
236                 else
237                 {
238                     struct group * gr = getgrnam(arg);
239                     if (gr == NULL)
240                     {
241                         printf("No such group\n");
242                         return 0;
243                     }
244                     sp->gid = gr->gr_gid;
245                 }
246             }
247             else if (sp->umask == -1 && strncmp(s, "umask=", 6) == 0)
248             {
249                 char * end;
250                 char * arg = s + 6;
251                 
252                 sp->umask = strtol(arg, &end, 8);
253                 if (errno == EINVAL ||
254                     *end)
255                 {
256                     printf("Invalid umask\n");
257                     return 0;
258                 }
259             }
260             else if (file == NULL && strncmp(s, "file=", 5) == 0)
261             {
262                 char * arg = s + 5;
263                 file = arg;
264             }
265             else
266             {
267                 printf("invalid or double argument: %s\n", s);
268                 return 0;
269             }
270         } while((s = eol));
271     }
272     else
273     {
274         file = str;
275     }
276     if(! file)
277     {
278         errno = EINVAL;
279         return 0;
280     }
281
282     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
283
284     if (!unix_strtoaddr_ex (file, &sp->addr))
285         return 0;
286     return &sp->addr;
287 }
288
289 static void *unix_straddr(COMSTACK h, const char *str)
290 {
291     char *f = xstrdup(str);
292     void *vp = unix_straddr1(h, str, f);
293     xfree(f);
294     return vp;
295 }
296
297 struct sockaddr_un *unix_strtoaddr(const char *str)
298 {
299     static struct sockaddr_un add;
300
301     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
302
303     if (!unix_strtoaddr_ex (str, &add))
304         return 0;
305     return &add;
306 }
307
308 static int unix_more(COMSTACK h)
309 {
310     unix_state *sp = (unix_state *)h->cprivate;
311
312     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
313                                          sp->altlen);
314 }
315
316 /*
317  * connect(2) will block (sometimes) - nothing we can do short of doing
318  * weird things like spawning subprocesses or threading or some weird junk
319  * like that.
320  */
321 static int unix_connect(COMSTACK h, void *address)
322 {
323     struct sockaddr_un *add = (struct sockaddr_un *)address;
324     int r;
325     int i;
326
327     TRC(fprintf(stderr, "unix_connect\n"));
328     h->io_pending = 0;
329     if (h->state != CS_ST_UNBND)
330     {
331         h->cerrno = CSOUTSTATE;
332         return -1;
333     }
334     for (i = 0; i<3; i++)
335     {
336         r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add));
337         if (r < 0 && yaz_errno() == EAGAIN)
338         {
339 #if HAVE_USLEEP
340             usleep(i*10000+1000); /* 1ms, 11ms, 21ms */
341 #else
342             sleep(1);
343 #endif
344             continue;
345         }
346         else
347             break;
348     }
349     if (r < 0)
350     {
351         if (yaz_errno() == EINPROGRESS)
352         {
353             h->event = CS_CONNECT;
354             h->state = CS_ST_CONNECTING;
355             h->io_pending = CS_WANT_WRITE;
356             return 1;
357         }
358         h->cerrno = CSYSERR;
359         return -1;
360     }
361     h->event = CS_CONNECT;
362     h->state = CS_ST_CONNECTING;
363
364     return unix_rcvconnect (h);
365 }
366
367 /*
368  * nop
369  */
370 static int unix_rcvconnect(COMSTACK h)
371 {
372     TRC(fprintf(stderr, "unix_rcvconnect\n"));
373
374     if (h->state == CS_ST_DATAXFER)
375         return 0;
376     if (h->state != CS_ST_CONNECTING)
377     {
378         h->cerrno = CSOUTSTATE;
379         return -1;
380     }
381     h->event = CS_DATA;
382     h->state = CS_ST_DATAXFER;
383     return 0;
384 }
385
386 static int unix_bind(COMSTACK h, void *address, int mode)
387 {
388     unix_state *sp = (unix_state *)h->cprivate;
389     struct sockaddr *addr = (struct sockaddr *)address;
390     const char * path = ((struct sockaddr_un *)addr)->sun_path;
391     struct stat stat_buf;
392
393     TRC (fprintf (stderr, "unix_bind\n"));
394
395     if(stat(path, &stat_buf) != -1) {
396         struct sockaddr_un socket_unix;
397         int socket_out = -1;
398
399         if((stat_buf.st_mode&S_IFMT) != S_IFSOCK) { /* used to be S_ISSOCK */
400             h->cerrno = CSYSERR;
401             yaz_set_errno(EEXIST); /* Not a socket (File exists) */
402             return -1;
403         }
404         if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
405             h->cerrno = CSYSERR;
406             return -1;
407         }
408         socket_unix.sun_family = AF_UNIX;
409         strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path)-1);
410         socket_unix.sun_path[sizeof(socket_unix.sun_path)-1] = 0;
411         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
412             if(yaz_errno() == ECONNREFUSED) {
413                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
414             } else {
415                 h->cerrno = CSYSERR;
416                 return -1;
417             }
418         } else {
419             close(socket_out);
420             h->cerrno = CSYSERR;
421             yaz_set_errno(EADDRINUSE);
422             return -1;
423         }
424         unlink(path);
425     }
426
427     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
428     {
429         h->cerrno = CSYSERR;
430         return -1;
431     }
432     chown(path, sp->uid, sp->gid);
433     chmod(path, sp->umask != -1 ? sp->umask : 0666);
434     if (mode == CS_SERVER && listen(h->iofile, 100) < 0)
435     {
436         h->cerrno = CSYSERR;
437         return -1;
438     }
439     h->state = CS_ST_IDLE;
440     h->event = CS_LISTEN;
441     return 0;
442 }
443
444 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
445                     int (*check_ip)(void *cd, const char *a, int len, int t),
446                     void *cd)
447 {
448     struct sockaddr_un addr;
449     YAZ_SOCKLEN_T len = sizeof(addr);
450
451     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
452     if (h->state != CS_ST_IDLE)
453     {
454         h->cerrno = CSOUTSTATE;
455         return -1;
456     }
457     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
458     if (h->newfd < 0)
459     {
460         if (
461             yaz_errno() == EWOULDBLOCK
462 #ifdef EAGAIN
463 #if EAGAIN != EWOULDBLOCK
464             || yaz_errno() == EAGAIN
465 #endif
466 #endif
467             )
468             h->cerrno = CSNODATA;
469         else
470             h->cerrno = CSYSERR;
471         return -1;
472     }
473     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
474         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
475     else if (addrlen)
476         *addrlen = 0;
477     h->state = CS_ST_INCON;
478     return 0;
479 }
480
481 static COMSTACK unix_accept(COMSTACK h)
482 {
483     COMSTACK cnew;
484     unix_state *state, *st = (unix_state *)h->cprivate;
485
486     TRC(fprintf(stderr, "unix_accept\n"));
487     if (h->state == CS_ST_INCON)
488     {
489         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
490         {
491             h->cerrno = CSYSERR;
492             close(h->newfd);
493             h->newfd = -1;
494             return 0;
495         }
496         memcpy(cnew, h, sizeof(*h));
497         cnew->iofile = h->newfd;
498         cnew->io_pending = 0;
499         if (!(state = (unix_state *)
500               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
501         {
502             h->cerrno = CSYSERR;
503             if (h->newfd != -1)
504             {
505                 close(h->newfd);
506                 h->newfd = -1;
507             }
508             return 0;
509         }
510         if (!(cnew->flags&CS_FLAGS_BLOCKING) && 
511             (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
512             )
513         {
514             h->cerrno = CSYSERR;
515             if (h->newfd != -1)
516             {
517                 close(h->newfd);
518                 h->newfd = -1;
519             }
520             xfree (cnew);
521             xfree (state);
522             return 0;
523         }
524         h->newfd = -1;
525         state->altbuf = 0;
526         state->altsize = state->altlen = 0;
527         state->towrite = state->written = -1;
528         state->complete = st->complete;
529         memcpy(&state->addr, &st->addr, sizeof(state->addr));
530         cnew->state = CS_ST_ACCEPT;
531         cnew->event = CS_NONE;
532         h->state = CS_ST_IDLE;
533
534         h = cnew;
535     }
536     if (h->state == CS_ST_ACCEPT)
537     {
538     }
539     else
540     {
541         h->cerrno = CSOUTSTATE;
542         return 0;
543     }
544     h->io_pending = 0;
545     h->state = CS_ST_DATAXFER;
546     h->event = CS_DATA;
547     return h;
548 }
549
550 #define CS_UNIX_BUFCHUNK 4096
551
552 /*
553  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
554  * 0=connection closed.
555  */
556 static int unix_get(COMSTACK h, char **buf, int *bufsize)
557 {
558     unix_state *sp = (unix_state *)h->cprivate;
559     char *tmpc;
560     int tmpi, berlen, rest, req, tomove;
561     int hasread = 0, res;
562
563     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
564     if (sp->altlen) /* switch buffers */
565     {
566         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
567                     (unsigned) sp->altbuf));
568         tmpc = *buf;
569         tmpi = *bufsize;
570         *buf = sp->altbuf;
571         *bufsize = sp->altsize;
572         hasread = sp->altlen;
573         sp->altlen = 0;
574         sp->altbuf = tmpc;
575         sp->altsize = tmpi;
576     }
577     h->io_pending = 0;
578     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
579     {
580         if (!*bufsize)
581         {
582             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
583                 return -1;
584         }
585         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
586             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
587                 return -1;
588         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
589         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
590         if (res < 0)
591         {
592             if (yaz_errno() == EWOULDBLOCK
593 #ifdef EAGAIN
594 #if EAGAIN != EWOULDBLOCK
595                 || yaz_errno() == EAGAIN
596 #endif
597 #endif
598                 || yaz_errno() == EINPROGRESS
599                 )
600             {
601                 h->io_pending = CS_WANT_READ;
602                 break;
603             }
604             else if (yaz_errno() == 0)
605                 continue;
606             else
607                 return -1;
608         }
609         else if (!res)
610             return hasread;
611         hasread += res;
612     }
613     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
614                   hasread, berlen));
615     /* move surplus buffer (or everything if we didn't get a BER rec.) */
616     if (hasread > berlen)
617     {
618         tomove = req = hasread - berlen;
619         rest = tomove % CS_UNIX_BUFCHUNK;
620         if (rest)
621             req += CS_UNIX_BUFCHUNK - rest;
622         if (!sp->altbuf)
623         {
624             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
625                 return -1;
626         } else if (sp->altsize < req)
627             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
628                 return -1;
629         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
630                     (unsigned) sp->altbuf));
631         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
632     }
633     if (berlen < CS_UNIX_BUFCHUNK - 1)
634         *(*buf + berlen) = '\0';
635     return berlen ? berlen : 1;
636 }
637
638
639
640 /*
641  * Returns 1, 0 or -1
642  * In nonblocking mode, you must call again with same buffer while
643  * return value is 1.
644  */
645 static int unix_put(COMSTACK h, char *buf, int size)
646 {
647     int res;
648     struct unix_state *state = (struct unix_state *)h->cprivate;
649
650     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
651     h->io_pending = 0;
652     h->event = CS_DATA;
653     if (state->towrite < 0)
654     {
655         state->towrite = size;
656         state->written = 0;
657     }
658     else if (state->towrite != size)
659     {
660         h->cerrno = CSWRONGBUF;
661         return -1;
662     }
663     while (state->towrite > state->written)
664     {
665         if ((res =
666              send(h->iofile, buf + state->written, size -
667                   state->written,
668 #ifdef MSG_NOSIGNAL
669                   MSG_NOSIGNAL
670 #else
671                   0
672 #endif
673                  )) < 0)
674         {
675             if (
676                 yaz_errno() == EWOULDBLOCK
677 #ifdef EAGAIN
678 #if EAGAIN != EWOULDBLOCK
679                 || yaz_errno() == EAGAIN
680 #endif
681 #endif
682                 )
683             {
684                 TRC(fprintf(stderr, "  Flow control stop\n"));
685                 h->io_pending = CS_WANT_WRITE;
686                 return 1;
687             }
688             h->cerrno = CSYSERR;
689             return -1;
690         }
691         state->written += res;
692         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
693                     res, state->written, size));
694     }
695     state->towrite = state->written = -1;
696     TRC(fprintf(stderr, "  Ok\n"));
697     return 0;
698 }
699
700 static int unix_close(COMSTACK h)
701 {
702     unix_state *sp = (struct unix_state *)h->cprivate;
703
704     TRC(fprintf(stderr, "unix_close\n"));
705     if (h->iofile != -1)
706     {
707         close(h->iofile);
708     }
709     if (sp->altbuf)
710         xfree(sp->altbuf);
711     xfree(sp);
712     xfree(h);
713     return 0;
714 }
715
716 static char *unix_addrstr(COMSTACK h)
717 {
718     unix_state *sp = (struct unix_state *)h->cprivate;
719     char *buf = sp->buf;
720     sprintf(buf, "unix:%s", sp->addr.sun_path);
721     return buf;
722 }
723
724 static int unix_set_blocking(COMSTACK p, int flags)
725 {
726     unsigned long flag;
727
728     if (p->flags == flags)
729         return 1;
730     flag = fcntl(p->iofile, F_GETFL, 0);
731     if (flags & CS_FLAGS_BLOCKING)
732         flag = flag & ~O_NONBLOCK;
733     else
734         flag = flag | O_NONBLOCK;
735     if (fcntl(p->iofile, F_SETFL, flag) < 0)
736         return 0;
737     p->flags = flags;
738     return 1;
739 }
740 #endif /* WIN32 */
741 /*
742  * Local variables:
743  * c-basic-offset: 4
744  * indent-tabs-mode: nil
745  * End:
746  * vim: shiftwidth=4 tabstop=8 expandtab
747  */
748