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