1 /*
2     OWFS -- One-Wire filesystem
3     OWHTTPD -- One-Wire Web Server
4     Written 2003 Paul H Alfille
5     email: paul.alfille@gmail.com
6     Released under the GPL
7     See the header file: ow.h for full attribution
8     1wire/iButton system from Dallas Semiconductor
9 */
10 
11 /* ow_net holds the network utility routines. Many stolen unashamedly from Steven's Book */
12 /* Much modification by Christian Magnusson especially for Valgrind and embedded */
13 /* non-threaded fixes by Jerry Scharf */
14 
15 #include <config.h>
16 #include "owfs_config.h"
17 #include "ow.h"
18 #include "ow_counters.h"
19 #include "ow_connection.h"
20 
21 /* Locking for thread work */
22 /* Variables only used in this particular file */
23 /* i.e. "locally global" */
24 my_rwlock_t shutdown_mutex_rw ;
25 pthread_mutex_t handler_thread_mutex ;
26 int handler_thread_count ;
27 int shutdown_in_progress ;
28 FILE_DESCRIPTOR_OR_ERROR shutdown_pipe[2] ;
29 
30 
31 /* Prototypes */
32 static GOOD_OR_BAD ServerAddr(const char * default_port, struct connection_out *out);
33 static GOOD_OR_BAD ServerListen(struct connection_out *out);
34 
35 static FILE_DESCRIPTOR_OR_ERROR SetupListenSet( fd_set * listenset ) ;
36 static GOOD_OR_BAD SetupListenSockets( void (*HandlerRoutine) (FILE_DESCRIPTOR_OR_ERROR file_descriptor) ) ;
37 static void CloseListenSockets( void ) ;
38 static void ProcessListenSocket( struct connection_out * out ) ;
39 static void *ProcessAcceptSocket(void *arg) ;
40 static void ProcessListenSet( fd_set * listenset ) ;
41 static GOOD_OR_BAD ListenCycle( void ) ;
42 
ServerAddr(const char * default_port,struct connection_out * out)43 static GOOD_OR_BAD ServerAddr(const char * default_port, struct connection_out *out)
44 {
45 	struct addrinfo hint;
46 	char *p;
47 
48 	if (out->name == NULL) {	// use defaults
49 		out->host = owstrdup("0.0.0.0");
50 		out->service = owstrdup(default_port);
51 	} else if ((p = strrchr(out->name, ':')) == NULL) {
52 		if (strchr(out->name, '.')) {	//probably an address
53 			out->host = owstrdup(out->name);
54 			out->service = owstrdup(default_port);
55 		} else {				// assume a port
56 			out->host = owstrdup("0.0.0.0");
57 			out->service = owstrdup(out->name);
58 		}
59 	} else {
60 		p[0] = '\0';
61 		out->host = owstrdup(out->name);
62 		p[0] = ':';
63 		out->service = owstrdup(&p[1]);
64 	}
65 	memset(&hint, 0, sizeof(struct addrinfo));
66 	hint.ai_flags = AI_PASSIVE;
67 	// SOCK_CLOEXEC causes problems
68 	//	hint.ai_socktype = SOCK_STREAM | SOCK_CLOEXEC ;
69 	hint.ai_socktype = SOCK_STREAM ;
70 #if OW_CYGWIN || defined(__FreeBSD__)
71 	hint.ai_family = AF_INET;	// FreeBSD will bind IP6 preferentially
72 #else							/* __FreeBSD__ */
73 	hint.ai_family = AF_UNSPEC;
74 #endif							/* __FreeBSD__ */
75 
76 	LEVEL_DEBUG("ServerAddr: [%s] [%s]", SAFESTRING(out->host), SAFESTRING(out->service));
77 
78 	if ( getaddrinfo(out->host, out->service, &hint, &out->ai) != 0 ) {
79 		ERROR_CONNECT("GetAddrInfo error [%s]=%s:%s", SAFESTRING(out->name), SAFESTRING(out->host), SAFESTRING(out->service));
80 		return gbBAD;
81 	}
82 	return gbGOOD;
83 }
84 
85 /* for all connection_out
86  * use ip and port to open a socket for listening
87  * systemd and launchd already have the socket
88  * so this routine is not called for them
89  * (caught in ServerOutSetup)
90  * */
ServerListen(struct connection_out * out)91 static GOOD_OR_BAD ServerListen(struct connection_out *out)
92 {
93 	if (out->ai == NULL) {
94 		LEVEL_CONNECT("Server address not yet parsed [%s]", SAFESTRING(out->name));
95 		return gbBAD ;
96 	}
97 
98 	if (out->ai_ok == NULL) {
99 		out->ai_ok = out->ai;
100 	}
101 
102 	do {
103 		int on = 1;
104 		FILE_DESCRIPTOR_OR_ERROR file_descriptor = socket(out->ai_ok->ai_family, out->ai_ok->ai_socktype, out->ai_ok->ai_protocol);
105 
106 		if ( FILE_DESCRIPTOR_NOT_VALID(file_descriptor) ) {
107 			ERROR_CONNECT("Socket problem [%s]", SAFESTRING(out->name));
108 		} else if (setsockopt(file_descriptor, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) != 0) {
109 			ERROR_CONNECT("SetSockOpt problem [%s]", SAFESTRING(out->name));
110 		} else if (bind(file_descriptor, out->ai_ok->ai_addr, out->ai_ok->ai_addrlen) != 0) {
111 			// this is where the default linking to a busy port shows up
112 			ERROR_CONNECT("Bind problem [%s]", SAFESTRING(out->name));
113 		} else if (listen(file_descriptor, SOMAXCONN) != 0) {
114 			ERROR_CONNECT("Listen problem [%s]", SAFESTRING(out->name));
115 		} else {
116 //					fcntl (file_descriptor, F_SETFD, FD_CLOEXEC); // for safe forking
117 			out->file_descriptor = file_descriptor;
118 			return gbGOOD;
119 		}
120 		Test_and_Close(&file_descriptor) ;
121 	} while ((out->ai_ok = out->ai_ok->ai_next));
122 
123 	LEVEL_CONNECT("No good listen network sockets [%s]", SAFESTRING(out->name));
124 	return gbBAD;
125 }
126 
ServerOutSetup(struct connection_out * out)127 GOOD_OR_BAD ServerOutSetup(struct connection_out *out)
128 {
129 	switch ( out->inet_type ) {
130 		case inet_launchd:
131 		case inet_systemd:
132 			// file descriptor already set up
133 			return gbGOOD ;
134 		default:
135 			break ;
136 	}
137 
138 	if ( out->name == NULL ) { // NULL name means default attempt
139 		char * default_port ;
140 		// First time through, try default port
141 		switch (Globals.program_type) {
142 			case program_type_server:
143 			case program_type_external:
144 				default_port = DEFAULT_SERVER_PORT ;
145 				break ;
146 			case program_type_ftpd:
147 				default_port = DEFAULT_FTP_PORT ;
148 				break ;
149 			default:
150 				default_port = NULL ;
151 				break ;
152 		}
153 		if ( default_port != NULL ) { // one of the 2 cases above
154 			RETURN_BAD_IF_BAD( ServerAddr( default_port, out ) ) ;
155 			if ( GOOD(ServerListen(out)) ) {
156 				return gbGOOD ;
157 			}
158 			ERROR_CONNECT("Default port not successful. Try an ephemeral port");
159 		}
160 	}
161 
162 	// second time through, use ephemeral port
163 	RETURN_BAD_IF_BAD( ServerAddr( "0", out ) ) ;
164 
165 	return ServerListen(out) ;
166 }
167 
168 /* MAke a set of the listening sockets to poll for a connection */
169 /* Done by looking though connect_out */
SetupListenSet(fd_set * listenset)170 static FILE_DESCRIPTOR_OR_ERROR SetupListenSet( fd_set * listenset )
171 {
172 	FILE_DESCRIPTOR_OR_ERROR maxfd = FILE_DESCRIPTOR_BAD ;
173 	struct connection_out * out ;
174 
175 	FD_ZERO( listenset ) ;
176 	for (out = Outbound_Control.head; out; out = out->next) {
177 		FILE_DESCRIPTOR_OR_ERROR fd = out->file_descriptor ;
178 		if ( FILE_DESCRIPTOR_VALID( fd ) ) {
179 			FD_SET( fd, listenset ) ;
180 			if ( fd > maxfd ) {
181 				maxfd = fd ;
182 			}
183 		}
184 	}
185 	return maxfd ;
186 }
187 
SetupListenSockets(void (* HandlerRoutine)(FILE_DESCRIPTOR_OR_ERROR file_descriptor))188 static GOOD_OR_BAD SetupListenSockets( void (*HandlerRoutine) (FILE_DESCRIPTOR_OR_ERROR file_descriptor) )
189 {
190 	struct connection_out * out ;
191 	GOOD_OR_BAD any_sockets = gbBAD ;
192 
193 	for (out = Outbound_Control.head; out; out = out->next) {
194 		if ( GOOD( ServerOutSetup( out ) ) ) {
195 			any_sockets = gbGOOD;
196 			ZeroConf_Announce(out);
197 		}
198 		out-> HandlerRoutine = HandlerRoutine ;
199 	}
200 	return any_sockets ;
201 }
202 
203 /* close all connection_out listen sockets */
CloseListenSockets(void)204 static void CloseListenSockets( void )
205 {
206 	struct connection_out * out ;
207 
208 	for (out = Outbound_Control.head; out; out = out->next) {
209 		Test_and_Close( &(out->file_descriptor) ) ;
210 	}
211 }
212 
213 /* Go through list set to find requesting sockets */
ProcessListenSet(fd_set * listenset)214 static void ProcessListenSet( fd_set * listenset )
215 {
216 	struct connection_out * out ;
217 
218 	for (out = Outbound_Control.head; out; out = out->next) {
219 		if ( FD_ISSET( out->file_descriptor, listenset ) ) {
220 			ProcessListenSocket( out ) ;
221 		}
222 	}
223 }
224 
225 /* structure */
226 struct Accept_Socket_Data {
227 	FILE_DESCRIPTOR_OR_ERROR acceptfd;
228 	struct connection_out * out;
229 };
230 
231 /* Wait for a connection
232  * process it
233  * Expects to be called in a loop */
ListenCycle(void)234 static GOOD_OR_BAD ListenCycle( void )
235 {
236 	fd_set listenset ;
237 	FILE_DESCRIPTOR_OR_ERROR maxfd = SetupListenSet( &listenset) ;
238 	if ( FILE_DESCRIPTOR_VALID( maxfd ) ) {
239 		if ( select( maxfd+1, &listenset, NULL, NULL, NULL) > 0 ) {
240 			ProcessListenSet( &listenset) ;
241 			return gbGOOD ;
242 		}
243 	}
244 	return gbBAD ;
245 }
246 
247 // Read data from the waiting socket and do the actual work
ProcessAcceptSocket(void * arg)248 static void *ProcessAcceptSocket(void *arg)
249 {
250 	struct Accept_Socket_Data * asd = (struct Accept_Socket_Data *) arg;
251 	DETACH_THREAD;
252 
253 	// Do the actual work
254 	asd->out->HandlerRoutine( asd->acceptfd );
255 
256 	// cleanup
257 	Test_and_Close( &(asd->acceptfd) );
258 	owfree(asd);
259 	LEVEL_DEBUG("Normal completion.");
260 
261 	// All done. If shutdown in progress and this is a last handler thread, send a message to the main thread.
262 	RWLOCK_RLOCK( shutdown_mutex_rw ) ;
263 	_MUTEX_LOCK( handler_thread_mutex ) ;
264 	--handler_thread_count ;
265 	if ( shutdown_in_progress && handler_thread_count==0) {
266 		if ( FILE_DESCRIPTOR_VALID( shutdown_pipe[fd_pipe_write] ) ) {
267 			ignore_result = write( shutdown_pipe[fd_pipe_write],"X",1) ; //dummy payload
268 		}
269 	}
270 	_MUTEX_UNLOCK( handler_thread_mutex ) ;
271 	RWLOCK_RUNLOCK( shutdown_mutex_rw ) ;
272 
273 	return VOID_RETURN;
274 }
275 
ProcessListenSocket(struct connection_out * out)276 static void ProcessListenSocket( struct connection_out * out )
277 {
278 	FILE_DESCRIPTOR_OR_ERROR acceptfd;
279 	struct Accept_Socket_Data * asd ;
280 
281 	acceptfd = accept(out->file_descriptor, NULL, NULL);
282 
283 	if ( FILE_DESCRIPTOR_NOT_VALID( acceptfd ) ) {
284 		return ;
285 	}
286 
287 	// allocate space to pass variables to thread
288 	// MUST be cleaned up in thread handler, not in this routine
289 	asd = owmalloc( sizeof(struct Accept_Socket_Data) ) ;
290 	if ( asd == NULL ) {
291 		LEVEL_DEBUG("Could not allocate memory to handle this request");
292 		close( acceptfd ) ;
293 		return ;
294 	}
295 	asd->acceptfd = acceptfd ;
296 	asd->out = out ;
297 
298 	// Launch Handler thread only if shutdown not in progress
299 	RWLOCK_RLOCK( shutdown_mutex_rw ) ;
300 	if ( ! shutdown_in_progress ) {
301 		pthread_t tid;
302 		_MUTEX_LOCK( handler_thread_mutex ) ;
303 		++handler_thread_count ;
304 		_MUTEX_UNLOCK( handler_thread_mutex ) ;
305 		if ( pthread_create(&tid, DEFAULT_THREAD_ATTR, ProcessAcceptSocket, asd ) != 0 ) {
306 			// Do it in the main routine rather than a thread
307 			LEVEL_DEBUG("Thread creation problem. Will handle request unthreaded");
308 			ProcessAcceptSocket(asd) ;
309 		}
310 	}
311 
312 	RWLOCK_RUNLOCK( shutdown_mutex_rw ) ;
313 	// asd does not leak; it is passed to ProcessAcceptSocket in another thread.
314 }
315 
316 /* Setup Servers -- select on each port */
317 /* Not only sets up, we start a loop for new connections and processes them,
318  * basically, this is the main loop of the owserver and owhttpd program
319  * */
ServerProcess(void (* HandlerRoutine)(FILE_DESCRIPTOR_OR_ERROR file_descriptor))320 void ServerProcess(void (*HandlerRoutine) (FILE_DESCRIPTOR_OR_ERROR file_descriptor))
321 {
322 	/* Locking for thread work */
323 	int need_to_read_pipe ;
324 
325 	handler_thread_count = 0 ;
326 	shutdown_in_progress = 0 ;
327 	Init_Pipe( shutdown_pipe ) ;
328 
329 	RWLOCK_INIT( shutdown_mutex_rw ) ;
330 	_MUTEX_INIT(handler_thread_mutex);
331 
332 	if ( pipe( shutdown_pipe ) != 0 ) {
333 		ERROR_DEFAULT("Cannot allocate a shutdown pipe. The program shutdown may be messy");
334 		Init_Pipe( shutdown_pipe ) ;
335 	}
336 
337 	if ( GOOD( SetupListenSockets( HandlerRoutine ) ) ) {
338 		Announce_Systemd() ; // systemd mode -- ready for business
339 		while (	GOOD( ListenCycle() ) ) {
340 		}
341 
342 		// Make sure all the handler threads are complete before closing down
343 		RWLOCK_WLOCK( shutdown_mutex_rw ) ;
344 		shutdown_in_progress = 1 ; // Signal time to wrap up
345 		// need to test if there is a handler to wait for before closing
346 		// note that a new one can't be started while the write lock is held
347 		need_to_read_pipe = handler_thread_count>0 && FILE_DESCRIPTOR_VALID( shutdown_pipe[fd_pipe_read] )  ;
348 		RWLOCK_WUNLOCK( shutdown_mutex_rw ) ;
349 
350 		if ( need_to_read_pipe ) {
351 			// now wait for write to pipe from last handler thread
352 			char buf[2] ;
353 			ignore_result = read( shutdown_pipe[fd_pipe_read],buf,1) ;
354 		}
355 		Test_and_Close_Pipe(shutdown_pipe) ;
356 		RWLOCK_DESTROY(shutdown_mutex_rw) ;
357 		_MUTEX_DESTROY(handler_thread_mutex) ;
358 		CloseListenSockets() ;
359 
360 	} else {
361 		LEVEL_DEFAULT("Isolated from any control -- exit") ;
362 	}
363 	/* Cleanup that may never be reached */
364 	return;
365 }
366 
367 /* Call from elseware
368  * specifically the configuration monitoring code
369  * to stop the loops
370  * */
InterruptListening(void)371 void InterruptListening( void )
372 {
373 	// Launch Handler thread only if shutdown not in progress
374 	LEVEL_DEBUG("Stop listening process") ;
375 	RWLOCK_WLOCK( shutdown_mutex_rw ) ;
376 	shutdown_in_progress = 1 ;
377 	RWLOCK_WUNLOCK( shutdown_mutex_rw ) ;
378 	LEVEL_DEBUG("Listening loop stopped") ;
379 
380 	// this means no processing is going on
381 	// and no more calls to connection_out structures
382 	// so can close those file descriptors bore exit or restart
383 }
384 
385