fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / redirect.c
1 /*
2  * (c) Copyright 1998-2001 by Rob Braun
3  * All rights reserved.  The file named COPYRIGHT specifies the terms
4  * and conditions for redistribution.
5  */
6 #include "config.h"
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/time.h>
10 #ifdef HAVE_SYS_RESOURCE_H
11 #include <sys/resource.h>
12 #endif
13 #include <sys/wait.h>
14 #include <netinet/in.h>
15 #include <errno.h>
16 #include <pwd.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <sys/wait.h>
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <netinet/tcp.h>
24 #ifdef HAVE_ARPA_INET_H
25 #include <arpa/inet.h>
26 #endif
27 #ifdef HAVE_SYS_SIGNAL_H
28 #include <sys/signal.h>
29 #endif
30
31 #include "redirect.h"
32 #include "service.h"
33 #include "log.h"
34 #include "sconf.h"
35 #include "msg.h"
36
37 #define NET_BUFFER 1500
38
39 static int RedirServerFd = -1;
40
41 /* Theoretically, this gets invoked when the remote side is no
42  * longer available for reading or writing.
43  * So, we send a HUP to the child process, wait(), then exit.
44  */
45 #ifdef __GNUC__
46 __attribute__ ((noreturn))
47 #endif
48 static void redir_sigpipe( int signum ) 
49 {
50    Sclose(RedirServerFd);
51    _exit(0);
52 }
53
54 /* Do the redirection of a service */
55 /* This function gets called from child.c after we have been forked */
56 void redir_handler( struct server *serp )
57 {
58    struct service *sp = SERVER_SERVICE( serp );
59    struct service_config *scp = SVC_CONF( sp );
60    int RedirDescrip = SERVER_FD( serp );
61    int maxfd, num_read, num_wrote=0, ret=0;
62    unsigned int sin_len = 0;
63    unsigned long bytes_in = 0, bytes_out = 0;
64    int no_to_nagle = 1;
65    int on = 1, v6on;
66    char buff[NET_BUFFER];
67    fd_set rdfd, msfd;
68    struct timeval *timep = NULL;
69    const char *func = "redir_handler";
70    union xsockaddr serveraddr ;
71
72    if( signal(SIGPIPE, redir_sigpipe) == SIG_ERR ) 
73       msg(LOG_ERR, func, "unable to setup signal handler");
74
75    close_all_svc_descriptors();
76
77    /* If it's a tcp service we are redirecting */
78    if( SC_PROTOVAL(scp) == IPPROTO_TCP )
79    {
80       memcpy(&serveraddr, SC_REDIR_ADDR(scp), sizeof(serveraddr));
81       if( serveraddr.sa_in.sin_family == AF_INET ) {
82          sin_len = sizeof( struct sockaddr_in );
83          RedirServerFd = socket(AF_INET, SOCK_STREAM, 0);
84        } else if( serveraddr.sa_in.sin_family == AF_INET6 ) {
85          sin_len = sizeof( struct sockaddr_in6 );
86          RedirServerFd = socket(AF_INET6, SOCK_STREAM, 0);
87       } else {
88          msg(LOG_ERR, func, "not a valid protocol. Use IPv4 or IPv6.");
89          exit(0);
90       }
91
92       if( RedirServerFd < 0 )
93       {
94          msg(LOG_ERR, func, "cannot create socket: %m");
95          exit(0);
96       }
97
98       if( SC_IPV6( scp ) ) {
99          if( SC_V6ONLY( scp ) ) {
100             v6on = 1;
101          } else {
102             v6on = 0;
103          }
104 #ifdef IPV6_V6ONLY
105          if( setsockopt(RedirServerFd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6on, sizeof(v6on)) < 0 ) { 
106             msg( LOG_ERR, func, "Setting IPV6_V6ONLY option failed (%m)" );
107          }
108 #endif
109
110       }
111       if( SC_KEEPALIVE( scp ) )
112          if (setsockopt(RedirServerFd, SOL_SOCKET, SO_KEEPALIVE, 
113                         (char *)&on, sizeof( on ) ) < 0 )
114             msg(LOG_ERR, func, 
115                 "setsockopt SO_KEEPALIVE RedirServerFd failed: %m");
116       
117       if( serveraddr.sa_in.sin_family == AF_INET )
118          serveraddr.sa_in.sin_port = htons(serveraddr.sa_in.sin_port);
119       if( serveraddr.sa_in.sin_family == AF_INET6 )
120          serveraddr.sa_in6.sin6_port = htons(serveraddr.sa_in6.sin6_port);
121
122       if( connect(RedirServerFd, &serveraddr.sa, sin_len) < 0 )
123       {
124          msg(LOG_ERR, func, "can't connect to remote host %s: %m",
125             xaddrname( &serveraddr ) );
126          exit(0);
127       }
128
129       /* connection now established */
130
131       if (setsockopt(RedirServerFd, IPPROTO_TCP, TCP_NODELAY, 
132          (char *) &no_to_nagle, sizeof( on ) ) < 0) {
133
134          msg(LOG_ERR, func, "setsockopt RedirServerFd failed: %m");
135       }
136
137       if (setsockopt(RedirDescrip, IPPROTO_TCP, TCP_NODELAY, 
138          (char *) &no_to_nagle, sizeof( on ) ) < 0) {
139
140          msg(LOG_ERR, func, "setsockopt RedirDescrip failed: %m");
141       }
142
143       maxfd = (RedirServerFd > RedirDescrip)?RedirServerFd:RedirDescrip;
144       FD_ZERO(&msfd);
145       FD_SET(RedirDescrip, &msfd);
146       FD_SET(RedirServerFd, &msfd);
147
148       while(1) {
149          memcpy(&rdfd, &msfd, sizeof(rdfd));
150          if (select(maxfd + 1, &rdfd, (fd_set *)0, (fd_set *)0, timep) <= 0) {
151             /* place for timeout code, currently does not time out */
152             break;
153          }
154
155          if (FD_ISSET(RedirDescrip, &rdfd)) {
156             do {
157                num_read = read(RedirDescrip,
158                   buff, sizeof(buff));
159                if (num_read == -1 && errno == EINTR)
160                   continue;
161                if (num_read <= 0)
162                   goto REDIROUT;
163                bytes_in += num_read;
164             } while (num_read < 0);
165
166             /* Loop until we have written everything
167              * that was read */
168             num_wrote = 0;
169             while( num_wrote < num_read ) {
170                ret = write(RedirServerFd,
171                   buff + num_wrote,
172                   num_read - num_wrote);
173                if (ret == -1 && errno == EINTR)
174                   continue;
175                if (ret <= 0)
176                   goto REDIROUT;
177                num_wrote += ret;
178             }
179          }
180
181          if (FD_ISSET(RedirServerFd, &rdfd)) {
182             do {
183                num_read = read(RedirServerFd,
184                   buff, sizeof(buff));
185                if (num_read == -1 && errno == EINTR)
186                   continue;
187                if (num_read <= 0)
188                   goto REDIROUT;
189                bytes_out += num_read;
190             } while (num_read < 0);
191
192             /* Loop until we have written everything
193              * that was read */
194             num_wrote = 0;
195             while( num_wrote < num_read ) {
196                ret = write(RedirDescrip,
197                   buff + num_wrote,
198                   num_read - num_wrote);
199                if (ret == -1 && errno == EINTR)
200                   continue;
201                if (ret <= 0)
202                   goto REDIROUT;
203                num_wrote += ret;
204             }
205          }
206       }
207 REDIROUT:
208       if( M_IS_SET( SC_LOG_ON_SUCCESS(scp), LO_TRAFFIC ) ) {
209          svc_logprint( SERVER_CONNSERVICE( serp ), "TRAFFIC",
210                        "in=%lu(bytes) out=%lu(bytes)", bytes_in, bytes_out );
211       }
212
213       exit(0);
214    }
215
216    msg(LOG_ERR, func, 
217    "redirect with any protocol other than tcp is not supported at this time.");
218    exit(0);
219 }