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