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