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