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