New COMSTACK: UNIX socket
[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.1 2002-06-04 11:36:10 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     struct hostent *hp;
147     char *p, buf[512];
148     short int port = 210;
149     unsigned tmpadd;
150
151     if (!unix_init ())
152         return 0;
153     TRC(fprintf(stderr, "unix_strtoaddress: %s\n", str ? str : "NULL"));
154     add->sun_family = AF_UNIX;
155     strncpy(add->sun_path, str, sizeof(add->sun_path));
156     return 1;
157 }
158
159 void *unix_straddr(COMSTACK h, const char *str)
160 {
161     unix_state *sp = (unix_state *)h->cprivate;
162
163     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
164
165     if (!unix_strtoaddr_ex (str, &sp->addr))
166         return 0;
167     return &sp->addr;
168 }
169
170 struct sockaddr_un *unix_strtoaddr(const char *str)
171 {
172     static struct sockaddr_un add;
173
174     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
175
176     if (!unix_strtoaddr_ex (str, &add))
177         return 0;
178     return &add;
179 }
180
181 int unix_more(COMSTACK h)
182 {
183     unix_state *sp = (unix_state *)h->cprivate;
184
185     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
186                                          sp->altlen);
187 }
188
189 /*
190  * connect(2) will block (sometimes) - nothing we can do short of doing
191  * weird things like spawning subprocesses or threading or some weird junk
192  * like that.
193  */
194 int unix_connect(COMSTACK h, void *address)
195 {
196     struct sockaddr_un *add = (struct sockaddr_un *)address;
197     int r;
198
199     TRC(fprintf(stderr, "unix_connect\n"));
200     h->io_pending = 0;
201     if (h->state != CS_ST_UNBND)
202     {
203         h->cerrno = CSOUTSTATE;
204         return -1;
205     }
206     r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add));
207     if (r < 0)
208     {
209         if (errno == EINPROGRESS)
210         {
211             h->event = CS_CONNECT;
212             h->state = CS_ST_CONNECTING;
213             h->io_pending = CS_WANT_WRITE|CS_WANT_READ;
214             return 1;
215         }
216         h->cerrno = CSYSERR;
217         return -1;
218     }
219     h->event = CS_CONNECT;
220     h->state = CS_ST_CONNECTING;
221
222     return unix_rcvconnect (h);
223 }
224
225 /*
226  * nop
227  */
228 int unix_rcvconnect(COMSTACK h)
229 {
230     unix_state *sp = (unix_state *)h->cprivate;
231     TRC(fprintf(stderr, "unix_rcvconnect\n"));
232
233     if (h->state == CS_ST_DATAXFER)
234         return 0;
235     if (h->state != CS_ST_CONNECTING)
236     {
237         h->cerrno = CSOUTSTATE;
238         return -1;
239     }
240     h->event = CS_DATA;
241     h->state = CS_ST_DATAXFER;
242     return 0;
243 }
244
245 #define CERTF "ztest.pem"
246 #define KEYF "ztest.pem"
247
248 int unix_bind(COMSTACK h, void *address, int mode)
249 {
250     struct sockaddr *addr = (struct sockaddr *)address;
251     unsigned long one = 1;
252     const char * path = ((struct sockaddr_un *)addr)->sun_path;
253     struct stat stat_buf;
254
255     TRC (fprintf (stderr, "unix_bind\n"));
256
257     if(stat(path, &stat_buf) != -1) {
258         struct sockaddr_un socket_unix;
259         int socket_out = -1;
260         if(! S_ISSOCK(stat_buf.st_mode)) {
261             h->cerrno = CSYSERR;
262             errno = EEXIST; /* Not a socket (File exists) */
263             return -1;
264         }
265         if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
266             h->cerrno = CSYSERR;
267             return -1;
268         }
269         socket_unix.sun_family = AF_UNIX;
270         strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path));
271         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
272             if(errno == ECONNREFUSED) {
273                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
274             } else {
275                 h->cerrno = CSYSERR;
276                 return -1;
277             }
278         } else {
279             close(socket_out);
280             h->cerrno = CSYSERR;
281             errno = EADDRINUSE;
282             return -1;
283         }
284         unlink(path);
285     }
286
287     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
288     {
289         h->cerrno = CSYSERR;
290         return -1;
291     }
292     if (mode == CS_SERVER && listen(h->iofile, 3) < 0)
293     {
294         h->cerrno = CSYSERR;
295         return -1;
296     }
297     h->state = CS_ST_IDLE;
298     h->event = CS_LISTEN;
299     return 0;
300 }
301
302 int unix_listen(COMSTACK h, char *raddr, int *addrlen,
303                 int (*check_ip)(void *cd, const char *a, int len, int t),
304                 void *cd)
305 {
306     struct sockaddr_un addr;
307 #ifdef __cplusplus
308     socklen_t len = SUN_LEN(&addr);
309 #else
310     int len = SUN_LEN(&addr);
311 #endif
312
313     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
314     if (h->state != CS_ST_IDLE)
315     {
316         h->cerrno = CSOUTSTATE;
317         return -1;
318     }
319     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
320     if (h->newfd < 0)
321     {
322         if (
323             errno == EWOULDBLOCK
324 #ifdef EAGAIN
325 #if EAGAIN != EWOULDBLOCK
326             || errno == EAGAIN
327 #endif
328 #endif
329             )
330             h->cerrno = CSNODATA;
331         else
332             h->cerrno = CSYSERR;
333         return -1;
334     }
335     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
336         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
337     else if (addrlen)
338         *addrlen = 0;
339     h->state = CS_ST_INCON;
340     return 0;
341 }
342
343 COMSTACK unix_accept(COMSTACK h)
344 {
345     COMSTACK cnew;
346     unix_state *state, *st = (unix_state *)h->cprivate;
347
348     TRC(fprintf(stderr, "unix_accept\n"));
349     if (h->state == CS_ST_INCON)
350     {
351         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
352         {
353             h->cerrno = CSYSERR;
354             close(h->newfd);
355             h->newfd = -1;
356             return 0;
357         }
358         memcpy(cnew, h, sizeof(*h));
359         cnew->iofile = h->newfd;
360         cnew->io_pending = 0;
361         if (!(state = (unix_state *)
362               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
363         {
364             h->cerrno = CSYSERR;
365             if (h->newfd != -1)
366             {
367                 close(h->newfd);
368                 h->newfd = -1;
369             }
370             return 0;
371         }
372         if (!cnew->blocking &&
373             (!cnew->blocking && fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
374             )
375         {
376             h->cerrno = CSYSERR;
377             if (h->newfd != -1)
378             {
379                 close(h->newfd);
380                 h->newfd = -1;
381             }
382             xfree (cnew);
383             xfree (state);
384             return 0;
385         }
386         h->newfd = -1;
387         state->altbuf = 0;
388         state->altsize = state->altlen = 0;
389         state->towrite = state->written = -1;
390         state->complete = st->complete;
391         cnew->state = CS_ST_ACCEPT;
392         cnew->event = CS_NONE;
393         h->state = CS_ST_IDLE;
394
395         h = cnew;
396     }
397     if (h->state == CS_ST_ACCEPT)
398     {
399     }
400     else
401     {
402         h->cerrno = CSOUTSTATE;
403         return 0;
404     }
405     h->io_pending = 0;
406     h->state = CS_ST_DATAXFER;
407     h->event = CS_DATA;
408     return h;
409 }
410
411 #define CS_UNIX_BUFCHUNK 4096
412
413 /*
414  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
415  * 0=connection closed.
416  */
417 int unix_get(COMSTACK h, char **buf, int *bufsize)
418 {
419     unix_state *sp = (unix_state *)h->cprivate;
420     char *tmpc;
421     int tmpi, berlen, rest, req, tomove;
422     int hasread = 0, res;
423
424     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
425     if (sp->altlen) /* switch buffers */
426     {
427         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
428                     (unsigned) sp->altbuf));
429         tmpc = *buf;
430         tmpi = *bufsize;
431         *buf = sp->altbuf;
432         *bufsize = sp->altsize;
433         hasread = sp->altlen;
434         sp->altlen = 0;
435         sp->altbuf = tmpc;
436         sp->altsize = tmpi;
437     }
438     h->io_pending = 0;
439     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
440     {
441         if (!*bufsize)
442         {
443             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
444                 return -1;
445         }
446         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
447             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
448                 return -1;
449         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
450         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
451         if (res < 0)
452         {
453             if (errno == EWOULDBLOCK
454 #ifdef EAGAIN
455 #if EAGAIN != EWOULDBLOCK
456                 || errno == EAGAIN
457 #endif
458 #endif
459                 || errno == EINPROGRESS
460                 )
461             {
462                 h->io_pending = CS_WANT_READ;
463                 break;
464             }
465             else if (errno == 0)
466                 continue;
467             else
468                 return -1;
469         }
470         else if (!res)
471             return 0;
472         hasread += res;
473     }
474     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
475                   hasread, berlen));
476     /* move surplus buffer (or everything if we didn't get a BER rec.) */
477     if (hasread > berlen)
478     {
479         tomove = req = hasread - berlen;
480         rest = tomove % CS_UNIX_BUFCHUNK;
481         if (rest)
482             req += CS_UNIX_BUFCHUNK - rest;
483         if (!sp->altbuf)
484         {
485             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
486                 return -1;
487         } else if (sp->altsize < req)
488             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
489                 return -1;
490         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
491                     (unsigned) sp->altbuf));
492         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
493     }
494     if (berlen < CS_UNIX_BUFCHUNK - 1)
495         *(*buf + berlen) = '\0';
496     return berlen ? berlen : 1;
497 }
498
499
500
501 /*
502  * Returns 1, 0 or -1
503  * In nonblocking mode, you must call again with same buffer while
504  * return value is 1.
505  */
506 int unix_put(COMSTACK h, char *buf, int size)
507 {
508     int res;
509     struct unix_state *state = (struct unix_state *)h->cprivate;
510
511     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
512     h->io_pending = 0;
513     h->event = CS_DATA;
514     if (state->towrite < 0)
515     {
516         state->towrite = size;
517         state->written = 0;
518     }
519     else if (state->towrite != size)
520     {
521         h->cerrno = CSWRONGBUF;
522         return -1;
523     }
524     while (state->towrite > state->written)
525     {
526         if ((res =
527              send(h->iofile, buf + state->written, size -
528                   state->written,
529 #ifdef MSG_NOSIGNAL
530                   MSG_NOSIGNAL
531 #else
532                   0
533 #endif
534                  )) < 0)
535         {
536             if (
537                 errno == EWOULDBLOCK
538 #ifdef EAGAIN
539 #if EAGAIN != EWOULDBLOCK
540                 || errno == EAGAIN
541 #endif
542 #endif
543                 )
544             {
545                 TRC(fprintf(stderr, "  Flow control stop\n"));
546                 h->io_pending = CS_WANT_WRITE;
547                 return 1;
548             }
549             h->cerrno = CSYSERR;
550             return -1;
551         }
552         state->written += res;
553         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
554                     res, state->written, size));
555     }
556     state->towrite = state->written = -1;
557     TRC(fprintf(stderr, "  Ok\n"));
558     return 0;
559 }
560
561
562
563 int unix_close(COMSTACK h)
564 {
565     unix_state *sp = (struct unix_state *)h->cprivate;
566
567     TRC(fprintf(stderr, "unix_close\n"));
568     if (h->iofile != -1)
569     {
570         close(h->iofile);
571     }
572     if (sp->altbuf)
573         xfree(sp->altbuf);
574     xfree(sp);
575     xfree(h);
576     return 0;
577 }
578
579 char *unix_addrstr(COMSTACK h)
580 {
581     unix_state *sp = (struct unix_state *)h->cprivate;
582     char *buf = sp->buf;
583     sprintf(buf, "unix:%s", sp->addr.sun_path);
584     return buf;
585 }
586
587 int static unix_set_blocking(COMSTACK p, int blocking)
588 {
589     unsigned long flag;
590
591     if (p->blocking == blocking)
592         return 1;
593     flag = fcntl(p->iofile, F_GETFL, 0);
594     if(!blocking)
595         flag = flag & ~O_NONBLOCK;
596     else
597         flag = flag | O_NONBLOCK;
598     if (fcntl(p->iofile, F_SETFL, flag) < 0)
599         return 0;
600     p->blocking = blocking;
601     return 1;
602 }
603 #endif