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