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