fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / udpint.c
1 /*
2  * (c) Copyright 1992 by Panagiotis Tsirigotis
3  * (c) Sections Copyright 1998-2001 by Rob Braun
4  * All rights reserved.  The file named COPYRIGHT specifies the terms 
5  * and conditions for redistribution.
6  */
7
8 #include "config.h"
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <netdb.h>
14 #include <sys/time.h>
15 #include <syslog.h>
16 #include <errno.h>
17
18 #ifdef HAVE_SYS_SELECT_H
19 #include <sys/select.h>
20 #endif
21
22 #include "udpint.h"
23 #include "intcommon.h"
24 #include "util.h"
25 #include "connection.h"
26 #include "access.h"
27 #include "log.h"
28 #include "msg.h"
29 #include "sconf.h"
30
31 /*
32  * Datagrams greater than this will be truncated
33  */
34 #define MAX_DATAGRAM_SIZE         ( 32 * 1024 )
35
36 struct packet
37 {
38    union xsockaddr from ;
39    char                  *data ;
40    int                  size ;
41 } ;
42
43 typedef struct packet packet_s ;
44
45
46 struct idgram_private
47 {
48    unsigned received_packets ;
49 } ;
50
51 #define IDP( p )               ((struct idgram_private *)(p))
52
53
54 static struct idgram_private idgram ;
55
56 static void di_mux(void) ;
57 static void udp_remote_to_local( struct intercept_s *ip, channel_s **chpp );
58 static status_e udp_local_to_remote( channel_s *chp );
59 static void send_data( int sd, char *buf, int len, union xsockaddr *addr );
60 static status_e get_incoming_packet( struct intercept_s *ip, packet_s *pp );
61
62 static const struct intercept_ops idgram_ops =
63    {
64       di_mux,
65       di_exit
66    } ;
67
68
69 static struct intercept_s dgram_intercept_state ;
70
71
72 struct intercept_s *di_init( struct server *serp )
73 {
74    struct intercept_s *ip = &dgram_intercept_state ;
75    
76    ip->int_socket_type = SOCK_DGRAM ;
77    ip->int_priv = (void *) &idgram ;
78    ip->int_ops = &idgram_ops ;
79    int_init( ip, serp ) ;
80    return( ip ) ;
81 }
82
83
84 void di_exit(void)
85 {
86    struct intercept_s *ip = &dgram_intercept_state ;
87
88    if ( IDP( ip->int_priv )->received_packets == 0 )
89       drain( INT_REMOTE( ip ) ) ;
90    int_exit( ip ) ;
91 }
92
93
94 /*
95  * Returns only if there is an I/O error while communicating with the server
96  */
97 static void di_mux(void)
98 {
99    struct intercept_s   *ip = &dgram_intercept_state ;
100    fd_set                     socket_mask ;
101    int                        mask_max ;
102
103    FD_ZERO( &socket_mask ) ;
104    FD_SET( INT_REMOTE( ip ), &socket_mask ) ;
105    mask_max = INT_REMOTE( ip ) ;
106
107    for ( ;; )
108    {
109       unsigned u ;
110       channel_s *chp ;
111       fd_set read_mask ;
112       int n_ready ;
113
114       read_mask = socket_mask ;
115       n_ready = int_select( mask_max+1, &read_mask ) ;
116
117       if ( n_ready == -1 )
118          return ;
119       
120       if ( FD_ISSET( INT_REMOTE( ip ), &read_mask ) )
121       {
122          udp_remote_to_local( ip, &chp ) ;
123          if ( chp != NULL )
124          {
125             FD_SET( chp->ch_local_socket, &socket_mask ) ;
126             if ( chp->ch_local_socket > mask_max )
127                mask_max = chp->ch_local_socket ;
128          }
129          if ( --n_ready == 0 )
130             continue ;
131       }
132
133       for ( u = 0 ; u < pset_count( INT_CONNECTIONS( ip ) ) ; u++ )
134       {
135          chp = CHP( pset_pointer( INT_CONNECTIONS( ip ), u ) ) ;
136
137          if ( FD_ISSET( chp->ch_local_socket, &read_mask ) )
138          {
139             if ( udp_local_to_remote( chp ) == FAILED )
140                return ;
141             if ( --n_ready == 0 )
142                break ;
143          }
144       }
145    }
146 }
147
148
149 /*
150  * Read data from the remote socket and send it to the appropriate local 
151  * socket.
152  * If this is a new connection, insert it in the connection table and
153  * place its handle in *chpp.
154  */
155 static void udp_remote_to_local( struct intercept_s *ip, channel_s **chpp )
156 {
157    char               buf[ MAX_DATAGRAM_SIZE ] ;
158    packet_s           packet ;
159    channel_s          *chp ;
160    bool_int           addr_checked ;
161
162    *chpp = CHANNEL_NULL ;
163
164    packet.data = buf ;
165    packet.size = sizeof( buf ) ;
166    if ( get_incoming_packet( ip, &packet ) == FAILED )
167       return ;
168
169    chp = int_lookupconn( ip, &packet.from, &addr_checked ) ;
170    if ( chp == CHANNEL_NULL )
171    {
172       struct server      *serp = INT_SERVER( ip ) ;
173       struct service    *sp = SERVER_SERVICE( serp ) ;
174       connection_s      *cop = SERVER_CONNECTION( serp ) ;
175
176       if ( ( chp = int_newconn( ip, &packet.from, INT_REMOTE( ip ) ) ) == NULL )
177          return ;
178
179       CONN_SETADDR( cop, &packet.from ) ;      /* for logging */
180
181       if ( INTERCEPT( ip ) )
182       {
183          mask_t check_mask ;
184          access_e result ;
185
186          M_OR( check_mask, XMASK( CF_ADDRESS ), XMASK( CF_TIME ) ) ;
187          result = access_control( sp, cop, &check_mask ) ;
188
189          if ( result != AC_OK )
190          {
191             svc_log_failure( sp, cop, result ) ;
192             chp->ch_state = BAD_CHANNEL ;
193             return ;
194          }
195       }
196       
197       /*
198        * Since we don't distinguish ports, there is no point to log
199        * another successful attempt from the same address
200        */
201       if ( ! addr_checked )
202          svc_log_success( sp, cop, SERVER_PID( serp ) ) ;
203          
204       *chpp = chp ;
205    }
206    else if ( chp->ch_state == BAD_CHANNEL )
207       return ;
208    
209 #ifdef DEBUG_UDPINT
210    if ( debug.on )
211       msg( LOG_DEBUG, "udp_remote_to_local",
212                "sending %d bytes to server on port %d",
213                      packet.size, ntohs( INT_LOCALADDR( ip )->sin_port ) ) ;
214 #endif
215
216    send_data( chp->ch_local_socket,
217          packet.data, packet.size, NULL ) ;
218 }
219
220
221 /*
222  * Send the data in buf to destination addr using the socket sd.
223  * If addr is NULL, use the default socket destination
224  */
225 static void send_data( int sd, char *buf, int len, union xsockaddr *addr )
226 {
227    char   *p ;
228    int   left ;
229    int   cc ;
230    const char   *func = "send_data" ;
231
232    for ( p = buf, left = len ; left > 0 ; left -= cc, p+= cc )
233    {
234       if ( addr == NULL )
235          cc = send( sd, p, left, 0 ) ;
236       else
237          cc = sendto( sd, p, left, 0, SA( addr ), sizeof( *addr ) ) ;
238
239       if ( cc == -1 ) {
240          if ( errno == EINTR )
241          {
242             cc = 0 ;
243             continue ;
244          }
245          else
246          {
247             msg( LOG_ERR, func, "%s: %m", addr ? "sendto" : "send" ) ;
248             return ;
249          }
250       }
251    }
252 }
253
254
255 static status_e get_incoming_packet( struct intercept_s *ip, packet_s *pp )
256 {
257    socklen_t from_len = 0;
258    const char *func = "get_incoming_packet" ;
259
260    if( SC_IPV4( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) )
261       from_len = sizeof( struct sockaddr_in );
262    if( SC_IPV6( SVC_CONF( SERVER_SERVICE( INT_SERVER( ip ) ) ) ) )
263       from_len = sizeof( struct sockaddr_in6 );
264
265    for ( ;; )
266    {
267       int cc ;
268
269       from_len = sizeof( pp->from ) ;
270       cc = recvfrom( INT_REMOTE( ip ), pp->data, pp->size,
271                                     0, SA( &pp->from ), &from_len ) ;
272       if ( cc == -1 )
273       {
274          if ( errno != EINTR )
275          {
276             msg( LOG_ERR, func, "recvfrom error: %m" ) ;
277             return( FAILED ) ;
278          }
279       }
280       else if ( cc == 0 )
281          return( FAILED ) ;
282       else
283       {
284          pp->size = cc ;
285          IDP( ip->int_priv )->received_packets++ ;
286          break ;
287       }
288    }
289
290    if ( from_len == 0 )
291    {
292       msg( LOG_ERR, func, "incoming packet had 0 length address" ) ;
293       return( FAILED ) ;
294    }
295    
296 #ifdef DEBUG_UDPINT
297    if ( debug.on )
298       msg( LOG_DEBUG, func, "Received %d bytes from address: %s,%d",
299          pp->size, xaddrname( &pp->from ), ntohs( xaddrport(&pp->from) ) );
300 #endif
301
302    return( OK ) ;
303 }
304
305
306 static status_e udp_local_to_remote( channel_s *chp )
307 {
308    char   buf[ MAX_DATAGRAM_SIZE ] ;
309    int    cc ;
310    const char   *func = "udp_local_to_remote" ;
311
312    for ( ;; )
313    {
314       cc = recv( chp->ch_local_socket, buf, sizeof( buf ), 0 ) ;
315    
316       if ( cc == -1 )
317       {
318          if ( errno != EINTR ) 
319          {
320             msg( LOG_ERR, func, "recv from daemon: %m" ) ;
321             return( FAILED ) ;
322          }
323       }
324       else if ( cc == 0 )
325          return( FAILED ) ;
326       else
327          break ;
328    }
329    
330 #ifdef DEBUG_UDPINT
331    if ( debug.on )
332       msg( LOG_DEBUG, func, "sending %d bytes to address %s,%d",
333          cc, xaddrname( &chp->ch_from ), ntohs( xaddrport(&chp->ch_from) ) ) ;
334 #endif
335
336    send_data( chp->ch_remote_socket, buf, cc, &chp->ch_from ) ;
337    return( OK ) ;
338 }
339