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