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