fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / intcommon.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/time.h>
11 #include <sys/socket.h>
12 #include <signal.h>
13 #include <syslog.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17
18 #include "intcommon.h"
19 #include "msg.h"
20 #include "signals.h"
21 #include "connection.h"
22 #include "sconf.h"
23 #include "state.h"
24 #include "main.h"
25 #include "xconfig.h"
26
27
28 void int_fail( const struct intercept_s *ip, const char *lsyscall )
29 {
30    msg( LOG_ERR, "fail", "%s failed: %m", lsyscall ) ;
31    (*ip->int_ops->exit)() ;
32    /* NOTREACHED */
33 }
34
35
36 /*
37  * Returns either a positive number or -1
38  */
39 int int_select( int max, fd_set *read_mask )
40 {
41    const char *func = "int_select" ;
42
43    for ( ;; )
44    {
45       int n_ready ;
46
47       n_ready = select( max+1, read_mask,
48                                  FD_SET_NULL, FD_SET_NULL, TIMEVAL_NULL ) ;
49       if ( n_ready > 0 )
50          return( n_ready ) ;
51       else if ( n_ready == -1 ) {
52          if ( errno == EINTR )
53             continue ;
54          else
55          {
56             msg( LOG_ERR, func, "select: %m" ) ;
57             return( -1 ) ;
58          }
59       }
60    }
61 }
62
63
64 void int_exit( struct intercept_s *ip )
65 {
66    int status = SERVER_EXITSTATUS( INT_SERVER( ip ) ) ;
67    const char *func = "int_exit" ;
68
69    if ( debug.on )
70    {
71       if ( PROC_EXITED( status ) )
72          msg( LOG_DEBUG, func, "intercepted server died" ) ;
73       else if ( PROC_SIGNALED( status ) )
74          msg( LOG_DEBUG, func, "intercepted server received signal %s",
75                sig_name( (int) PROC_TERMSIG( status ) ) ) ;
76    }
77    _exit( (int) PROC_EXITSTATUS( status ) ) ;
78 }
79
80
81 /*
82  * The ops vector must be installed before invoking this function
83  */
84 void int_init( struct intercept_s *ip, struct server *serp )
85 {
86    unsigned u ;
87    const char *func = "int_init" ;
88
89    /*
90     * Sanity test
91     */
92    if ( SERVER_SERVICE( serp ) != SERVER_CONNSERVICE( serp ) )
93    {
94       msg( LOG_ERR, func, "server service (%s) != connection service (%s)",
95                            SVC_ID( SERVER_SERVICE( serp ) ),
96                               SVC_ID( SERVER_CONNSERVICE( serp ) ) ) ;
97       exit( 1 ) ;
98    }
99
100    /*
101     * Close all unneeded descriptors
102     */
103    for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ )
104    {
105       struct service *sp = SP( pset_pointer( SERVICES( ps ), u ) ) ;
106
107       if ( sp == SERVER_SERVICE( serp ) )
108          continue ;
109       if ( LOG_GET_TYPE( SC_LOG( SVC_CONF( sp ) ) ) == L_FILE )
110          xlog_destroy( SVC_LOG( sp ) ) ;
111       (void) Sclose( SVC_FD( sp ) ) ;
112    }
113
114    /*
115     * Setup signal handling
116     */
117    if ( signal( SERVER_EXIT_SIG, int_sighandler ) == SIG_ERR )
118       int_fail( ip, "signal" ) ;
119    if ( signal( INTERCEPT_SIG, int_sighandler ) == SIG_ERR )
120       int_fail( ip, "signal" ) ;
121    if ( signal( SIGTERM, int_sighandler ) == SIG_ERR )
122       int_fail( ip, "signal" ) ;
123    
124    /*
125     * Initialize state
126     */
127    INTERCEPT( ip ) = TRUE ;
128    *INT_SERVER( ip ) = *serp ;
129    INT_REMOTE( ip ) = SERVER_FD( serp ) ;
130
131    INT_CONNECTIONS( ip ) = pset_create( 0, 0 ) ;
132    if ( INT_CONNECTIONS( ip ) == NULL )
133    {
134       msg( LOG_ERR, func, ES_NOMEM ) ;
135       (*ip->int_ops->exit)() ;
136    }
137 }
138
139
140 /*
141  * Make a new connection to the local server
142  */
143 channel_s *int_newconn( struct intercept_s *ip, 
144                         union xsockaddr *sinp,
145                         int remote_socket )
146 {
147    struct service       *sp          = SERVER_SERVICE( INT_SERVER( ip ) ) ;
148    int                   socket_type = SVC_SOCKET_TYPE( sp ) ;
149    union xsockaddr      *local       = INT_LOCALADDR( ip ) ;
150    char                 *sid         = SVC_ID( sp ) ;
151    channel_s            *chp ;
152    int                   sd ;
153    const char           *func = "int_newconn" ;
154
155    /*
156     * Get a socket and connect it to the local address
157     *
158     */
159    if ( ( sd = socket( local->sa.sa_family, socket_type, SC_PROTOVAL(SVC_CONF(sp)) ) ) == -1 )
160    {
161       msg( LOG_ERR, func,"(intercepting %s) socket creation failed: %m", sid ) ;
162       return( CHANNEL_NULL ) ;
163    }
164
165    if ( connect( sd, SA( local ), sizeof( *local ) ) == -1 )
166    {
167       msg( LOG_ERR, func, "(intercepting %s) connect failed: %m", sid ) ;
168       (void) Sclose( sd ) ;
169       return( CHANNEL_NULL ) ;
170    }
171
172    chp = NEW_CHANNEL() ;
173    if ( chp == CHANNEL_NULL )
174    {
175       msg( LOG_ERR, func, ES_NOMEM ) ;
176       (void) Sclose( sd ) ;
177       return( CHANNEL_NULL ) ;
178    }
179
180    if ( pset_add( INT_CONNECTIONS( ip ), chp ) == NULL )
181    {
182       msg( LOG_ERR, func, ES_NOMEM ) ;
183       FREE_CHANNEL( chp ) ;
184       (void) Sclose( sd ) ;
185       return( CHANNEL_NULL ) ;
186    }
187
188    chp->ch_state = GOOD_CHANNEL ;
189    chp->ch_from = *sinp ;
190    chp->ch_local_socket = sd ;
191    chp->ch_remote_socket = remote_socket ;
192    return( chp ) ;
193 }
194
195
196
197 /*
198  * Check if the (address,port) in sinp is already in the connection table.
199  * Return value:
200  *    a connection pointer if the address is found
201  *    NULL if the address if not found
202  *
203  * *addr_checked is set to TRUE of FALSE depending on whether there
204  * is already a connection from the same IP address in the table.
205  */
206 channel_s *int_lookupconn( struct intercept_s *ip, 
207                            union xsockaddr *sinp,
208                            bool_int *addr_checked )
209 {
210    unsigned      u ;
211    pset_h      conntab = INT_CONNECTIONS( ip ) ;
212
213    *addr_checked = FALSE ;
214
215    for ( u = 0 ; u < pset_count( conntab ) ; u++ )
216    {
217       register channel_s *chp = CHP( pset_pointer( conntab, u ) ) ;
218
219       if ( memcmp( &chp->ch_from, sinp, sizeof( *sinp ) ) == 0 )
220       {
221          *addr_checked = TRUE ;
222          if ( xaddrport(&chp->ch_from) == xaddrport(sinp) )
223             return( chp ) ;
224       }
225    }
226    return( CHANNEL_NULL ) ;
227 }