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
9 #include "config.h"
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <syslog.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19
20 #include "int.h"
21 #include "msg.h"
22 #include "log.h"
23 #include "tcpint.h"
24 #include "udpint.h"
25 #include "sconf.h"
26 #include "intcommon.h"
27 #include "child.h"
28 #include "state.h"
29 #include "main.h"
30 #include "signals.h"
31 #include "xconfig.h"
32 #include <netdb.h>
33
34 static void start_server( struct intercept_s *ip );
35 static void terminate_server( struct intercept_s *ip );
36
37 typedef struct intercept_s *(*initfunc)() ;
38
39 struct lookup_table
40 {
41 initfunc initializer ;
42 int socket_type ;
43 } ;
44
45
46 static struct lookup_table intercept_lookup_table[] =
47 {
48 { di_init, SOCK_DGRAM },
49 { si_init, SOCK_STREAM },
50 { NULL, 0 }
51 } ;
52
53
54 /*
55 * This variable has file scope for the benefit of the signal handler
56 */
57 static struct intercept_s *intp = NULL;
58
59
60
find_initializer(int type)61 static initfunc find_initializer( int type )
62 {
63 struct lookup_table *ltp ;
64
65 for ( ltp = intercept_lookup_table ; ltp->initializer ; ltp++ )
66 if ( ltp->socket_type == type )
67 return( ltp->initializer ) ;
68 msg( LOG_ERR, "find_initializer", "No initializer for type %d", type ) ;
69 _exit( 0 ) ;
70 /* NOTREACHED */
71 return (initfunc)0;
72 }
73
74
75 /*
76 * This function is the interface of the intercept code with the rest of
77 * the program.
78 */
intercept(struct server * serp)79 void intercept( struct server *serp )
80 {
81 struct service *sp = SERVER_SERVICE( serp ) ;
82 initfunc initializer ;
83
84 #ifdef DEBUG_INTERCEPTOR
85 if ( debug.on )
86 {
87 msg( LOG_DEBUG, "intercept", "%d is sleeping", getpid() ) ;
88 sleep( 10 ) ;
89 }
90 #endif
91
92 initializer = find_initializer( SVC_SOCKET_TYPE( sp ) ) ;
93 intp = (*initializer)( serp ) ;
94 start_server( intp ) ;
95 (*intp->int_ops->mux)() ;
96 terminate_server( intp ) ;
97 /*
98 * the terminate_server function should not return but even if it
99 * does, child_process will do the _exit.
100 */
101 }
102
103
104 /*
105 * Create a socket and bind it to (INADDR_LOOPBACK,0)
106 */
get_server_socket(struct intercept_s * ip)107 static int get_server_socket( struct intercept_s *ip )
108 {
109 struct service *sp = SERVER_SERVICE( INT_SERVER( ip ) ) ;
110 union xsockaddr *sinp = INT_LOCALADDR( ip ) ;
111 int sd ;
112 socklen_t size ;
113 const char *func = "get_server_socket" ;
114
115 if( SC_IPV6(SVC_CONF(sp)) ) {
116 struct addrinfo hint, *res = NULL;
117 memset(&hint, 0, sizeof(struct addrinfo));
118 hint.ai_family = AF_INET6;
119 hint.ai_flags = AI_NUMERICHOST;
120 sinp->sa_in6.sin6_family = AF_INET6;
121 sinp->sa_in6.sin6_port = 0;
122 if( getaddrinfo("::1", NULL, &hint, &res) != 0 )
123 int_fail( ip, "can't find ::1" );
124 if( res == NULL )
125 int_fail( ip, "no results for ::1" );
126 if( res->ai_family != AF_INET6 )
127 int_fail( ip, "non IPv6 result for ::1" );
128 memcpy(sinp, res->ai_addr, sizeof( struct sockaddr_in6 ));
129 freeaddrinfo(res);
130 size = sizeof(struct sockaddr_in6);
131 } else if( SC_IPV4(SVC_CONF(sp)) ) {
132 sinp->sa_in.sin_family = AF_INET;
133 sinp->sa_in.sin_port = 0;
134 sinp->sa_in.sin_addr.s_addr = inet_addr( "127.0.0.1" );
135 size = sizeof(struct sockaddr_in);
136 } else
137 int_fail( ip, "unknown socket family" );
138
139 if ( ( sd = socket( sinp->sa.sa_family, SVC_SOCKET_TYPE( sp ), SC_PROTOVAL(SVC_CONF(sp)) ) ) == -1 )
140 int_fail( ip, "socket creation" ) ;
141
142 if ( bind( sd, SA( sinp ), size ) == -1 )
143 int_fail( ip, "bind" ) ;
144
145 size = sizeof( *sinp ) ;
146 if ( getsockname( sd, (struct sockaddr *)( sinp ), &size ) == -1 )
147 int_fail( ip, "getsockname" ) ;
148
149 if ( debug.on )
150 msg( LOG_DEBUG, func, "address = %s, port = %d",
151 xaddrname( sinp ), ntohs( xaddrport( sinp ) ) ) ;
152
153 if ( ip->int_socket_type == SOCK_STREAM )
154 (void) listen( sd, LISTEN_BACKLOG ) ;
155
156 return( sd ) ;
157 }
158
159
start_server(struct intercept_s * ip)160 static void start_server( struct intercept_s *ip )
161 {
162 struct server *serp = INT_SERVER( ip ) ;
163 struct service *sp = SERVER_SERVICE( serp ) ;
164 int server_socket ;
165 pid_t pid ;
166
167 server_socket = get_server_socket( ip ) ;
168
169 pid = fork() ;
170
171 switch ( pid )
172 {
173 case -1:
174 int_fail( ip, "fork" ) ;
175 /* NOTREACHED */
176
177 case 0:
178 CONN_SET_DESCRIPTOR( SERVER_CONNECTION( serp ), server_socket ) ;
179 SVC_MAKE_EXTERNAL( sp ) ; /* avoid looping */
180 child_process( serp ) ;
181 /* NOTREACHED */
182
183 default:
184 SERVER_SET_PID( serp, pid ) ;
185 (void) Sclose( server_socket ) ;
186 }
187 }
188
189
190
191 /*
192 * Return value:
193 * OK if the server died
194 * FAILED otherwise
195 */
wait_child(struct intercept_s * ip)196 static status_e wait_child( struct intercept_s *ip )
197 {
198 const char *func = "wait_child" ;
199 int status ;
200 status_e ret = FAILED;
201 pid_t pid ;
202
203 while( (pid = waitpid( -1, &status, WNOHANG )) != 0 )
204 {
205
206 if ( pid == -1 )
207 {
208 if ( errno != EINTR )
209 {
210 msg( LOG_ERR, func, "wait: %m" ) ;
211 return( ret ) ;
212 }
213 }
214 else if ( pid == SERVER_PID( INT_SERVER( ip ) ) )
215 {
216 if ( PROC_STOPPED( status ) )
217 ret = FAILED;
218 SERVER_SET_EXIT_STATUS( INT_SERVER( ip ), status ) ;
219 ret = OK;
220 }
221 else
222 {
223 unsigned u;
224
225 /* Ideally, this will never be executed */
226 msg( LOG_ERR, func,
227 "wait returned pid of unknown process: %d", pid ) ;
228
229 /* Since we don't have the intercept pointer to this service,
230 * do our best to shut it down safely...
231 */
232 for( u = 0; u < pset_count( SERVERS(ps) ); u++ ) {
233 struct server *p = SERP( pset_pointer( SERVERS(ps), u) );
234
235 if( (p != NULL) && (SERVER_PID(p) == pid) ) {
236 struct service *sp = SERVER_SERVICE(p);
237 struct service_config *scp = SVC_CONF(sp);
238
239 if( SC_PROTOVAL(scp) == IPPROTO_TCP ) {
240 SERVER_SET_EXIT_STATUS( p, status );
241 si_exit();
242 } else if( SC_PROTOVAL(scp) == IPPROTO_UDP ) {
243 SERVER_SET_EXIT_STATUS( p, status );
244 di_exit();
245 } else {
246 msg( LOG_ERR, func, "Don't know how to exit %d", pid);
247 }
248 break;
249 }
250 }
251 }
252 }
253
254 return ret;
255 }
256
257
terminate_server(struct intercept_s * ip)258 static void terminate_server( struct intercept_s *ip )
259 {
260 pid_t pid = SERVER_PID( INT_SERVER( intp ) ) ;
261
262 if ( pid > 0 )
263 (void) kill( pid, SIGKILL ) ;
264
265 /*
266 * Normally, wait_child should never return since a SIGCHLD will
267 * invoke the signal handler which will then call the exit function.
268 */
269 if ( wait_child( ip ) == OK )
270 (*intp->int_ops->exit)() ;
271 }
272
273
int_sighandler(int sig)274 void int_sighandler( int sig )
275 {
276 const char *func = "int_sighandler" ;
277
278 if ( debug.on )
279 msg( LOG_DEBUG, func, "Received signal %s", sig_name( sig ) ) ;
280
281 if ( sig == SERVER_EXIT_SIG )
282 {
283 if ( wait_child( intp ) == OK )
284 (*intp->int_ops->exit)() ;
285 }
286 else if ( sig == INTERCEPT_SIG )
287 INTERCEPT( intp ) = FALSE ;
288 else if ( sig == SIGTERM )
289 terminate_server( intp ) ;
290 }
291