Uses preprocessor define WIN32 instead of WINDOWS to build code
[yaz-moved-to-github.git] / comstack / tcpip.c
1 /*
2  * Copyright (c) 1995-1999, Index Data
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: tcpip.c,v $
7  * Revision 1.27  1999-02-02 13:57:31  adam
8  * Uses preprocessor define WIN32 instead of WINDOWS to build code
9  * for Microsoft WIN32.
10  *
11  * Revision 1.26  1999/01/08 11:23:14  adam
12  * Added const modifier to some of the BER/ODR encoding routines.
13  *
14  * Revision 1.25  1998/07/07 15:49:23  adam
15  * Added braces to avoid warning.
16  *
17  * Revision 1.24  1998/06/29 07:59:17  adam
18  * Minor fix.
19  *
20  * Revision 1.23  1998/06/23 15:37:50  adam
21  * Added type cast to prevent warning.
22  *
23  * Revision 1.22  1998/06/22 11:32:36  adam
24  * Added 'conditional cs_listen' feature.
25  *
26  * Revision 1.21  1998/05/20 09:55:32  adam
27  * Function tcpip_get treats EINPROGRESS error in the same way as
28  * EWOULDBLOCK. EINPROGRESS shouldn't be returned - but it is on
29  * Solaris in some cases.
30  *
31  * Revision 1.20  1998/05/18 10:10:40  adam
32  * Minor change to avoid C++ warning.
33  *
34  * Revision 1.19  1998/02/11 11:53:33  adam
35  * Changed code so that it compiles as C++.
36  *
37  * Revision 1.18  1997/09/29 07:15:25  adam
38  * Changed use of setsockopt to avoid warnings on MSVC.
39  *
40  * Revision 1.17  1997/09/17 12:10:30  adam
41  * YAZ version 1.4.
42  *
43  * Revision 1.16  1997/09/01 08:49:14  adam
44  * New windows NT/95 port using MSV5.0. Minor changes only.
45  *
46  * Revision 1.15  1997/05/14 06:53:33  adam
47  * C++ support.
48  *
49  * Revision 1.14  1997/05/01 15:06:32  adam
50  * Moved WINSOCK init. code to tcpip_init routine.
51  *
52  * Revision 1.13  1996/11/01 08:45:18  adam
53  * Bug fix: used close on MS-Windows. Fixed to closesocket.
54  *
55  * Revision 1.12  1996/07/06 19:58:30  quinn
56  * System headerfiles gathered in yconfig
57  *
58  * Revision 1.11  1996/02/23  10:00:39  quinn
59  * WAIS Work
60  *
61  * Revision 1.10  1996/02/20  12:52:11  quinn
62  * WAIS protocol support.
63  *
64  * Revision 1.9  1996/02/10  12:23:11  quinn
65  * Enablie inetd operations fro TCP/IP stack
66  *
67  * Revision 1.8  1995/11/01  13:54:27  quinn
68  * Minor adjustments
69  *
70  * Revision 1.7  1995/10/30  12:41:16  quinn
71  * Added hostname lookup for server.
72  *
73  * Revision 1.6  1995/09/29  17:12:00  quinn
74  * Smallish
75  *
76  * Revision 1.5  1995/09/29  17:01:48  quinn
77  * More Windows work
78  *
79  * Revision 1.4  1995/09/28  10:12:26  quinn
80  * Windows-support changes
81  *
82  * Revision 1.3  1995/09/27  15:02:45  quinn
83  * Modified function heads & prototypes.
84  *
85  * Revision 1.2  1995/06/15  12:30:06  quinn
86  * Added @ as hostname alias for INADDR ANY.
87  *
88  * Revision 1.1  1995/06/14  09:58:20  quinn
89  * Renamed yazlib to comstack.
90  *
91  * Revision 1.20  1995/05/16  08:51:16  quinn
92  * License, documentation, and memory fixes
93  *
94  * Revision 1.19  1995/04/10  10:24:08  quinn
95  * Some bug-fixes.
96  *
97  * Revision 1.18  1995/03/30  13:29:27  quinn
98  * Added REUSEADDR in tcpip_bind
99  *
100  * Revision 1.17  1995/03/27  08:36:10  quinn
101  * Some work on nonblocking operation in xmosi.c and rfct.c.
102  * Added protocol parameter to cs_create()
103  *
104  * Revision 1.16  1995/03/21  15:53:41  quinn
105  * Added rcvconnect
106  *
107  * Revision 1.15  1995/03/21  12:31:27  quinn
108  * Added check for EINPROGRESS on connect.
109  *
110  * Revision 1.14  1995/03/20  09:47:21  quinn
111  * Added server-side support to xmosi.c
112  * Fixed possible problems in rfct
113  * Other little mods
114  *
115  * Revision 1.13  1995/03/15  16:15:13  adam
116  * Removed p_write.
117  *
118  * Revision 1.12  1995/03/15  15:36:27  quinn
119  * Mods to support nonblocking I/O
120  *
121  * Revision 1.11  1995/03/15  08:37:57  quinn
122  * Now we're pretty much set for nonblocking I/O.
123  *
124  * Revision 1.10  1995/03/14  17:00:07  quinn
125  * Bug-fixes - added tracing info to tcpip.c
126  *
127  * Revision 1.9  1995/03/14  10:28:42  quinn
128  * Adding server-side support to tcpip.c and fixing bugs in nonblocking I/O
129  *
130  * Revision 1.8  1995/03/10  14:22:50  quinn
131  * Removed debug output.
132  *
133  * Revision 1.7  1995/03/10  11:44:59  quinn
134  * Fixes and debugging
135  *
136  * Revision 1.6  1995/03/07  10:26:55  quinn
137  * Initialized type field in the comstacks.
138  *
139  * Revision 1.5  1995/02/14  20:40:07  quinn
140  * Various stuff.
141  *
142  * Revision 1.4  1995/02/14  11:54:49  quinn
143  * Beginning to add full CCL.
144  *
145  * Revision 1.3  1995/02/10  18:58:10  quinn
146  * Fixed tcpip_get (formerly tcpip_read).
147  * Turned tst (cli) into a proper, event-driven thingy.
148  *
149  * Revision 1.2  1995/02/10  15:55:47  quinn
150  * Small things.
151  *
152  * Revision 1.1  1995/02/09  15:51:52  quinn
153  * Works better now.
154  *
155  */
156
157 #include <stdio.h>
158 #include <string.h>
159 #include <stdlib.h>
160 #ifndef WIN32
161 #include <unistd.h>
162 #endif
163 #include <errno.h>
164 #include <fcntl.h>
165
166 #include <comstack.h>
167 #include <tcpip.h>
168
169 /* Chas added the following, so we get the definition of completeBER */
170 #include <odr.h>
171
172 int tcpip_close(COMSTACK h);
173 int tcpip_put(COMSTACK h, char *buf, int size);
174 int tcpip_get(COMSTACK h, char **buf, int *bufsize);
175 int tcpip_connect(COMSTACK h, void *address);
176 int tcpip_more(COMSTACK h);
177 int tcpip_rcvconnect(COMSTACK h);
178 int tcpip_bind(COMSTACK h, void *address, int mode);
179 int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
180                  int (*check_ip)(void *cd, const char *a, int len, int type),
181                  void *cd);
182
183 COMSTACK tcpip_accept(COMSTACK h);
184 char *tcpip_addrstr(COMSTACK h);
185 void *tcpip_straddr(COMSTACK h, const char *str);
186
187 #undef TRACE_TCPIP
188 #ifdef TRACE_TCPIP
189 #define TRC(x) x
190 #else
191 #define TRC(X)
192 #endif
193
194 typedef struct tcpip_state
195 {
196     char *altbuf; /* alternate buffer for surplus data */
197     int altsize;  /* size as xmalloced */
198     int altlen;   /* length of data or 0 if none */
199
200     int written;  /* -1 if we aren't writing */
201     int towrite;  /* to verify against user input */
202     int (*complete)(const unsigned char *buf, int len); /* length/comple. */
203     struct sockaddr_in addr;  /* returned by cs_straddr */
204     char buf[128]; /* returned by cs_addrstr */
205 } tcpip_state;
206
207 #ifdef WIN32
208 static int tcpip_init (void)
209 {
210     static int initialized = 0;
211     if (!initialized)
212     {
213         WORD requested;
214         WSADATA wd;
215
216         requested = MAKEWORD(1, 1);
217         if (WSAStartup(requested, &wd))
218             return 0;
219         initialized = 1;
220     }
221     return 1;
222 }
223 #else
224 static int tcpip_init (void)
225 {
226     return 1;
227 }
228 #endif
229 /*
230  * This function is always called through the cs_create() macro.
231  * s >= 0: socket has already been established for us.
232  */
233 COMSTACK tcpip_type(int s, int blocking, int protocol)
234 {
235     COMSTACK p;
236     tcpip_state *state;
237     int new_socket;
238 #ifdef WIN32
239     unsigned long tru = 1;
240 #else
241     struct protoent *proto;
242 #endif
243
244     if (!tcpip_init ())
245         return 0;
246     if (s < 0)
247     {
248 #ifndef WIN32
249         if (!(proto = getprotobyname("tcp")))
250             return 0;
251         if ((s = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0)
252 #else
253         if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
254 #endif
255             return 0;
256         new_socket = 1;
257     }
258     else
259         new_socket = 0;
260     if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
261         return 0;
262     if (!(state = (struct tcpip_state *)(p->cprivate =
263                                          xmalloc(sizeof(tcpip_state)))))
264         return 0;
265
266 #ifdef WIN32
267     if (!(p->blocking = blocking) && ioctlsocket(s, FIONBIO, &tru) < 0)
268 #else
269     if (!(p->blocking = blocking) && fcntl(s, F_SETFL, O_NONBLOCK) < 0)
270 #endif
271         return 0;
272
273     p->iofile = s;
274     p->type = tcpip_type;
275     p->protocol = (enum oid_proto) protocol;
276
277     p->f_connect = tcpip_connect;
278     p->f_rcvconnect = tcpip_rcvconnect;
279     p->f_get = tcpip_get;
280     p->f_put = tcpip_put;
281     p->f_close = tcpip_close;
282     p->f_more = tcpip_more;
283     p->f_bind = tcpip_bind;
284     p->f_listen = tcpip_listen;
285     p->f_accept = tcpip_accept;
286     p->f_addrstr = tcpip_addrstr;
287     p->f_straddr = tcpip_straddr;
288
289     p->state = new_socket ? CS_UNBND : CS_IDLE; /* state of line */
290     p->event = CS_NONE;
291     p->cerrno = 0;
292     p->stackerr = 0;
293
294     state->altbuf = 0;
295     state->altsize = state->altlen = 0;
296     state->towrite = state->written = -1;
297     if (protocol == PROTO_WAIS)
298         state->complete = completeWAIS;
299     else
300         state->complete = completeBER;
301
302     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
303     TRC(fprintf(stderr, "Created new TCPIP comstack\n"));
304
305     return p;
306 }
307
308 static int tcpip_strtoaddr_ex(const char *str, struct sockaddr_in *add)
309 {
310     struct hostent *hp;
311     char *p, buf[512];
312     short int port = 210;
313     unsigned tmpadd;
314
315     if (!tcpip_init ())
316         return 0;
317     TRC(fprintf(stderr, "tcpip_strtoaddress: %s\n", str ? str : "NULL"));
318     add->sin_family = AF_INET;
319     strcpy(buf, str);
320     if ((p = strchr(buf, ':')))
321     {
322         *p = 0;
323         port = atoi(p + 1);
324     }
325     add->sin_port = htons(port);
326     if (!strcmp("@", buf))
327         add->sin_addr.s_addr = INADDR_ANY;
328     else if ((hp = gethostbyname(buf)))
329         memcpy(&add->sin_addr.s_addr, *hp->h_addr_list,
330                sizeof(struct in_addr));
331     else if ((tmpadd = (unsigned) inet_addr(buf)) != 0)
332         memcpy(&add->sin_addr.s_addr, &tmpadd, sizeof(struct in_addr));
333     else
334         return 0;
335     return 1;
336 }
337
338 void *tcpip_straddr(COMSTACK h, const char *str)
339 {
340     tcpip_state *sp = (tcpip_state *)h->cprivate;
341
342     if (!tcpip_strtoaddr_ex (str, &sp->addr))
343         return 0;
344     return &sp->addr;
345 }
346
347 struct sockaddr_in *tcpip_strtoaddr(const char *str)
348 {
349     static struct sockaddr_in add;
350     
351     if (!tcpip_strtoaddr_ex (str, &add))
352         return 0;
353     return &add;
354 }
355
356 int tcpip_more(COMSTACK h)
357 {
358     tcpip_state *sp = (tcpip_state *)h->cprivate;
359     
360     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
361         sp->altlen);
362 }
363
364 /*
365  * connect(2) will block (sometimes) - nothing we can do short of doing
366  * weird things like spawning subprocesses or threading or some weird junk
367  * like that.
368  */
369 int tcpip_connect(COMSTACK h, void *address)
370 {
371     struct sockaddr_in *add = (struct sockaddr_in *)address;
372
373     TRC(fprintf(stderr, "tcpip_connect\n"));
374     if (connect(h->iofile, (struct sockaddr *) add, sizeof(*add)) < 0)
375     {
376 #ifdef WIN32
377         if (WSAGetLastError() == WSAEWOULDBLOCK)
378 #else
379         if (errno == EINPROGRESS)
380 #endif
381             return 1;
382         return -1;
383     }
384     h->state = CS_DATAXFER;
385     return 0;
386 }
387
388 /*
389  * nop
390  */
391 int tcpip_rcvconnect(COMSTACK h)
392 {
393     TRC(fprintf(stderr, "tcpip_rcvconnect\n"));
394     return 0;
395 }
396
397 int tcpip_bind(COMSTACK h, void *address, int mode)
398 {
399     struct sockaddr *addr = (struct sockaddr *)address;
400 #ifdef WIN32
401     BOOL one = 1;
402 #else
403     unsigned long one = 1;
404 #endif
405
406     TRC(fprintf(stderr, "tcpip_bind\n"));
407     if (setsockopt(h->iofile, SOL_SOCKET, SO_REUSEADDR, (char*) 
408         &one, sizeof(one)) < 0)
409     {
410         h->cerrno = CSYSERR;
411         return -1;
412     }
413     if (bind(h->iofile, addr, sizeof(struct sockaddr_in)) < 0)
414     {
415         h->cerrno = CSYSERR;
416         return -1;
417     }
418     if (mode == CS_SERVER && listen(h->iofile, 3) < 0)
419     {
420         h->cerrno = CSYSERR;
421         return -1;
422     }
423     h->state = CS_IDLE;
424     return 0;
425 }
426
427 #if 0
428 void tcpip_get_ip(COMSTACK h, char *ip_buf)
429 {
430     struct tcpip_state *sp = (tcpip_state *)h->cprivate;
431     const char *ip_addr = (const char *) (&sp->addr->sin_addr.s_addr);
432     int i;
433
434     for (i = 0; i<4; i++)
435         TRC (fprintf (stderr, "%u ", ip_addr[i]));
436     TRC (fprintf (stderr, "\n"));
437 }
438 #endif
439
440 int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
441                  int (*check_ip)(void *cd, const char *a, int len, int type),
442                  void *cd)
443 {
444     struct sockaddr_in addr;
445     int len = sizeof(addr);
446
447     TRC(fprintf(stderr, "tcpip_listen pid=%d\n", getpid()));
448     if (h->state != CS_IDLE)
449     {
450         h->cerrno = CSOUTSTATE;
451         return -1;
452     }
453     if ((h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len)) < 0)
454     {
455 #ifdef WIN32
456         if (WSAGetLastError() == WSAEWOULDBLOCK)
457 #else
458         if (errno == EWOULDBLOCK)
459 #endif
460
461             h->cerrno = CSNODATA;
462         else
463             h->cerrno = CSYSERR;
464         return -1;
465     }
466     if (addrlen && *addrlen >= sizeof(struct sockaddr_in))
467         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_in));
468     else if (addrlen)
469         *addrlen = 0;
470     if (check_ip && (*check_ip)(cd, (const char *) &addr.sin_addr,
471         sizeof(addr.sin_addr), AF_INET))
472     {
473         h->cerrno = CSDENY;
474 #ifdef WIN32
475         closesocket(h->iofile);
476 #else
477         close(h->iofile);
478 #endif
479         return -1;
480     }
481     h->state = CS_INCON;
482     return 0;
483 }
484
485 COMSTACK tcpip_accept(COMSTACK h)
486 {
487     COMSTACK cnew;
488     tcpip_state *state, *st = (tcpip_state *)h->cprivate;
489 #ifdef WIN32
490     unsigned long tru = 1;
491 #endif
492
493     TRC(fprintf(stderr, "tcpip_accept\n"));
494     if (h->state != CS_INCON)
495     {
496         h->cerrno = CSOUTSTATE;
497         return 0;
498     }
499     if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
500     {
501         h->cerrno = CSYSERR;
502         return 0;
503     }
504     memcpy(cnew, h, sizeof(*h));
505     cnew->iofile = h->newfd;
506     if (!(state = (tcpip_state *)(cnew->cprivate = xmalloc(sizeof(tcpip_state)))))
507     {
508         h->cerrno = CSYSERR;
509         return 0;
510     }
511 #ifdef WIN32
512     if (!cnew->blocking && ioctlsocket(cnew->iofile, FIONBIO, &tru) < 0)
513 #else
514     if (!cnew->blocking && fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
515 #endif
516         return 0;
517     state->altbuf = 0;
518     state->altsize = state->altlen = 0;
519     state->towrite = state->written = -1;
520     state->complete = st->complete;
521     cnew->state = CS_DATAXFER;
522     h->state = CS_IDLE;
523     return cnew;
524 }
525
526 #define CS_TCPIP_BUFCHUNK 4096
527
528 /*
529  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
530  * 0=connection closed.
531  */
532 int tcpip_get(COMSTACK h, char **buf, int *bufsize)
533 {
534     tcpip_state *sp = (tcpip_state *)h->cprivate;
535     char *tmpc;
536     int tmpi, berlen, rest, req, tomove;
537     int hasread = 0, res;
538
539     TRC(fprintf(stderr, "tcpip_get: bufsize=%d\n", *bufsize));
540     if (sp->altlen) /* switch buffers */
541     {
542         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
543             (unsigned) sp->altbuf));
544         tmpc = *buf;
545         tmpi = *bufsize;
546         *buf = sp->altbuf;
547         *bufsize = sp->altsize;
548         hasread = sp->altlen;
549         sp->altlen = 0;
550         sp->altbuf = tmpc;
551         sp->altsize = tmpi;
552     }
553     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
554     {
555         if (!*bufsize)
556         {
557             if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
558                 return -1;
559         }
560         else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
561             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
562                 return -1;
563         if ((res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0)) < 0)
564         {
565 #ifdef WIN32
566             if (WSAGetLastError() == WSAEWOULDBLOCK)
567 #else
568 #ifdef EINPROGRESS
569             if (errno == EINPROGRESS || errno == EWOULDBLOCK)
570 #else
571             if (errno == EWOULDBLOCK)
572 #endif
573 #endif
574                 break;
575             else
576                 return -1;
577         }
578         if (!res)
579             return 0;
580         hasread += res;
581         TRC(fprintf(stderr, "  res=%d, hasread=%d\n", res, hasread));
582     }
583     TRC(fprintf(stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
584         hasread, berlen));
585     /* move surplus buffer (or everything if we didn't get a BER rec.) */
586     if (hasread > berlen)
587     {
588         tomove = req = hasread - berlen;
589         rest = tomove % CS_TCPIP_BUFCHUNK;
590         if (rest)
591             req += CS_TCPIP_BUFCHUNK - rest;
592         if (!sp->altbuf)
593         {
594             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
595                 return -1;
596         } else if (sp->altsize < req)
597             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
598                 return -1;
599         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
600             (unsigned) sp->altbuf));
601         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
602     }
603     if (berlen < CS_TCPIP_BUFCHUNK - 1)
604         *(*buf + berlen) = '\0';
605     return berlen ? berlen : 1;
606 }
607
608 /*
609  * Returns 1, 0 or -1
610  * In nonblocking mode, you must call again with same buffer while
611  * return value is 1.
612  */
613 int tcpip_put(COMSTACK h, char *buf, int size)
614 {
615     int res;
616     struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
617
618     TRC(fprintf(stderr, "tcpip_put: size=%d\n", size));
619     if (state->towrite < 0)
620     {
621         state->towrite = size;
622         state->written = 0;
623     }
624     else if (state->towrite != size)
625     {
626         h->cerrno = CSWRONGBUF;
627         return -1;
628     }
629     while (state->towrite > state->written)
630     {
631         if ((res = send(h->iofile, buf + state->written, size -
632             state->written, 0)) < 0)
633         {
634 #ifdef WIN32
635             if (WSAGetLastError() == WSAEWOULDBLOCK)
636 #else
637             if (errno == EAGAIN)
638 #endif
639             {
640                 TRC(fprintf(stderr, "  Flow control stop\n"));
641                 return 1;
642             }
643             h->cerrno = CSYSERR;
644             return -1;
645         }
646         state->written += res;
647         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
648             res, state->written, size));
649     }
650     state->towrite = state->written = -1;
651     TRC(fprintf(stderr, "  Ok\n"));
652     return 0;
653 }
654
655 int tcpip_close(COMSTACK h)
656 {
657     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
658
659     TRC(fprintf(stderr, "tcpip_close\n"));
660     if (h->iofile != -1)
661 #ifdef WIN32
662         closesocket(h->iofile);
663 #else
664         close(h->iofile);
665 #endif
666     if (sp->altbuf)
667         xfree(sp->altbuf);
668     xfree(sp);
669     xfree(h);
670     return 0;
671 }
672
673 char *tcpip_addrstr(COMSTACK h)
674 {
675     struct sockaddr_in addr;
676     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
677     char *r, *buf = sp->buf;
678     int len;
679     struct hostent *host;
680     
681     len = sizeof(addr);
682     if (getpeername(h->iofile, (struct sockaddr*) &addr, &len) < 0)
683     {
684         h->cerrno = CSYSERR;
685         return 0;
686     }
687     if ((host = gethostbyaddr((char*)&addr.sin_addr, sizeof(addr.sin_addr),
688                               AF_INET)))
689         r = (char*) host->h_name;
690     else
691         r = inet_ntoa(addr.sin_addr);
692     sprintf(buf, "tcp:%s", r);
693     return buf;
694 }