Merge branch 'sru_2_0' of ssh://git.indexdata.com/home/git/pub/yaz into sru_2_0
[yaz-moved-to-github.git] / src / unix.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2013 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file unix.c
7  * \brief Implements UNIX domain socket COMSTACK
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
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/errno.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 void 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 const 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 char *buf, int len); /* length/complete. */
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->user = 0;
164
165     state->altbuf = 0;
166     state->altsize = state->altlen = 0;
167     state->towrite = state->written = -1;
168     state->complete = cs_complete_auto;
169
170     TRC(fprintf(stderr, "Created new UNIX comstack\n"));
171
172     return p;
173 }
174
175
176 static int unix_strtoaddr_ex(const char *str, struct sockaddr_un *add)
177 {
178     char *cp;
179     if (!unix_init ())
180         return 0;
181     TRC(fprintf(stderr, "unix_strtoaddress: %s\n", str ? str : "NULL"));
182     add->sun_family = AF_UNIX;
183     strncpy(add->sun_path, str, sizeof(add->sun_path)-1);
184     add->sun_path[sizeof(add->sun_path)-1] = 0;
185     cp = strchr (add->sun_path, ':');
186     if (cp)
187         *cp = '\0';
188     return 1;
189 }
190
191 static void *unix_straddr1(COMSTACK h, const char *str, char *f)
192 {
193     unix_state *sp = (unix_state *)h->cprivate;
194     char * s = f;
195     const char * file = NULL;
196
197     sp->uid = sp->gid = sp->umask = -1;
198
199     if (strchr(s, '='))
200     {
201         char *eol;
202         do
203         {
204             if ((eol = strchr(s, ',')))
205                 *eol++ = '\0';
206             if (sp->uid  == -1 && strncmp(s, "user=",  5) == 0)
207             {
208                 char * arg = s + 5;
209                 if (strspn(arg, "0123456789") == strlen(arg))
210                 {
211                     sp->uid = atoi(arg);
212                 }
213                 else
214                 {
215                     struct passwd * pw = getpwnam(arg);
216                     if(pw == NULL)
217                     {
218                         printf("No such user\n");
219                         return 0;
220                     }
221                     sp->uid = pw->pw_uid;
222                 }
223             }
224             else if (sp->gid == -1 && strncmp(s, "group=", 6) == 0)
225             {
226                 char * arg = s + 6;
227                 if (strspn(arg, "0123456789") == strlen(arg))
228                 {
229                     sp->gid = atoi(arg);
230                 }
231                 else
232                 {
233                     struct group * gr = getgrnam(arg);
234                     if (gr == NULL)
235                     {
236                         printf("No such group\n");
237                         return 0;
238                     }
239                     sp->gid = gr->gr_gid;
240                 }
241             }
242             else if (sp->umask == -1 && strncmp(s, "umask=", 6) == 0)
243             {
244                 char * end;
245                 char * arg = s + 6;
246
247                 sp->umask = strtol(arg, &end, 8);
248                 if (errno == EINVAL ||
249                     *end)
250                 {
251                     printf("Invalid umask\n");
252                     return 0;
253                 }
254             }
255             else if (file == NULL && strncmp(s, "file=", 5) == 0)
256             {
257                 char * arg = s + 5;
258                 file = arg;
259             }
260             else
261             {
262                 printf("invalid or double argument: %s\n", s);
263                 return 0;
264             }
265         } while((s = eol));
266     }
267     else
268     {
269         file = str;
270     }
271     if(! file)
272     {
273         errno = EINVAL;
274         return 0;
275     }
276
277     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
278
279     if (!unix_strtoaddr_ex (file, &sp->addr))
280         return 0;
281     return &sp->addr;
282 }
283
284 static void *unix_straddr(COMSTACK h, const char *str)
285 {
286     char *f = xstrdup(str);
287     void *vp = unix_straddr1(h, str, f);
288     xfree(f);
289     return vp;
290 }
291
292 struct sockaddr_un *unix_strtoaddr(const char *str)
293 {
294     static struct sockaddr_un add;
295
296     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
297
298     if (!unix_strtoaddr_ex (str, &add))
299         return 0;
300     return &add;
301 }
302
303 static int unix_more(COMSTACK h)
304 {
305     unix_state *sp = (unix_state *)h->cprivate;
306
307     return sp->altlen && (*sp->complete)(sp->altbuf, sp->altlen);
308 }
309
310 /*
311  * connect(2) will block (sometimes) - nothing we can do short of doing
312  * weird things like spawning subprocesses or threading or some weird junk
313  * like that.
314  */
315 static int unix_connect(COMSTACK h, void *address)
316 {
317     struct sockaddr_un *add = (struct sockaddr_un *)address;
318     int r;
319     int i;
320
321     TRC(fprintf(stderr, "unix_connect\n"));
322     h->io_pending = 0;
323     if (h->state != CS_ST_UNBND)
324     {
325         h->cerrno = CSOUTSTATE;
326         return -1;
327     }
328     for (i = 0; i<3; i++)
329     {
330         r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add));
331         if (r < 0 && yaz_errno() == EAGAIN)
332         {
333 #if HAVE_USLEEP
334             usleep(i*10000+1000); /* 1ms, 11ms, 21ms */
335 #else
336             sleep(1);
337 #endif
338             continue;
339         }
340         else
341             break;
342     }
343     if (r < 0)
344     {
345         if (yaz_errno() == EINPROGRESS)
346         {
347             h->event = CS_CONNECT;
348             h->state = CS_ST_CONNECTING;
349             h->io_pending = CS_WANT_WRITE;
350             return 1;
351         }
352         h->cerrno = CSYSERR;
353         return -1;
354     }
355     h->event = CS_CONNECT;
356     h->state = CS_ST_CONNECTING;
357
358     return unix_rcvconnect (h);
359 }
360
361 /*
362  * nop
363  */
364 static int unix_rcvconnect(COMSTACK h)
365 {
366     TRC(fprintf(stderr, "unix_rcvconnect\n"));
367
368     if (h->state == CS_ST_DATAXFER)
369         return 0;
370     if (h->state != CS_ST_CONNECTING)
371     {
372         h->cerrno = CSOUTSTATE;
373         return -1;
374     }
375     h->event = CS_DATA;
376     h->state = CS_ST_DATAXFER;
377     return 0;
378 }
379
380 static int unix_bind(COMSTACK h, void *address, int mode)
381 {
382     unix_state *sp = (unix_state *)h->cprivate;
383     struct sockaddr *addr = (struct sockaddr *)address;
384     const char * path = ((struct sockaddr_un *)addr)->sun_path;
385     struct stat stat_buf;
386
387     TRC (fprintf (stderr, "unix_bind\n"));
388
389     if(stat(path, &stat_buf) != -1) {
390         struct sockaddr_un socket_unix;
391         int socket_out = -1;
392
393         if((stat_buf.st_mode&S_IFMT) != S_IFSOCK) { /* used to be S_ISSOCK */
394             h->cerrno = CSYSERR;
395             yaz_set_errno(EEXIST); /* Not a socket (File exists) */
396             return -1;
397         }
398         if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
399             h->cerrno = CSYSERR;
400             return -1;
401         }
402         socket_unix.sun_family = AF_UNIX;
403         strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path)-1);
404         socket_unix.sun_path[sizeof(socket_unix.sun_path)-1] = 0;
405         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
406             if(yaz_errno() == ECONNREFUSED) {
407                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
408             } else {
409                 h->cerrno = CSYSERR;
410                 return -1;
411             }
412         } else {
413             close(socket_out);
414             h->cerrno = CSYSERR;
415             yaz_set_errno(EADDRINUSE);
416             return -1;
417         }
418         unlink(path);
419     }
420
421     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
422     {
423         h->cerrno = CSYSERR;
424         return -1;
425     }
426     if (chown(path, sp->uid, sp->gid))
427     {
428         h->cerrno = CSYSERR;
429         return -1;
430     }
431     if (chmod(path, sp->umask != -1 ? sp->umask : 0666))
432     {
433         h->cerrno = CSYSERR;
434         return -1;
435     }
436     if (mode == CS_SERVER && listen(h->iofile, 100) < 0)
437     {
438         h->cerrno = CSYSERR;
439         return -1;
440     }
441     h->state = CS_ST_IDLE;
442     h->event = CS_LISTEN;
443     return 0;
444 }
445
446 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
447                     int (*check_ip)(void *cd, const char *a, int len, int t),
448                     void *cd)
449 {
450     struct sockaddr_un addr;
451     YAZ_SOCKLEN_T len = sizeof(addr);
452
453     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
454     if (h->state != CS_ST_IDLE)
455     {
456         h->cerrno = CSOUTSTATE;
457         return -1;
458     }
459     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
460     if (h->newfd < 0)
461     {
462         if (
463             yaz_errno() == EWOULDBLOCK
464 #ifdef EAGAIN
465 #if EAGAIN != EWOULDBLOCK
466             || yaz_errno() == EAGAIN
467 #endif
468 #endif
469             )
470             h->cerrno = CSNODATA;
471         else
472             h->cerrno = CSYSERR;
473         return -1;
474     }
475     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
476         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
477     else if (addrlen)
478         *addrlen = 0;
479     h->state = CS_ST_INCON;
480     return 0;
481 }
482
483 static COMSTACK unix_accept(COMSTACK h)
484 {
485     COMSTACK cnew;
486     unix_state *state, *st = (unix_state *)h->cprivate;
487
488     TRC(fprintf(stderr, "unix_accept\n"));
489     if (h->state == CS_ST_INCON)
490     {
491         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
492         {
493             h->cerrno = CSYSERR;
494             close(h->newfd);
495             h->newfd = -1;
496             return 0;
497         }
498         memcpy(cnew, h, sizeof(*h));
499         cnew->iofile = h->newfd;
500         cnew->io_pending = 0;
501         if (!(state = (unix_state *)
502               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
503         {
504             h->cerrno = CSYSERR;
505             if (h->newfd != -1)
506             {
507                 close(h->newfd);
508                 h->newfd = -1;
509             }
510             return 0;
511         }
512         if (!(cnew->flags&CS_FLAGS_BLOCKING) &&
513             (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
514             )
515         {
516             h->cerrno = CSYSERR;
517             if (h->newfd != -1)
518             {
519                 close(h->newfd);
520                 h->newfd = -1;
521             }
522             xfree (cnew);
523             xfree (state);
524             return 0;
525         }
526         h->newfd = -1;
527         state->altbuf = 0;
528         state->altsize = state->altlen = 0;
529         state->towrite = state->written = -1;
530         state->complete = st->complete;
531         memcpy(&state->addr, &st->addr, sizeof(state->addr));
532         cnew->state = CS_ST_ACCEPT;
533         cnew->event = CS_NONE;
534         h->state = CS_ST_IDLE;
535
536         h = cnew;
537     }
538     if (h->state == CS_ST_ACCEPT)
539     {
540     }
541     else
542     {
543         h->cerrno = CSOUTSTATE;
544         return 0;
545     }
546     h->io_pending = 0;
547     h->state = CS_ST_DATAXFER;
548     h->event = CS_DATA;
549     return h;
550 }
551
552 #define CS_UNIX_BUFCHUNK 4096
553
554 /*
555  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
556  * 0=connection closed.
557  */
558 static int unix_get(COMSTACK h, char **buf, int *bufsize)
559 {
560     unix_state *sp = (unix_state *)h->cprivate;
561     char *tmpc;
562     int tmpi, berlen, rest, req, tomove;
563     int hasread = 0, res;
564
565     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
566     if (sp->altlen) /* switch buffers */
567     {
568         TRC(fprintf(stderr, "  %d bytes in altbuf (%p )\n", sp->altlen,
569                     sp->altbuf));
570         tmpc = *buf;
571         tmpi = *bufsize;
572         *buf = sp->altbuf;
573         *bufsize = sp->altsize;
574         hasread = sp->altlen;
575         sp->altlen = 0;
576         sp->altbuf = tmpc;
577         sp->altsize = tmpi;
578     }
579     h->io_pending = 0;
580     while (!(berlen = (*sp->complete)(*buf, hasread)))
581     {
582         if (!*bufsize)
583         {
584             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
585                 return -1;
586         }
587         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
588             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
589                 return -1;
590         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
591         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
592         if (res < 0)
593         {
594             if (yaz_errno() == EWOULDBLOCK
595 #ifdef EAGAIN
596 #if EAGAIN != EWOULDBLOCK
597                 || yaz_errno() == EAGAIN
598 #endif
599 #endif
600                 || yaz_errno() == EINPROGRESS
601                 )
602             {
603                 h->io_pending = CS_WANT_READ;
604                 break;
605             }
606             else if (yaz_errno() == 0)
607                 continue;
608             else
609                 return -1;
610         }
611         else if (!res)
612             return hasread;
613         hasread += res;
614     }
615     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
616                   hasread, berlen));
617     /* move surplus buffer (or everything if we didn't get a BER rec.) */
618     if (hasread > berlen)
619     {
620         tomove = req = hasread - berlen;
621         rest = tomove % CS_UNIX_BUFCHUNK;
622         if (rest)
623             req += CS_UNIX_BUFCHUNK - rest;
624         if (!sp->altbuf)
625         {
626             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
627                 return -1;
628         } else if (sp->altsize < req)
629             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
630                 return -1;
631         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(%p)\n", tomove,
632                     sp->altbuf));
633         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
634     }
635     if (berlen < CS_UNIX_BUFCHUNK - 1)
636         *(*buf + berlen) = '\0';
637     return berlen ? berlen : 1;
638 }
639
640
641
642 /*
643  * Returns 1, 0 or -1
644  * In nonblocking mode, you must call again with same buffer while
645  * return value is 1.
646  */
647 static int unix_put(COMSTACK h, char *buf, int size)
648 {
649     int res;
650     struct unix_state *state = (struct unix_state *)h->cprivate;
651
652     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
653     h->io_pending = 0;
654     h->event = CS_DATA;
655     if (state->towrite < 0)
656     {
657         state->towrite = size;
658         state->written = 0;
659     }
660     else if (state->towrite != size)
661     {
662         h->cerrno = CSWRONGBUF;
663         return -1;
664     }
665     while (state->towrite > state->written)
666     {
667         if ((res =
668              send(h->iofile, buf + state->written, size -
669                   state->written,
670 #ifdef MSG_NOSIGNAL
671                   MSG_NOSIGNAL
672 #else
673                   0
674 #endif
675                  )) < 0)
676         {
677             if (
678                 yaz_errno() == EWOULDBLOCK
679 #ifdef EAGAIN
680 #if EAGAIN != EWOULDBLOCK
681                 || yaz_errno() == EAGAIN
682 #endif
683 #endif
684                 )
685             {
686                 TRC(fprintf(stderr, "  Flow control stop\n"));
687                 h->io_pending = CS_WANT_WRITE;
688                 return 1;
689             }
690             h->cerrno = CSYSERR;
691             return -1;
692         }
693         state->written += res;
694         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
695                     res, state->written, size));
696     }
697     state->towrite = state->written = -1;
698     TRC(fprintf(stderr, "  Ok\n"));
699     return 0;
700 }
701
702 static void unix_close(COMSTACK h)
703 {
704     unix_state *sp = (struct unix_state *)h->cprivate;
705
706     TRC(fprintf(stderr, "unix_close\n"));
707     if (h->iofile != -1)
708     {
709         close(h->iofile);
710     }
711     if (sp->altbuf)
712         xfree(sp->altbuf);
713     xfree(sp);
714     xfree(h);
715 }
716
717 static const char *unix_addrstr(COMSTACK h)
718 {
719     unix_state *sp = (struct unix_state *)h->cprivate;
720     char *buf = sp->buf;
721     sprintf(buf, "unix:%s", sp->addr.sun_path);
722     return buf;
723 }
724
725 static int unix_set_blocking(COMSTACK p, int flags)
726 {
727     unsigned long flag;
728
729     if (p->flags == flags)
730         return 1;
731     flag = fcntl(p->iofile, F_GETFL, 0);
732     if (flags & CS_FLAGS_BLOCKING)
733         flag = flag & ~O_NONBLOCK;
734     else
735         flag = flag | O_NONBLOCK;
736     if (fcntl(p->iofile, F_SETFL, flag) < 0)
737         return 0;
738     p->flags = flags;
739     return 1;
740 }
741 #endif /* WIN32 */
742 /*
743  * Local variables:
744  * c-basic-offset: 4
745  * c-file-style: "Stroustrup"
746  * indent-tabs-mode: nil
747  * End:
748  * vim: shiftwidth=4 tabstop=8 expandtab
749  */
750