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