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