source: trunk/FACT++/dim/src/tcpip.c@ 13135

Last change on this file since 13135 was 13135, checked in by tbretz, 13 years ago
Updated to v19r30
File size: 33.0 KB
Line 
1/*
2 * DNA (Delphi Network Access) implements the network layer for the DIM
3 * (Delphi Information Managment) System.
4 *
5 * Started : 10-11-91
6 * Last modification : 29-07-94
7 * Written by : C. Gaspar
8 * Adjusted by : G.C. Ballintijn
9 *
10 */
11
12/*
13#define DEBUG
14*/
15
16/* Modifies the number of open connections to 8192 for Windows and Linux */
17/* Can not be moved from here ! */
18#include <dim_tcpip.h>
19
20#ifdef WIN32
21#define ioctl ioctlsocket
22
23#define closesock myclosesocket
24#define readsock recv
25#define writesock send
26
27#define EINTR WSAEINTR
28#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
29#define EWOULDBLOCK WSAEWOULDBLOCK
30#define ECONNREFUSED WSAECONNREFUSED
31#define HOST_NOT_FOUND WSAHOST_NOT_FOUND
32#define NO_DATA WSANO_DATA
33
34#else
35/*
36#define closesock(s) shutdown(s,2)
37*/
38#define closesock(s) close(s)
39#define readsock(a,b,c,d) read(a,b,c)
40
41#if defined(__linux__) && !defined (darwin)
42#define writesock(a,b,c,d) send(a,b,c,MSG_NOSIGNAL)
43#else
44#define writesock(a,b,c,d) write(a,b,c)
45#endif
46
47#ifdef solaris
48#define BSD_COMP
49/*
50#include <thread.h>
51*/
52#endif
53
54#ifdef LYNXOS
55#ifdef RAID
56typedef int pid_t;
57#endif
58#endif
59
60#include <ctype.h>
61#include <sys/socket.h>
62#include <fcntl.h>
63#include <netinet/in.h>
64#include <netinet/tcp.h>
65#include <signal.h>
66#include <sys/ioctl.h>
67#include <errno.h>
68#include <netdb.h>
69#endif
70
71#include <stdio.h>
72#include <time.h>
73#define DIMLIB
74#include <dim.h>
75
76#define ushort unsigned short
77
78static int Threads_on = 0;
79
80static int init_done = FALSE; /* Is this module initialized? */
81static int queue_id = 0;
82
83#ifdef WIN32
84static struct sockaddr_in DIM_sockname;
85#endif
86
87static int DIM_IO_path[2] = {-1,-1};
88static int DIM_IO_Done = 0;
89static int DIM_IO_valid = 1;
90
91static int Listen_backlog = SOMAXCONN;
92static int Keepalive_timeout_set = 0;
93static int Write_timeout = WRITE_TMOUT;
94static int Write_timeout_set = 0;
95static int Write_buffer_size = TCP_SND_BUF_SIZE;
96static int Read_buffer_size = TCP_RCV_BUF_SIZE;
97
98int Tcpip_max_io_data_write = TCP_SND_BUF_SIZE - 16;
99int Tcpip_max_io_data_read = TCP_RCV_BUF_SIZE - 16;
100
101void dim_set_listen_backlog(int size)
102{
103 Listen_backlog = size;
104}
105
106int dim_get_listen_backlog()
107{
108 return(Listen_backlog);
109}
110
111void dim_set_keepalive_timeout(int secs)
112{
113 Keepalive_timeout_set = secs;
114}
115
116int dim_get_keepalive_timeout()
117{
118 int ret;
119 extern int get_keepalive_tmout();
120
121 if(!(ret = Keepalive_timeout_set))
122 {
123 ret = get_keepalive_tmout();
124 }
125 return(ret);
126}
127
128void dim_set_write_timeout(int secs)
129{
130 Write_timeout = secs;
131 Write_timeout_set = 1;
132}
133
134int dim_get_write_timeout()
135{
136 int ret;
137 extern int get_write_tmout();
138
139 if(!Write_timeout_set)
140 {
141 if((ret = get_write_tmout()))
142 Write_timeout = ret;
143 }
144 return(Write_timeout);
145}
146
147int dim_set_write_buffer_size(int size)
148{
149 if(size >= TCP_SND_BUF_SIZE)
150 {
151 Write_buffer_size = size;
152 Tcpip_max_io_data_write = size - 16;
153 return(1);
154 }
155 return(0);
156}
157
158int dim_get_write_buffer_size()
159{
160 return(Write_buffer_size);
161}
162
163int dim_set_read_buffer_size(int size)
164{
165 if(size >= TCP_RCV_BUF_SIZE)
166 {
167 Read_buffer_size = size;
168 Tcpip_max_io_data_read = size - 16;
169 return(1);
170 }
171 return(0);
172}
173
174int dim_get_read_buffer_size()
175{
176 return(Read_buffer_size);
177}
178
179#ifdef WIN32
180int init_sock()
181{
182 WORD wVersionRequested;
183 WSADATA wsaData;
184 int err;
185 static int sock_init_done = 0;
186
187 if(sock_init_done) return(1);
188 wVersionRequested = MAKEWORD( 2, 0 );
189 err = WSAStartup( wVersionRequested, &wsaData );
190
191 if ( err != 0 )
192 {
193 return(0);
194 }
195
196 /* Confirm that the WinSock DLL supports 2.0.*/
197 /* Note that if the DLL supports versions greater */
198 /* than 2.0 in addition to 2.0, it will still return */
199 /* 2.0 in wVersion since that is the version we */
200 /* requested. */
201
202 if ( LOBYTE( wsaData.wVersion ) != 2 ||
203 HIBYTE( wsaData.wVersion ) != 0 )
204 {
205 WSACleanup( );
206 return(0);
207 }
208 sock_init_done = 1;
209 return(1);
210}
211
212int myclosesocket(int path)
213{
214 int code, ret;
215 code = WSAGetLastError();
216 ret = closesocket(path);
217 WSASetLastError(code);
218 return ret;
219}
220#endif
221
222int dim_tcpip_init(int thr_flag)
223{
224#ifdef WIN32
225 int addr, flags = 1;
226/*
227 void tcpip_task();
228*/
229 void create_io_thread(void);
230#else
231 struct sigaction sig_info;
232 sigset_t set;
233 void io_sig_handler();
234 void dummy_io_sig_handler();
235 void tcpip_pipe_sig_handler();
236#endif
237 extern int get_write_tmout();
238
239 if(init_done)
240 return(1);
241
242 dim_get_write_timeout();
243#ifdef WIN32
244 init_sock();
245 Threads_on = 1;
246#else
247 if(thr_flag)
248 {
249 Threads_on = 1;
250 }
251 else
252 {
253 sigemptyset(&set);
254
255 sigaddset(&set,SIGALRM);
256 sig_info.sa_handler = io_sig_handler;
257 sig_info.sa_mask = set;
258#ifndef LYNXOS
259 sig_info.sa_flags = SA_RESTART;
260#else
261 sig_info.sa_flags = 0;
262#endif
263
264 if( sigaction(SIGIO, &sig_info, 0) < 0 )
265 {
266 perror( "sigaction(SIGIO)" );
267 exit(1);
268 }
269
270 sigemptyset(&set);
271 sig_info.sa_handler = tcpip_pipe_sig_handler;
272 sig_info.sa_mask = set;
273#ifndef LYNXOS
274 sig_info.sa_flags = SA_RESTART;
275#else
276 sig_info.sa_flags = 0;
277#endif
278
279 if( sigaction(SIGPIPE, &sig_info, 0) < 0 ) {
280 perror( "sigaction(SIGPIPE)" );
281 exit(1);
282 }
283
284 }
285#endif
286 if(Threads_on)
287 {
288#ifdef WIN32
289 if(DIM_IO_path[0] == -1)
290 {
291 if( (DIM_IO_path[0] = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
292 {
293 perror("socket");
294 return(0);
295 }
296
297 DIM_sockname.sin_family = PF_INET;
298 addr = 0;
299 DIM_sockname.sin_addr = *((struct in_addr *) &addr);
300 DIM_sockname.sin_port = htons((ushort) 2000);
301 ioctl(DIM_IO_path[0], FIONBIO, &flags);
302 }
303#else
304 if(DIM_IO_path[0] == -1)
305 {
306 pipe(DIM_IO_path);
307 }
308#endif
309 }
310 if(!queue_id)
311 queue_id = dtq_create();
312
313#ifdef WIN32
314/*
315#ifndef STDCALL
316 tid = _beginthread((void *)(void *)tcpip_task,0,NULL);
317#else
318 tid = _beginthreadex(NULL, NULL,
319 tcpip_task,0,0,NULL);
320#endif
321*/
322 create_io_thread();
323#endif
324 init_done = 1;
325 return(1);
326}
327
328void dim_tcpip_stop()
329{
330#ifdef WIN32
331 closesock(DIM_IO_path[0]);
332#else
333 close(DIM_IO_path[0]);
334 close(DIM_IO_path[1]);
335#endif
336 DIM_IO_path[0] = -1;
337 DIM_IO_path[1] = -1;
338 DIM_IO_Done = 0;
339 init_done = 0;
340}
341
342static int enable_sig(int conn_id)
343{
344 int ret = 1, flags = 1;
345#ifndef WIN32
346 int pid;
347#endif
348
349#ifdef DEBUG
350 if(!Net_conns[conn_id].channel)
351 {
352 printf("Enabling signals on channel 0\n");
353 fflush(stdout);
354 }
355#endif
356
357 if(!init_done)
358 {
359 dim_tcpip_init(0);
360 }
361 if(Threads_on)
362 {
363#ifdef WIN32
364 DIM_IO_valid = 0;
365/*
366 ret = connect(DIM_IO_path[0], (struct sockaddr*)&DIM_sockname, sizeof(DIM_sockname));
367*/
368 closesock(DIM_IO_path[0]);
369 DIM_IO_path[0] = -1;
370 if( (DIM_IO_path[0] = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
371 {
372 perror("socket");
373 return(1);
374 }
375 ret = ioctl(DIM_IO_path[0], FIONBIO, &flags);
376 if(ret != 0)
377 {
378 perror("ioctlsocket");
379 }
380 DIM_IO_valid = 1;
381#else
382 if(DIM_IO_path[1] != -1)
383 {
384 if(!DIM_IO_Done)
385 {
386 DIM_IO_Done = 1;
387 write(DIM_IO_path[1], &flags, 4);
388 }
389 }
390#endif
391 }
392#ifndef WIN32
393 if(!Threads_on)
394 {
395 pid = getpid();
396
397#ifndef __linux__
398 ret = ioctl(Net_conns[conn_id].channel, SIOCSPGRP, &pid );
399#else
400 ret = fcntl(Net_conns[conn_id].channel,F_SETOWN, pid);
401#endif
402 if(ret == -1)
403 {
404#ifdef DEBUG
405 printf("ioctl returned -1\n");
406#endif
407 return(ret);
408 }
409 }
410 ret = ioctl(Net_conns[conn_id].channel, FIOASYNC, &flags );
411 if(ret == -1)
412 {
413#ifdef DEBUG
414 printf("ioctl1 returned -1\n");
415#endif
416 return(ret);
417 }
418
419 flags = fcntl(Net_conns[conn_id].channel,F_GETFD,0);
420#ifdef DEBUG
421 if(flags == -1)
422 {
423 printf("error\n");
424 }
425#endif
426 ret = fcntl(Net_conns[conn_id].channel,F_SETFD, flags | FD_CLOEXEC );
427 if(ret == -1)
428 {
429#ifdef DEBUG
430 printf("ioctl2 returned -1\n");
431#endif
432 return(ret);
433 }
434#endif
435 return(1);
436}
437
438/*
439static void dump_list()
440{
441 int i;
442
443 for( i = 1; i < Curr_N_Conns; i++ )
444 if( Dna_conns[i].busy ) {
445 printf( "dump_list: conn_id=%d reading=%d\n",
446 i, Net_conns[i].reading );
447 }
448}
449*/
450
451static int list_to_fds( fd_set *fds )
452{
453 int i;
454 int found = 0;
455
456 FD_ZERO( fds ) ;
457 for( i = 1; i < Curr_N_Conns; i++ )
458 {
459 if( Dna_conns[i].busy )
460 {
461 if(Net_conns[i].channel)
462 {
463 found = 1;
464 FD_SET( Net_conns[i].channel, fds );
465
466 }
467 }
468 }
469 return(found);
470}
471
472static int fds_get_entry( fd_set *fds, int *conn_id )
473{
474 int i;
475
476 for( i = 1; i < Curr_N_Conns; i++ )
477 {
478 if( Dna_conns[i].busy &&
479 FD_ISSET(Net_conns[i].channel, fds) )
480 {
481 if(Net_conns[i].channel)
482 {
483 *conn_id = i;
484 return 1;
485 }
486 }
487 }
488 return 0;
489}
490
491#if defined(__linux__) && !defined (darwin)
492
493void tcpip_set_keepalive( int channel, int tmout )
494{
495 int val;
496
497 /* Enable keepalive for the given channel */
498 val = 1;
499 setsockopt(channel, SOL_SOCKET, SO_KEEPALIVE, (char*)&val, sizeof(val));
500
501 /* Set the keepalive poll interval to something small.
502 Warning: this section may not be portable! */
503 val = tmout;
504 setsockopt(channel, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&val, sizeof(val));
505 val = 3;
506 setsockopt(channel, IPPROTO_TCP, TCP_KEEPCNT, (char*)&val, sizeof(val));
507 val = tmout/3;
508 setsockopt(channel, IPPROTO_TCP, TCP_KEEPINTVL, (char*)&val, sizeof(val));
509}
510
511#else
512
513static void tcpip_test_write( int conn_id )
514{
515 /* Write to every socket we use, which uses the TCPIP protocol,
516 * which has an established connection (reading), which is currently
517 * not writing data, so we can check if it is still alive.
518 */
519 time_t cur_time;
520
521 if(strcmp(Net_conns[conn_id].node,"MYNODE"))
522 {
523 cur_time = time(NULL);
524 if( cur_time - Net_conns[conn_id].last_used > Net_conns[conn_id].timeout )
525 {
526 dna_test_write( conn_id );
527 }
528 }
529}
530
531#endif
532
533void tcpip_set_test_write(int conn_id, int timeout)
534{
535
536#if defined(__linux__) && !defined (darwin)
537 tcpip_set_keepalive(Net_conns[conn_id].channel, timeout);
538#else
539
540 Net_conns[conn_id].timr_ent = dtq_add_entry( queue_id, timeout,
541 tcpip_test_write, conn_id );
542 Net_conns[conn_id].timeout = timeout;
543 Net_conns[conn_id].last_used = time(NULL);
544
545#endif
546
547}
548
549void tcpip_rem_test_write(int conn_id)
550{
551 if(Net_conns[conn_id].timr_ent)
552 {
553 dtq_rem_entry(queue_id, Net_conns[conn_id].timr_ent);
554 Net_conns[conn_id].timr_ent = NULL;
555 }
556 Net_conns[conn_id].last_used = time(NULL);
557}
558
559void tcpip_pipe_sig_handler( int num )
560{
561 if(num){}
562/*
563 printf( "*** pipe_sig_handler called ***\n" );
564*/
565}
566
567static int get_bytes_to_read(int conn_id)
568{
569 int i, ret, count;
570
571 for(i = 0; i < 3; i++)
572 {
573 ret = ioctl( Net_conns[conn_id].channel, FIONREAD, &count );
574 if( ret != 0)
575 {
576 count = 0;
577 break;
578 }
579 if(count > 0)
580 {
581 break;
582 }
583 }
584 return(count);
585}
586
587static int do_read( int conn_id )
588{
589 /* There is 'data' pending, read it.
590 */
591 int len, totlen, size, count;
592 char *p;
593
594 count = get_bytes_to_read(conn_id);
595 if(!count)
596 {
597/*
598 dna_report_error(conn_id, -1,
599 "Connection closed by remote peer", DIM_ERROR, DIMTCPRDERR);
600 printf("conn_id %d\n", conn_id);
601*/
602 Net_conns[conn_id].read_rout( conn_id, -1, 0 );
603 return 0;
604 }
605
606 size = Net_conns[conn_id].size;
607 p = Net_conns[conn_id].buffer;
608 totlen = 0;
609/*
610 count = 1;
611*/
612 while( size > 0 && count > 0 )
613 {
614/*
615 would this be better? not sure afterwards...
616 nbytes = (size < count) ? size : count;
617 if( (len = readsock(Net_conns[conn_id].channel, p, nbytes, 0)) <= 0 )
618*/
619 if( (len = readsock(Net_conns[conn_id].channel, p, size, 0)) <= 0 )
620 { /* Connection closed by other side. */
621 Net_conns[conn_id].read_rout( conn_id, -1, 0 );
622 return 0;
623 }
624 else
625 {
626
627 /*
628 printf("tcpip: read %d bytes:\n",len);
629 printf( "buffer[0]=%d\n", vtohl((int *)p[0]));
630 printf( "buffer[1]=%d\n", vtohl((int *)p[1]));
631 printf( "buffer[2]=%x\n", vtohl((int *)p[2]));
632 */
633 totlen += len;
634 size -= len;
635 p += len;
636 }
637 if(size)
638 count = get_bytes_to_read(conn_id);
639 }
640
641 Net_conns[conn_id].last_used = time(NULL);
642 Net_conns[conn_id].read_rout( conn_id, 1, totlen );
643 return 1;
644}
645
646
647void do_accept( int conn_id )
648{
649 /* There is a 'connect' pending, serve it.
650 */
651 struct sockaddr_in other;
652 int othersize;
653
654 othersize = sizeof(other);
655 memset( (char *) &other, 0, othersize );
656 Net_conns[conn_id].mbx_channel = accept( Net_conns[conn_id].channel,
657 (struct sockaddr*)&other, (unsigned int *)&othersize );
658 if( Net_conns[conn_id].mbx_channel < 0 )
659 {
660 return;
661 }
662/*
663 else
664 {
665 int all, a, b, c, d;
666 char *pall;
667
668 all = other.sin_addr.s_addr;
669 pall = &all;
670 a = pall[0];
671 a &= 0x000000ff;
672 b = pall[1];
673 b &= 0x000000ff;
674 c = pall[2];
675 c &= 0x000000ff;
676 d = pall[3];
677 d &= 0x000000ff;
678printf("TCPIP got %d.%d.%d.%d \n",
679 a,b,c,d);
680 if((a == 134) && (b == 79) && (c == 157) && (d == 40))
681 {
682 closesock(Net_conns[conn_id].mbx_channel);
683 return;
684 }
685 }
686*/
687
688 Net_conns[conn_id].last_used = time(NULL);
689 Net_conns[conn_id].read_rout( Net_conns[conn_id].mbx_channel,
690 conn_id, TCPIP );
691}
692
693void io_sig_handler(int num)
694{
695 fd_set rfds;
696 int conn_id, ret, selret, count;
697 struct timeval timeout;
698
699 if(num){}
700 do
701 {
702 timeout.tv_sec = 0; /* Don't wait, just poll */
703 timeout.tv_usec = 0;
704 list_to_fds( &rfds );
705 selret = select(FD_SETSIZE, &rfds, NULL, NULL, &timeout);
706 if(selret > 0)
707 {
708 while( (ret = fds_get_entry( &rfds, &conn_id )) > 0 )
709 {
710 if( Net_conns[conn_id].reading )
711 {
712 count = 0;
713 do
714 {
715 if(Net_conns[conn_id].channel)
716 {
717 do_read( conn_id );
718 count = get_bytes_to_read(conn_id);
719 }
720 else
721 {
722 count = 0;
723 }
724 }while(count > 0 );
725 }
726 else
727 {
728 do_accept( conn_id );
729 }
730 FD_CLR( (unsigned)Net_conns[conn_id].channel, &rfds );
731 }
732 }
733 }while(selret > 0);
734}
735
736void tcpip_task( void *dummy)
737{
738 /* wait for an IO signal, find out what is happening and
739 * call the right routine to handle the situation.
740 */
741 fd_set rfds, efds, *pfds;
742 int conn_id, ret, count;
743#ifndef WIN32
744 int data;
745#endif
746 if(dummy){}
747 while(1)
748 {
749 while(!DIM_IO_valid)
750 dim_usleep(1000);
751
752 list_to_fds( &rfds );
753 FD_ZERO(&efds);
754#ifdef WIN32
755 pfds = &efds;
756#else
757 pfds = &rfds;
758#endif
759 FD_SET( DIM_IO_path[0], pfds );
760 ret = select(FD_SETSIZE, &rfds, NULL, &efds, NULL);
761 if(ret > 0)
762 {
763 if(FD_ISSET(DIM_IO_path[0], pfds) )
764 {
765#ifndef WIN32
766 read(DIM_IO_path[0], &data, 4);
767 DIM_IO_Done = 0;
768#endif
769 FD_CLR( (unsigned)DIM_IO_path[0], pfds );
770 }
771/*
772 {
773 DISABLE_AST
774*/
775 while( (ret = fds_get_entry( &rfds, &conn_id )) > 0 )
776 {
777 if( Net_conns[conn_id].reading )
778 {
779 count = 0;
780 do
781 {
782 DISABLE_AST
783 if(Net_conns[conn_id].channel)
784 {
785 do_read( conn_id );
786 count = get_bytes_to_read(conn_id);
787 }
788 else
789 {
790 count = 0;
791 }
792 ENABLE_AST
793 }while(count > 0 );
794 }
795 else
796 {
797 DISABLE_AST
798 do_accept( conn_id );
799 ENABLE_AST
800 }
801 FD_CLR( (unsigned)Net_conns[conn_id].channel, &rfds );
802 }
803/*
804 ENABLE_AST
805 }
806*/
807#ifndef WIN32
808 return;
809#endif
810 }
811 }
812}
813
814int tcpip_start_read( int conn_id, char *buffer, int size, void (*ast_routine)() )
815{
816 /* Install signal handler stuff on the socket, and record
817 * some necessary information: we are reading, and want size
818 * as size, and use buffer.
819 */
820
821 Net_conns[conn_id].read_rout = ast_routine;
822 Net_conns[conn_id].buffer = buffer;
823 Net_conns[conn_id].size = size;
824 if(Net_conns[conn_id].reading == -1)
825 {
826 if(enable_sig( conn_id ) == -1)
827 {
828#ifdef DEBUG
829 printf("START_READ - enable_sig returned -1\n");
830#endif
831 return(0);
832 }
833 }
834 Net_conns[conn_id].reading = TRUE;
835 return(1);
836}
837
838int check_node_addr( char *node, unsigned char *ipaddr)
839{
840unsigned char *ptr;
841int ret;
842
843 ptr = (unsigned char *)node+strlen(node)+1;
844 ipaddr[0] = *ptr++;
845 ipaddr[1] = *ptr++;
846 ipaddr[2] = *ptr++;
847 ipaddr[3] = *ptr++;
848 if( (ipaddr[0] == 0xff) &&
849 (ipaddr[1] == 0xff) &&
850 (ipaddr[2] == 0xff) &&
851 (ipaddr[3] == 0xff) )
852 {
853 errno = ECONNREFUSED; /* fake an error code */
854#ifdef WIN32
855 WSASetLastError(errno);
856#endif
857 return(0);
858 }
859 if( gethostbyaddr(ipaddr, sizeof(ipaddr), AF_INET) == (struct hostent *)0 )
860 {
861#ifndef WIN32
862 ret = h_errno;
863#else
864 ret = WSAGetLastError();
865#endif
866 if((ret == HOST_NOT_FOUND) || (ret == NO_DATA))
867 return(0);
868/*
869 errno = ECONNREFUSED;
870#ifdef WIN32
871 WSASetLastError(errno);
872#endif
873 return(0);
874*/
875 }
876 return(1);
877}
878
879int tcpip_open_client( int conn_id, char *node, char *task, int port )
880{
881 /* Create connection: create and initialize socket stuff. Try
882 * and make a connection with the server.
883 */
884 struct sockaddr_in sockname;
885#ifndef VxWorks
886 struct hostent *host = 0;
887#else
888 int host_addr;
889#endif
890 int path, val, ret_code, ret;
891 int a,b,c,d;
892 unsigned char ipaddr[4];
893 int host_number = 0;
894
895 dim_tcpip_init(0);
896 if(isdigit(node[0]))
897 {
898 sscanf(node,"%d.%d.%d.%d",&a, &b, &c, &d);
899 ipaddr[0] = a;
900 ipaddr[1] = b;
901 ipaddr[2] = c;
902 ipaddr[3] = d;
903 host_number = 1;
904#ifndef VxWorks
905 if( gethostbyaddr(ipaddr, sizeof(ipaddr), AF_INET) == (struct hostent *)0 )
906 {
907#ifndef WIN32
908 ret = h_errno;
909#else
910 ret = WSAGetLastError();
911#endif
912 if((ret == HOST_NOT_FOUND) || (ret == NO_DATA))
913 {
914 if(!check_node_addr(node, ipaddr))
915 return(0);
916 }
917 }
918#endif
919 }
920#ifndef VxWorks
921 else if( (host = gethostbyname(node)) == (struct hostent *)0 )
922 {
923 if(!check_node_addr(node, ipaddr))
924 return(0);
925 host_number = 1;
926/*
927 ptr = (unsigned char *)node+strlen(node)+1;
928 ipaddr[0] = *ptr++;
929 ipaddr[1] = *ptr++;
930 ipaddr[2] = *ptr++;
931 ipaddr[3] = *ptr++;
932 host_number = 1;
933 if( (ipaddr[0] == 0xff) &&
934 (ipaddr[1] == 0xff) &&
935 (ipaddr[2] == 0xff) &&
936 (ipaddr[3] == 0xff) )
937 {
938 errno = ECONNREFUSED;
939#ifdef WIN32
940 WSASetLastError(errno);
941#endif
942 return(0);
943 }
944 if( gethostbyaddr(ipaddr, sizeof(ipaddr), AF_INET) == (struct hostent *)0 )
945 {
946 errno = ECONNREFUSED;
947#ifdef WIN32
948 WSASetLastError(errno);
949#endif
950 return(0);
951 }
952*/
953 }
954#else
955 *(strchr(node,'.')) = '\0';
956 host_addr = hostGetByName(node);
957 printf("node %s addr: %x\n",node, host_addr);
958#endif
959
960 if( (path = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
961 {
962 perror("socket");
963 return(0);
964 }
965
966 val = 1;
967
968 if ((ret_code = setsockopt(path, IPPROTO_TCP, TCP_NODELAY,
969 (char*)&val, sizeof(val))) == -1 )
970 {
971#ifdef DEBUG
972 printf("Couln't set TCP_NODELAY\n");
973#endif
974 closesock(path);
975 return(0);
976 }
977
978 val = Write_buffer_size;
979 if ((ret_code = setsockopt(path, SOL_SOCKET, SO_SNDBUF,
980 (char*)&val, sizeof(val))) == -1 )
981 {
982#ifdef DEBUG
983 printf("Couln't set SO_SNDBUF\n");
984#endif
985 closesock(path);
986 return(0);
987 }
988
989 val = Read_buffer_size;
990 if ((ret_code = setsockopt(path, SOL_SOCKET, SO_RCVBUF,
991 (char*)&val, sizeof(val))) == -1 )
992 {
993#ifdef DEBUG
994 printf("Couln't set SO_RCVBUF\n");
995#endif
996 closesock(path);
997 return(0);
998 }
999
1000#if defined(__linux__) && !defined (darwin)
1001 val = 2;
1002 if ((ret_code = setsockopt(path, IPPROTO_TCP, TCP_SYNCNT,
1003 (char*)&val, sizeof(val))) == -1 )
1004 {
1005#ifdef DEBUG
1006 printf("Couln't set TCP_SYNCNT\n");
1007#endif
1008 }
1009#endif
1010
1011 sockname.sin_family = PF_INET;
1012#ifndef VxWorks
1013 if(host_number)
1014 sockname.sin_addr = *((struct in_addr *) ipaddr);
1015 else
1016 sockname.sin_addr = *((struct in_addr *) host->h_addr);
1017#else
1018 if(host_number)
1019 sockname.sin_addr = *((struct in_addr *) ipaddr);
1020 else
1021 sockname.sin_addr = *((struct in_addr *) &host_addr);
1022#endif
1023 sockname.sin_port = htons((ushort) port); /* port number to send to */
1024 while((ret = connect(path, (struct sockaddr*)&sockname, sizeof(sockname))) == -1 )
1025 {
1026 if(errno != EINTR)
1027 {
1028 closesock(path);
1029 return(0);
1030 }
1031 }
1032 strcpy( Net_conns[conn_id].node, node );
1033 strcpy( Net_conns[conn_id].task, task );
1034 Net_conns[conn_id].channel = path;
1035 Net_conns[conn_id].port = port;
1036 Net_conns[conn_id].last_used = time(NULL);
1037 Net_conns[conn_id].reading = -1;
1038 Net_conns[conn_id].timr_ent = NULL;
1039 Net_conns[conn_id].write_timedout = 0;
1040 return(1);
1041}
1042
1043int tcpip_open_server( int conn_id, char *task, int *port )
1044{
1045 /* Create connection: create and initialize socket stuff,
1046 * find a free port on this node.
1047 */
1048 struct sockaddr_in sockname;
1049 int path, val, ret_code, ret;
1050
1051 dim_tcpip_init(0);
1052 if( (path = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
1053 {
1054 return(0);
1055 }
1056
1057 val = 1;
1058 if ((ret_code = setsockopt(path, IPPROTO_TCP, TCP_NODELAY,
1059 (char*)&val, sizeof(val))) == -1 )
1060
1061 {
1062#ifdef DEBUG
1063 printf("Couln't set TCP_NODELAY\n");
1064#endif
1065 closesock(path);
1066 return(0);
1067 }
1068
1069 val = Write_buffer_size;
1070 if ((ret_code = setsockopt(path, SOL_SOCKET, SO_SNDBUF,
1071 (void *)&val, sizeof(val))) == -1 )
1072 {
1073#ifdef DEBUG
1074 printf("Couln't set SO_SNDBUF\n");
1075#endif
1076 closesock(path);
1077 return(0);
1078 }
1079/*
1080 sval1 = sizeof(val1);
1081 if ((ret_code = getsockopt(path, SOL_SOCKET, SO_SNDBUF,
1082 (void *)&val1, &sval1)) == -1 )
1083 {
1084#ifdef DEBUG
1085 printf("Couln't set SO_SNDBUF\n");
1086#endif
1087 closesock(path);
1088 return(0);
1089 }
1090printf("Set size to %d, got size %d\n", val, val1);
1091*/
1092 val = Read_buffer_size;
1093 if ((ret_code = setsockopt(path, SOL_SOCKET, SO_RCVBUF,
1094 (void *)&val, sizeof(val))) == -1 )
1095 {
1096#ifdef DEBUG
1097 printf("Couln't set SO_RCVBUF\n");
1098#endif
1099 closesock(path);
1100 return(0);
1101 }
1102
1103 if( *port == SEEK_PORT )
1104 { /* Search a free one. */
1105 *port = START_PORT_RANGE - 1;
1106 do
1107 {
1108 (*port)++;
1109 sockname.sin_family = AF_INET;
1110 sockname.sin_addr.s_addr = INADDR_ANY;
1111 sockname.sin_port = htons((ushort) *port);
1112 if( *port > STOP_PORT_RANGE ) {
1113 errno = EADDRNOTAVAIL; /* fake an error code */
1114 closesock(path);
1115#ifdef WIN32
1116 WSASetLastError(errno);
1117#endif
1118 return(0);
1119 }
1120 ret = bind(path, (struct sockaddr*)&sockname, sizeof(sockname));
1121/*
1122printf("Trying port %d, ret = %d\n", *port, ret);
1123*/
1124 } while( ret == -1 );
1125/*
1126 } while( bind(path, (struct sockaddr*)&sockname, sizeof(sockname)) == -1 );
1127*/
1128 } else {
1129#ifndef WIN32
1130 val = 1;
1131 if( setsockopt(path, SOL_SOCKET, SO_REUSEADDR, (char*)&val,
1132 sizeof(val)) == -1 )
1133 {
1134#ifdef DEBUG
1135 printf("Couln't set SO_REUSEADDR\n");
1136#endif
1137 closesock(path);
1138 return(0);
1139 }
1140#endif
1141 sockname.sin_family = AF_INET;
1142 sockname.sin_addr.s_addr = INADDR_ANY;
1143 sockname.sin_port = htons((ushort) *port);
1144 if( (ret = bind(path, (struct sockaddr*) &sockname, sizeof(sockname))) == -1 )
1145 {
1146 closesock(path);
1147 return(0);
1148 }
1149 }
1150
1151 if( (ret = listen(path, Listen_backlog)) == -1 )
1152 {
1153 closesock(path);
1154 return(0);
1155 }
1156
1157 strcpy( Net_conns[conn_id].node, "MYNODE" );
1158 strcpy( Net_conns[conn_id].task, task );
1159 Net_conns[conn_id].channel = path;
1160 Net_conns[conn_id].port = *port;
1161 Net_conns[conn_id].last_used = time(NULL);
1162 Net_conns[conn_id].reading = -1;
1163 Net_conns[conn_id].timr_ent = NULL;
1164 Net_conns[conn_id].write_timedout = 0;
1165 return(1);
1166}
1167
1168
1169int tcpip_start_listen( int conn_id, void (*ast_routine)() )
1170{
1171 /* Install signal handler stuff on the socket, and record
1172 * some necessary information: we are NOT reading, thus
1173 * no size.
1174 */
1175
1176 Net_conns[conn_id].read_rout = ast_routine;
1177 Net_conns[conn_id].size = -1;
1178 if(Net_conns[conn_id].reading == -1)
1179 {
1180 if(enable_sig( conn_id ) == -1)
1181 {
1182#ifdef DEBUG
1183 printf("START_LISTEN - enable_sig returned -1\n");
1184#endif
1185 return(0);
1186 }
1187 }
1188 Net_conns[conn_id].reading = FALSE;
1189 return(1);
1190}
1191
1192
1193int tcpip_open_connection( int conn_id, int path )
1194{
1195 /* Fill in/clear some fields, the node and task field
1196 * get filled in later by a special packet.
1197 */
1198 int val, ret_code;
1199
1200
1201 val = 1;
1202 if ((ret_code = setsockopt(path, IPPROTO_TCP, TCP_NODELAY,
1203 (char*)&val, sizeof(val))) == -1 )
1204 {
1205#ifdef DEBUG
1206 printf("Couln't set TCP_NODELAY\n");
1207#endif
1208 closesock(path);
1209 return(0);
1210 }
1211 val = Write_buffer_size;
1212 if ((ret_code = setsockopt(path, SOL_SOCKET, SO_SNDBUF,
1213 (char*)&val, sizeof(val))) == -1 )
1214 {
1215#ifdef DEBUG
1216 printf("Couln't set SO_SNDBUF\n");
1217#endif
1218 closesock(path);
1219 return(0);
1220 }
1221
1222 val = Read_buffer_size;
1223 if ((ret_code = setsockopt(path, SOL_SOCKET, SO_RCVBUF,
1224 (char*)&val, sizeof(val))) == -1 )
1225 {
1226#ifdef DEBUG
1227 printf("Couln't set SO_RCVBUF\n");
1228#endif
1229 closesock(path);
1230 return(0);
1231 }
1232
1233 Net_conns[conn_id].channel = path;
1234 Net_conns[conn_id].node[0] = 0;
1235 Net_conns[conn_id].task[0] = 0;
1236 Net_conns[conn_id].port = 0;
1237 Net_conns[conn_id].reading = -1;
1238 Net_conns[conn_id].timr_ent = NULL;
1239 Net_conns[conn_id].write_timedout = 0;
1240 return(1);
1241}
1242
1243
1244void tcpip_get_node_task( int conn_id, char *node, char *task )
1245{
1246 strcpy( node, Net_conns[conn_id].node );
1247 strcpy( task, Net_conns[conn_id].task );
1248}
1249
1250int tcpip_write( int conn_id, char *buffer, int size )
1251{
1252 /* Do a (synchronous) write to conn_id.
1253 */
1254 int wrote;
1255
1256 wrote = writesock( Net_conns[conn_id].channel, buffer, size, 0 );
1257 if( wrote == -1 ) {
1258/*
1259 Net_conns[conn_id].read_rout( conn_id, -1, 0 );
1260*/
1261 return(0);
1262 }
1263 return(wrote);
1264}
1265
1266int set_non_blocking(int channel)
1267{
1268 int ret, flags = 1;
1269 ret = ioctl(channel, FIONBIO, &flags );
1270 if(ret == -1)
1271 {
1272#ifdef DEBUG
1273 printf("ioctl non block returned -1\n");
1274#endif
1275 return(ret);
1276 }
1277 return(1);
1278}
1279
1280int set_blocking(int channel)
1281{
1282 int ret, flags = 0;
1283 ret = ioctl(channel, FIONBIO, &flags );
1284 if(ret == -1)
1285 {
1286#ifdef DEBUG
1287 printf("ioctl block returned -1\n");
1288#endif
1289 return(ret);
1290 }
1291 return(1);
1292}
1293
1294int tcpip_write_nowait( int conn_id, char *buffer, int size )
1295{
1296 /* Do a (asynchronous) write to conn_id.
1297 */
1298 int wrote, ret, selret;
1299
1300 struct timeval timeout;
1301 fd_set wfds;
1302 int tcpip_would_block();
1303
1304 set_non_blocking(Net_conns[conn_id].channel);
1305 wrote = writesock( Net_conns[conn_id].channel, buffer, size, 0 );
1306#ifndef WIN32
1307 ret = errno;
1308#else
1309 ret = WSAGetLastError();
1310#endif
1311 set_blocking(Net_conns[conn_id].channel);
1312 if(wrote == -1)
1313 {
1314 if(tcpip_would_block(ret))
1315 {
1316 timeout.tv_sec = Write_timeout;
1317 timeout.tv_usec = 0;
1318 FD_ZERO(&wfds);
1319 FD_SET( Net_conns[conn_id].channel, &wfds);
1320 selret = select(FD_SETSIZE, NULL, &wfds, NULL, &timeout);
1321 if(selret > 0)
1322 {
1323 wrote = writesock( Net_conns[conn_id].channel, buffer, size, 0 );
1324 if( wrote == -1 )
1325 {
1326 return(0);
1327 }
1328 }
1329 }
1330 else
1331 {
1332 return(0);
1333 }
1334 }
1335 if(wrote == -1)
1336 {
1337 Net_conns[conn_id].write_timedout = 1;
1338 }
1339 return(wrote);
1340}
1341
1342int tcpip_close( int conn_id )
1343{
1344 int channel;
1345 /* Clear all traces of the connection conn_id.
1346 */
1347 if(Net_conns[conn_id].timr_ent)
1348 {
1349 dtq_rem_entry(queue_id, Net_conns[conn_id].timr_ent);
1350 Net_conns[conn_id].timr_ent = NULL;
1351 }
1352 channel = Net_conns[conn_id].channel;
1353 Net_conns[conn_id].channel = 0;
1354 Net_conns[conn_id].port = 0;
1355 Net_conns[conn_id].node[0] = 0;
1356 Net_conns[conn_id].task[0] = 0;
1357 if(channel)
1358 {
1359 if(Net_conns[conn_id].write_timedout)
1360 {
1361 Net_conns[conn_id].write_timedout = 0;
1362#if defined(__linux__) && !defined (darwin)
1363 shutdown(channel, 2);
1364#endif
1365 }
1366 closesock(channel);
1367 }
1368 return(1);
1369}
1370
1371
1372int tcpip_failure( int code )
1373{
1374 return(!code);
1375}
1376
1377int tcpip_would_block( int code )
1378{
1379 if(code == EWOULDBLOCK)
1380 return(1);
1381 return(0);
1382}
1383
1384void tcpip_report_error( int code )
1385{
1386#ifndef WIN32
1387 if(code){}
1388 perror("tcpip");
1389#else
1390 int my_perror();
1391
1392 my_perror("tcpip", code);
1393#endif
1394}
1395
1396#ifdef WIN32
1397int my_perror(char *str, int error)
1398{
1399int code;
1400
1401 if(error <= 0)
1402 code = WSAGetLastError();
1403 else
1404 code = error;
1405 printf("new - %s\n",strerror(code));
1406 printf("%s: ",str);
1407 switch(code)
1408 {
1409 case WSAEWOULDBLOCK:
1410 printf("Operation would block");
1411 break;
1412 case WSAEINPROGRESS:
1413 printf("Operation now in progress");
1414 break;
1415 case WSAEALREADY:
1416 printf("Operation already in progress");
1417 break;
1418 case WSAENOTSOCK:
1419 printf("Socket operation on non-socket");
1420 break;
1421 case WSAEDESTADDRREQ:
1422 printf("Destination address required");
1423 break;
1424 case WSAEMSGSIZE:
1425 printf("Message too long");
1426 break;
1427 case WSAEPROTOTYPE:
1428 printf("Protocol wrong type for socket");
1429 break;
1430 case WSAENOPROTOOPT:
1431 printf("Protocol not available");
1432 break;
1433 case WSAEPROTONOSUPPORT:
1434 printf("Protocol not supported");
1435 break;
1436 case WSAESOCKTNOSUPPORT:
1437 printf("Socket type not supported");
1438 break;
1439 case WSAEOPNOTSUPP:
1440 printf("Operation not supported on transport endpoint");
1441 break;
1442 case WSAEPFNOSUPPORT:
1443 printf("Protocol family not supported");
1444 break;
1445 case WSAEAFNOSUPPORT:
1446 printf("Address family not supported by protocol");
1447 break;
1448 case WSAEADDRINUSE:
1449 printf("Address already in use");
1450 break;
1451 case WSAEADDRNOTAVAIL:
1452 printf("Cannot assign requested address");
1453 break;
1454 case WSAENETDOWN:
1455 printf("Network is down");
1456 break;
1457 case WSAENETUNREACH:
1458 printf("Network is unreachable");
1459 break;
1460 case WSAENETRESET:
1461 printf("Network dropped connection because of reset");
1462 break;
1463 case WSAECONNABORTED:
1464 printf("Software caused connection abort");
1465 break;
1466 case WSAECONNRESET:
1467 printf("Connection reset by peer");
1468 break;
1469 case WSAENOBUFS:
1470 printf("No buffer space available");
1471 break;
1472 case WSAEISCONN:
1473 printf("Transport endpoint is already connected");
1474 break;
1475 case WSAENOTCONN:
1476 printf("Transport endpoint is not connected");
1477 break;
1478 case WSAESHUTDOWN:
1479 printf("Cannot send after transport endpoint shutdown");
1480 break;
1481 case WSAETOOMANYREFS:
1482 printf("Too many references: cannot splice");
1483 break;
1484 case WSAETIMEDOUT:
1485 printf("Connection timed out");
1486 break;
1487 case WSAECONNREFUSED:
1488 printf("Connection refused");
1489 break;
1490 case WSAELOOP:
1491 printf("Too many symbolic links encountered");
1492 break;
1493 case WSAENAMETOOLONG:
1494 printf("File name too long");
1495 break;
1496 case WSAEHOSTDOWN:
1497 printf("Host is down");
1498 break;
1499 case WSAEHOSTUNREACH:
1500 printf("No route to host");
1501 break;
1502 case WSAENOTEMPTY:
1503 printf("Directory not empty");
1504 break;
1505 case WSAEUSERS:
1506 printf("Too many users");
1507 break;
1508 case WSAEDQUOT:
1509 printf("Quota exceeded");
1510 break;
1511 case WSAESTALE:
1512 printf("Stale NFS file handle");
1513 break;
1514 case WSAEREMOTE:
1515 printf("Object is remote");
1516 break;
1517 case WSAHOST_NOT_FOUND:
1518 printf("Host not found");
1519 break;
1520 case WSATRY_AGAIN:
1521 printf("Host not found, or SERVERFAIL");
1522 break;
1523 case WSANO_RECOVERY:
1524 printf("Non recoverable errors, FORMERR, REFUSED, NOTIMP");
1525 break;
1526 case WSANO_DATA:
1527 printf("Valid name, no data record of requested type");
1528 break;
1529 default:
1530 printf("Unknown error %d",code);
1531 }
1532 printf("\n");
1533 return(1);
1534}
1535
1536void my_strerror(int error, char *msg)
1537{
1538int code;
1539char str[128];
1540
1541 if(error <= 0)
1542 code = WSAGetLastError();
1543 else
1544 code = error;
1545 switch(code)
1546 {
1547 case WSAEWOULDBLOCK:
1548 sprintf(str,"Operation would block");
1549 break;
1550 case WSAEINPROGRESS:
1551 sprintf(str,"Operation now in progress");
1552 break;
1553 case WSAEALREADY:
1554 sprintf(str,"Operation already in progress");
1555 break;
1556 case WSAENOTSOCK:
1557 sprintf(str,"Socket operation on non-socket");
1558 break;
1559 case WSAEDESTADDRREQ:
1560 sprintf(str,"Destination address required");
1561 break;
1562 case WSAEMSGSIZE:
1563 sprintf(str,"Message too long");
1564 break;
1565 case WSAEPROTOTYPE:
1566 sprintf(str,"Protocol wrong type for socket");
1567 break;
1568 case WSAENOPROTOOPT:
1569 sprintf(str,"Protocol not available");
1570 break;
1571 case WSAEPROTONOSUPPORT:
1572 sprintf(str,"Protocol not supported");
1573 break;
1574 case WSAESOCKTNOSUPPORT:
1575 sprintf(str,"Socket type not supported");
1576 break;
1577 case WSAEOPNOTSUPP:
1578 sprintf(str,"Operation not supported on transport endpoint");
1579 break;
1580 case WSAEPFNOSUPPORT:
1581 sprintf(str,"Protocol family not supported");
1582 break;
1583 case WSAEAFNOSUPPORT:
1584 sprintf(str,"Address family not supported by protocol");
1585 break;
1586 case WSAEADDRINUSE:
1587 sprintf(str,"Address already in use");
1588 break;
1589 case WSAEADDRNOTAVAIL:
1590 sprintf(str,"Cannot assign requested address");
1591 break;
1592 case WSAENETDOWN:
1593 sprintf(str,"Network is down");
1594 break;
1595 case WSAENETUNREACH:
1596 sprintf(str,"Network is unreachable");
1597 break;
1598 case WSAENETRESET:
1599 sprintf(str,"Network dropped connection because of reset");
1600 break;
1601 case WSAECONNABORTED:
1602 sprintf(str,"Software caused connection abort");
1603 break;
1604 case WSAECONNRESET:
1605 sprintf(str,"Connection reset by peer");
1606 break;
1607 case WSAENOBUFS:
1608 sprintf(str,"No buffer space available");
1609 break;
1610 case WSAEISCONN:
1611 sprintf(str,"Transport endpoint is already connected");
1612 break;
1613 case WSAENOTCONN:
1614 sprintf(str,"Transport endpoint is not connected");
1615 break;
1616 case WSAESHUTDOWN:
1617 sprintf(str,"Cannot send after transport endpoint shutdown");
1618 break;
1619 case WSAETOOMANYREFS:
1620 sprintf(str,"Too many references: cannot splice");
1621 break;
1622 case WSAETIMEDOUT:
1623 sprintf(str,"Connection timed out");
1624 break;
1625 case WSAECONNREFUSED:
1626 sprintf(str,"Connection refused");
1627 break;
1628 case WSAELOOP:
1629 sprintf(str,"Too many symbolic links encountered");
1630 break;
1631 case WSAENAMETOOLONG:
1632 sprintf(str,"File name too long");
1633 break;
1634 case WSAEHOSTDOWN:
1635 sprintf(str,"Host is down");
1636 break;
1637 case WSAEHOSTUNREACH:
1638 sprintf(str,"No route to host");
1639 break;
1640 case WSAENOTEMPTY:
1641 sprintf(str,"Directory not empty");
1642 break;
1643 case WSAEUSERS:
1644 sprintf(str,"Too many users");
1645 break;
1646 case WSAEDQUOT:
1647 sprintf(str,"Quota exceeded");
1648 break;
1649 case WSAESTALE:
1650 sprintf(str,"Stale NFS file handle");
1651 break;
1652 case WSAEREMOTE:
1653 sprintf(str,"Object is remote");
1654 break;
1655 case WSAHOST_NOT_FOUND:
1656 sprintf(str,"Host not found");
1657 break;
1658 case WSATRY_AGAIN:
1659 sprintf(str,"Host not found, or SERVERFAIL");
1660 break;
1661 case WSANO_RECOVERY:
1662 sprintf(str,"Non recoverable errors, FORMERR, REFUSED, NOTIMP");
1663 break;
1664 case WSANO_DATA:
1665 sprintf(str,"Valid name, no data record of requested type");
1666 break;
1667 default:
1668 sprintf(str,"Unknown error %d",code);
1669 }
1670 strcpy(msg, str);
1671}
1672#endif
1673
1674void tcpip_get_error( char *str, int code )
1675{
1676 DISABLE_AST
1677#ifndef WIN32
1678 if(code){}
1679 if((errno == ENOENT) && (h_errno == HOST_NOT_FOUND))
1680 strcpy(str,"Host not found");
1681 else
1682 strcpy(str, strerror(errno));
1683#else
1684 my_strerror(code, str);
1685#endif
1686 ENABLE_AST
1687}
Note: See TracBrowser for help on using the repository browser.