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