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