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