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