WAIS Work
[yaz-moved-to-github.git] / comstack / tcpip.c
1 /*
2  * Copyright (c) 1995, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: tcpip.c,v $
7  * Revision 1.11  1996-02-23 10:00:39  quinn
8  * WAIS Work
9  *
10  * Revision 1.10  1996/02/20  12:52:11  quinn
11  * WAIS protocol support.
12  *
13  * Revision 1.9  1996/02/10  12:23:11  quinn
14  * Enablie inetd operations fro TCP/IP stack
15  *
16  * Revision 1.8  1995/11/01  13:54:27  quinn
17  * Minor adjustments
18  *
19  * Revision 1.7  1995/10/30  12:41:16  quinn
20  * Added hostname lookup for server.
21  *
22  * Revision 1.6  1995/09/29  17:12:00  quinn
23  * Smallish
24  *
25  * Revision 1.5  1995/09/29  17:01:48  quinn
26  * More Windows work
27  *
28  * Revision 1.4  1995/09/28  10:12:26  quinn
29  * Windows-support changes
30  *
31  * Revision 1.3  1995/09/27  15:02:45  quinn
32  * Modified function heads & prototypes.
33  *
34  * Revision 1.2  1995/06/15  12:30:06  quinn
35  * Added @ as hostname alias for INADDR ANY.
36  *
37  * Revision 1.1  1995/06/14  09:58:20  quinn
38  * Renamed yazlib to comstack.
39  *
40  * Revision 1.20  1995/05/16  08:51:16  quinn
41  * License, documentation, and memory fixes
42  *
43  * Revision 1.19  1995/04/10  10:24:08  quinn
44  * Some bug-fixes.
45  *
46  * Revision 1.18  1995/03/30  13:29:27  quinn
47  * Added REUSEADDR in tcpip_bind
48  *
49  * Revision 1.17  1995/03/27  08:36:10  quinn
50  * Some work on nonblocking operation in xmosi.c and rfct.c.
51  * Added protocol parameter to cs_create()
52  *
53  * Revision 1.16  1995/03/21  15:53:41  quinn
54  * Added rcvconnect
55  *
56  * Revision 1.15  1995/03/21  12:31:27  quinn
57  * Added check for EINPROGRESS on connect.
58  *
59  * Revision 1.14  1995/03/20  09:47:21  quinn
60  * Added server-side support to xmosi.c
61  * Fixed possible problems in rfct
62  * Other little mods
63  *
64  * Revision 1.13  1995/03/15  16:15:13  adam
65  * Removed p_write.
66  *
67  * Revision 1.12  1995/03/15  15:36:27  quinn
68  * Mods to support nonblocking I/O
69  *
70  * Revision 1.11  1995/03/15  08:37:57  quinn
71  * Now we're pretty much set for nonblocking I/O.
72  *
73  * Revision 1.10  1995/03/14  17:00:07  quinn
74  * Bug-fixes - added tracing info to tcpip.c
75  *
76  * Revision 1.9  1995/03/14  10:28:42  quinn
77  * Adding server-side support to tcpip.c and fixing bugs in nonblocking I/O
78  *
79  * Revision 1.8  1995/03/10  14:22:50  quinn
80  * Removed debug output.
81  *
82  * Revision 1.7  1995/03/10  11:44:59  quinn
83  * Fixes and debugging
84  *
85  * Revision 1.6  1995/03/07  10:26:55  quinn
86  * Initialized type field in the comstacks.
87  *
88  * Revision 1.5  1995/02/14  20:40:07  quinn
89  * Various stuff.
90  *
91  * Revision 1.4  1995/02/14  11:54:49  quinn
92  * Beginning to add full CCL.
93  *
94  * Revision 1.3  1995/02/10  18:58:10  quinn
95  * Fixed tcpip_get (formerly tcpip_read).
96  * Turned tst (cli) into a proper, event-driven thingy.
97  *
98  * Revision 1.2  1995/02/10  15:55:47  quinn
99  * Small things.
100  *
101  * Revision 1.1  1995/02/09  15:51:52  quinn
102  * Works better now.
103  *
104  */
105
106 #include <stdio.h>
107 #include <string.h>
108 #include <stdlib.h>
109 #include <unistd.h>
110 #include <errno.h>
111 #include <fcntl.h>
112
113 #include <comstack.h>
114 #include <tcpip.h>
115
116 #ifndef WINDOWS
117 #include <sys/time.h>
118 #endif
119
120 int tcpip_close(COMSTACK h);
121 int tcpip_put(COMSTACK h, char *buf, int size);
122 int tcpip_get(COMSTACK h, char **buf, int *bufsize);
123 int tcpip_connect(COMSTACK h, void *address);
124 int tcpip_more(COMSTACK h);
125 int tcpip_rcvconnect(COMSTACK h);
126 int tcpip_bind(COMSTACK h, void *address, int mode);
127 int tcpip_listen(COMSTACK h, char *addrp, int *addrlen);
128 COMSTACK tcpip_accept(COMSTACK h);
129 char *tcpip_addrstr(COMSTACK h);
130
131 /*
132  * Determine length/completeness of incoming packages
133  */
134 int completeBER(unsigned char *buf, int len); /* from the ODR module */
135 int completeWAIS(unsigned char *buf, int len); /* from waislen.c */
136
137 #ifdef TRACE_TCPIP
138 #define TRC(x) x
139 #else
140 #define TRC(X)
141 #endif
142
143 static int initialized = 0;
144
145 typedef struct tcpip_state
146 {
147     char *altbuf; /* alternate buffer for surplus data */
148     int altsize;  /* size as xmalloced */
149     int altlen;   /* length of data or 0 if none */
150
151     int written;  /* -1 if we aren't writing */
152     int towrite;  /* to verify against user input */
153     int (*complete)(unsigned char *buf, int len); /* length/completeness */
154 } tcpip_state;
155
156 /*
157  * This function is always called through the cs_create() macro.
158  * s >= 0: socket has already been established for us.
159  */
160 COMSTACK tcpip_type(int s, int blocking, int protocol)
161 {
162     COMSTACK p;
163     tcpip_state *state;
164     int new_socket;
165 #ifdef WINDOWS
166     unsigned long tru = 1;
167 #else
168     struct protoent *proto;
169 #endif
170
171     if (!initialized)
172     {
173 #ifdef WINDOWS
174         WORD requested;
175         WSADATA wd;
176
177         requested = MAKEWORD(1, 1);
178         if (WSAStartup(requested, &wd))
179             return 0;
180 #endif
181         initialized = 1;
182     }
183
184     if (s < 0)
185     {
186 #ifndef WINDOWS
187         if (!(proto = getprotobyname("tcp")))
188             return 0;
189         if ((s = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
190 #else
191         if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
192 #endif
193             return 0;
194         new_socket = 1;
195     }
196     else
197         new_socket = 0;
198     if (!(p = xmalloc(sizeof(struct comstack))))
199         return 0;
200     if (!(state = p->private = xmalloc(sizeof(tcpip_state))))
201         return 0;
202
203 #ifdef WINDOWS
204     if (!(p->blocking = blocking) && ioctlsocket(s, FIONBIO, &tru) < 0)
205 #else
206     if (!(p->blocking = blocking) && fcntl(s, F_SETFL, O_NONBLOCK) < 0)
207 #endif
208         return 0;
209
210     p->iofile = s;
211     p->type = tcpip_type;
212     p->protocol = protocol;
213
214     p->f_connect = tcpip_connect;
215     p->f_rcvconnect = tcpip_rcvconnect;
216     p->f_get = tcpip_get;
217     p->f_put = tcpip_put;
218     p->f_close = tcpip_close;
219     p->f_more = tcpip_more;
220     p->f_bind = tcpip_bind;
221     p->f_listen = tcpip_listen;
222     p->f_accept = tcpip_accept;
223     p->f_addrstr = tcpip_addrstr;
224
225     p->state = new_socket ? CS_UNBND : CS_IDLE; /* state of line */
226     p->event = CS_NONE;
227     p->cerrno = 0;
228     p->stackerr = 0;
229
230     state->altbuf = 0;
231     state->altsize = state->altlen = 0;
232     state->towrite = state->written = -1;
233     if (protocol == PROTO_WAIS)
234         state->complete = completeWAIS;
235     else
236         state->complete = completeBER;
237
238     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
239     TRC(fprintf(stderr, "Created new TCPIP comstack\n"));
240
241     return p;
242 }
243
244 struct sockaddr_in *tcpip_strtoaddr(const char *str)
245 {
246     static struct sockaddr_in add;
247     struct hostent *hp;
248     char *p, buf[512];
249     short int port = 210;
250     unsigned tmpadd;
251
252     TRC(fprintf(stderr, "tcpip_strtoaddress: %s\n", str ? str : "NULL"));
253     add.sin_family = AF_INET;
254     strcpy(buf, str);
255     if ((p = strchr(buf, ':')))
256     {
257         *p = 0;
258         port = atoi(p + 1);
259     }
260     add.sin_port = htons(port);
261     if (!strcmp("@", buf))
262         add.sin_addr.s_addr = INADDR_ANY;
263     else if ((hp = gethostbyname(buf)))
264         memcpy(&add.sin_addr.s_addr, *hp->h_addr_list, sizeof(struct in_addr));
265     else if ((tmpadd = (unsigned) inet_addr(buf)) != 0)
266         memcpy(&add.sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
267     else
268         return 0;
269     return &add;
270 }
271
272 int tcpip_more(COMSTACK h)
273 {
274     tcpip_state *sp = h->private;
275
276     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
277         sp->altlen);
278 }
279
280 /*
281  * connect(2) will block (sometimes) - nothing we can do short of doing
282  * weird things like spawning subprocesses or threading or some weird junk
283  * like that.
284  */
285 int tcpip_connect(COMSTACK h, void *address)
286 {
287     struct sockaddr_in *add = address;
288
289     TRC(fprintf(stderr, "tcpip_connect\n"));
290     if (connect(h->iofile, (struct sockaddr *) add, sizeof(*add)) < 0)
291     {
292 #ifdef WINDOWS
293         if (WSAGetLastError() == WSAEWOULDBLOCK)
294 #else
295         if (errno == EINPROGRESS)
296 #endif
297             return 1;
298         return -1;
299     }
300     h->state = CS_DATAXFER;
301     return 0;
302 }
303
304 /*
305  * nop
306  */
307 int tcpip_rcvconnect(COMSTACK h)
308 {
309     TRC(fprintf(stderr, "tcpip_rcvconnect\n"));
310     return 0;
311 }
312
313 int tcpip_bind(COMSTACK h, void *address, int mode)
314 {
315     struct sockaddr *addr = address;
316     unsigned long one = 1;
317
318     TRC(fprintf(stderr, "tcpip_bind\n"));
319     if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
320     {
321         h->cerrno = CSYSERR;
322         return -1;
323     }
324     if (bind(h->iofile, addr, sizeof(struct sockaddr_in)) < 0)
325     {
326         h->cerrno = CSYSERR;
327         return -1;
328     }
329     if (mode == CS_SERVER && listen(h->iofile, 3) < 0)
330     {
331         h->cerrno = CSYSERR;
332         return -1;
333     }
334     h->state = CS_IDLE;
335     return 0;
336 }
337
338 int tcpip_listen(COMSTACK h, char *raddr, int *addrlen)
339 {
340     struct sockaddr_in addr;
341     int len = sizeof(addr);
342
343     TRC(fprintf(stderr, "tcpip_listen\n"));
344     if (h->state != CS_IDLE)
345     {
346         h->cerrno = CSOUTSTATE;
347         return -1;
348     }
349     if ((h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len)) < 0)
350     {
351 #ifdef WINDOWS
352         if (WSAGetLastError() == WSAEWOULDBLOCK)
353 #else
354         if (errno == EWOULDBLOCK)
355 #endif
356
357             h->cerrno = CSNODATA;
358         else
359             h->cerrno = CSYSERR;
360         return -1;
361     }
362     if (addrlen && *addrlen > sizeof(struct sockaddr_in))
363         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in));
364     else if (addrlen)
365         *addrlen = 0;
366     h->state = CS_INCON;
367     return 0;
368 }
369
370 COMSTACK tcpip_accept(COMSTACK h)
371 {
372     COMSTACK new;
373     tcpip_state *state, *st = h->private;
374 #ifdef WINDOWS
375     unsigned long tru = 1;
376 #endif
377
378     TRC(fprintf(stderr, "tcpip_accept\n"));
379     if (h->state != CS_INCON)
380     {
381         h->cerrno = CSOUTSTATE;
382         return 0;
383     }
384     if (!(new = xmalloc(sizeof(*new))))
385     {
386         h->cerrno = CSYSERR;
387         return 0;
388     }
389     memcpy(new, h, sizeof(*h));
390     new->iofile = h->newfd;
391     if (!(state = new->private = xmalloc(sizeof(tcpip_state))))
392     {
393         h->cerrno = CSYSERR;
394         return 0;
395     }
396 #ifdef WINDOWS
397     if (!new->blocking && ioctlsocket(new->iofile, FIONBIO, &tru) < 0)
398 #else
399     if (!new->blocking && fcntl(new->iofile, F_SETFL, O_NONBLOCK) < 0)
400 #endif
401         return 0;
402     state->altbuf = 0;
403     state->altsize = state->altlen = 0;
404     state->towrite = state->written = -1;
405     state->complete = st->complete;
406     new->state = CS_DATAXFER;
407     h->state = CS_IDLE;
408     return new;
409 }
410
411 #define CS_TCPIP_BUFCHUNK 4096
412
413 /*
414  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
415  * 0=connection closed.
416  */
417 int tcpip_get(COMSTACK h, char **buf, int *bufsize)
418 {
419     tcpip_state *sp = h->private;
420     char *tmpc;
421     int tmpi, berlen, rest, req, tomove;
422     int hasread = 0, res;
423
424     TRC(fprintf(stderr, "tcpip_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     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
439     {
440         if (!*bufsize)
441         {
442             if (!(*buf = xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
443                 return -1;
444         }
445         else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
446             if (!(*buf =xrealloc(*buf, *bufsize *= 2)))
447                 return -1;
448         if ((res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0)) < 0)
449 #ifdef WINDOWS
450             if (WSAGetLastError() == WSAEWOULDBLOCK)
451 #else
452             if (errno == EWOULDBLOCK)
453 #endif
454                 break;
455             else
456                 return -1;
457         if (!res)
458             return 0;
459         hasread += res;
460         TRC(fprintf(stderr, "  res=%d, hasread=%d\n", res, hasread));
461     }
462     TRC(fprintf(stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
463         hasread, berlen));
464     /* move surplus buffer (or everything if we didn't get a BER rec.) */
465     if (hasread > berlen)
466     {
467         tomove = req = hasread - berlen;
468         rest = tomove % CS_TCPIP_BUFCHUNK;
469         if (rest)
470             req += CS_TCPIP_BUFCHUNK - rest;
471         if (!sp->altbuf)
472         {
473             if (!(sp->altbuf = xmalloc(sp->altsize = req)))
474                 return -1;
475         } else if (sp->altsize < req)
476             if (!(sp->altbuf =xrealloc(sp->altbuf, sp->altsize = req)))
477                 return -1;
478         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
479             (unsigned) sp->altbuf));
480         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
481     }
482     if (berlen < CS_TCPIP_BUFCHUNK - 1)
483         *(*buf + berlen) = '\0';
484     return berlen ? berlen : 1;
485 }
486
487 /*
488  * Returns 1, 0 or -1
489  * In nonblocking mode, you must call again with same buffer while
490  * return value is 1.
491  */
492 int tcpip_put(COMSTACK h, char *buf, int size)
493 {
494     int res;
495     struct tcpip_state *state = h->private;
496
497     TRC(fprintf(stderr, "tcpip_put: size=%d\n", size));
498     if (state->towrite < 0)
499     {
500         state->towrite = size;
501         state->written = 0;
502     }
503     else if (state->towrite != size)
504     {
505         h->cerrno = CSWRONGBUF;
506         return -1;
507     }
508     while (state->towrite > state->written)
509     {
510         if ((res = send(h->iofile, buf + state->written, size -
511             state->written, 0)) < 0)
512         {
513 #ifdef WINDOWS
514             if (WSAGetLastError() == WSAEWOULDBLOCK)
515 #else
516             if (errno == EAGAIN)
517 #endif
518             {
519                 TRC(fprintf(stderr, "  Flow control stop\n"));
520                 return 1;
521             }
522             h->cerrno = CSYSERR;
523             return -1;
524         }
525         state->written += res;
526         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
527             res, state->written, size));
528     }
529     state->towrite = state->written = -1;
530     TRC(fprintf(stderr, "  Ok\n"));
531     return 0;
532 }
533
534 int tcpip_close(COMSTACK h)
535 {
536     tcpip_state *sp = h->private;
537
538     TRC(fprintf(stderr, "tcpip_close\n"));
539     close(h->iofile);
540     if (sp->altbuf)
541         xfree(sp->altbuf);
542     xfree(sp);
543     xfree(h);
544     return 0;
545 }
546
547 char *tcpip_addrstr(COMSTACK h)
548 {
549     struct sockaddr_in addr;
550     static char buf[64];
551     char *r;
552     int len;
553     struct hostent *host;
554
555     len = sizeof(addr);
556     if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0)
557     {
558         h->cerrno = CSYSERR;
559         return 0;
560     }
561     if ((host = gethostbyaddr((char*)&addr.sin_addr, sizeof(addr.sin_addr),
562         AF_INET)))
563         r = (char*) host->h_name;
564     else
565         r = inet_ntoa(addr.sin_addr);
566     sprintf(buf, "tcp:%s", r);
567     return buf;
568 }