func17()1 /* source: xio-listen.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4 
5 /* this file contains the source for listen socket options */
6 
7 #include "xiosysincludes.h"
8 
9 #if WITH_LISTEN
10 
11 #include "xioopen.h"
12 #include "xio-named.h"
13 #include "xio-socket.h"
14 #include "xio-ip.h"
15 #include "xio-ip4.h"
16 #include "xio-listen.h"
17 #include "xio-tcpwrap.h"
18 
19 /***** LISTEN options *****/
20 const struct optdesc opt_backlog = { "backlog",   NULL, OPT_BACKLOG,     GROUP_LISTEN, PH_LISTEN, TYPE_INT,    OFUNC_SPEC };
21 const struct optdesc opt_fork    = { "fork",      NULL, OPT_FORK,        GROUP_CHILD,   PH_PASTACCEPT, TYPE_BOOL,  OFUNC_SPEC };
22 const struct optdesc opt_max_children = { "max-children",      NULL, OPT_MAX_CHILDREN,        GROUP_CHILD,   PH_PASTACCEPT, TYPE_INT,  OFUNC_SPEC };
23 /**/
24 #if (WITH_UDP || WITH_TCP)
25 const struct optdesc opt_range   = { "range",     NULL, OPT_RANGE,       GROUP_RANGE,  PH_ACCEPT, TYPE_STRING, OFUNC_SPEC };
26 #endif
27 const struct optdesc opt_accept_timeout = { "accept-timeout", "listen-timeout", OPT_ACCEPT_TIMEOUT, GROUP_LISTEN, PH_LISTEN, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.accept_timeout) };
28 
29 
30 /*
31    applies and consumes the following option:
32    PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
33    PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
34    OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
35    OPT_SOURCEPORT, OPT_LOWPORT, cloexec
36  */
37 int
38    xioopen_listen(struct single *xfd, int xioflags,
39 		  struct sockaddr *us, socklen_t uslen,
40 		  struct opt *opts, struct opt *opts0,
41 		  int pf, int socktype, int proto) {
42    int level;
43    int result;
44 
45 #if WITH_RETRY
46    if (xfd->forever || xfd->retry) {
47       level = E_INFO;
48    } else
49 #endif /* WITH_RETRY */
50       level = E_ERROR;
51 
52    while (true) {	/* loop over failed attempts */
53 
54       /* tcp listen; this can fork() for us; it only returns on error or on
55 	 successful establishment of tcp connection */
56       result = _xioopen_listen(xfd, xioflags,
57 			       (struct sockaddr *)us, uslen,
58 			       opts, pf, socktype, proto, level);
59 	 /*! not sure if we should try again on retry/forever */
60       switch (result) {
61       case STAT_OK: break;
62 #if WITH_RETRY
63       case STAT_RETRYLATER:
64       case STAT_RETRYNOW:
65 	 if (xfd->forever || xfd->retry) {
66 	    dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
67 	    if (result == STAT_RETRYLATER) {
68 	       Nanosleep(&xfd->intervall, NULL);
69 	    }
70 	    dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
71 	    --xfd->retry;
72 	    continue;
73 	 }
74 	 return STAT_NORETRY;
75 #endif /* WITH_RETRY */
76       default:
77 	 return result;
78       }
79 
80       break;
81    }	/* drop out on success */
82 
83    return result;
84 }
85 
86 
87 /* creates the listening socket, bind, applies options; waits for incoming
88    connection, checks its source address and port. Depending on fork option, it
89    may fork a subprocess.
90    pf specifies the syntax expected for range option. In the case of generic
91    socket it is 0 (expecting raw binary data), and the real pf can be obtained
92    from us->af_family; for other socket types pf == us->af_family
93    Returns 0 if a connection was accepted; with fork option, this is always in
94    a subprocess!
95    Other return values indicate a problem; this can happen in the master
96    process or in a subprocess.
97    This function does not retry. If you need retries, handle this in a
98    loop in the calling function (and always provide the options...)
99    After fork, we set the forever/retry of the child process to 0
100    applies and consumes the following option:
101    PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
102    PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
103    OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
104    OPT_SOURCEPORT, OPT_LOWPORT, cloexec
105  */
106 int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
107 		 struct opt *opts, int pf, int socktype, int proto, int level) {
108    struct sockaddr sa;
109    socklen_t salen;
110    int backlog = 5;	/* why? 1 seems to cause problems under some load */
111    char *rangename;
112    bool dofork = false;
113    int maxchildren = 0;
114    char infobuff[256];
115    char lisname[256];
116    union sockaddr_union _peername;
117    union sockaddr_union _sockname;
118    union sockaddr_union *pa = &_peername;	/* peer address */
119    union sockaddr_union *la = &_sockname;	/* local address */
120    socklen_t pas = sizeof(_peername);	/* peer address size */
121    socklen_t las = sizeof(_sockname);	/* local address size */
122    int result;
123 
124    retropt_bool(opts, OPT_FORK, &dofork);
125 
126    if (dofork) {
127       if (!(xioflags & XIO_MAYFORK)) {
128 	 Error("option fork not allowed here");
129 	 return STAT_NORETRY;
130       }
131       xfd->flags |= XIO_DOESFORK;
132    }
133 
134    retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
135 
136    if (! dofork && maxchildren) {
137        Error("option max-children not allowed without option fork");
138        return STAT_NORETRY;
139    }
140 
141    if (applyopts_single(xfd, opts, PH_INIT) < 0)  return -1;
142 
143    if (dofork) {
144       xiosetchilddied();	/* set SIGCHLD handler */
145    }
146 
147    if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
148       return STAT_RETRYLATER;
149    }
150    applyopts(xfd->fd, opts, PH_PASTSOCKET);
151 
152    applyopts_offset(xfd, opts);
153    applyopts_cloexec(xfd->fd, opts);
154 
155    applyopts(xfd->fd, opts, PH_PREBIND);
156    applyopts(xfd->fd, opts, PH_BIND);
157    if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
158       Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
159 	   sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
160 	   strerror(errno));
161       Close(xfd->fd);
162       return STAT_RETRYLATER;
163    }
164 
165 #if WITH_UNIX
166    if (us->sa_family == AF_UNIX) {
167       if (((union sockaddr_union *)us)->un.sun_path[0] != '\0') {
168 	 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
169       } else {
170 	 applyopts(xfd->fd, opts, PH_FD);
171       }
172    }
173 #endif
174    /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
175       fields that we want to know. */
176    salen = sizeof(sa);
177    if (Getsockname(xfd->fd, us, &uslen) < 0) {
178       Warn4("getsockname(%d, %p, {%d}): %s",
179 	    xfd->fd, &us, uslen, strerror(errno));
180    }
181 
182    applyopts(xfd->fd, opts, PH_PASTBIND);
183 #if WITH_UNIX
184    if (us->sa_family == AF_UNIX) {
185       if (((union sockaddr_union *)us)->un.sun_path[0] != '\0') {
186 	 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
187 	 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
188 	 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
189       } else {
190 	 applyopts(xfd->fd, opts, PH_EARLY);
191 	 applyopts(xfd->fd, opts, PH_PREOPEN);
192       }
193    }
194 #endif /* WITH_UNIX */
195 
196 #if WITH_IP4 /*|| WITH_IP6*/
197    if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
198       if (xioparserange(rangename, pf, &xfd->para.socket.range)
199 	  < 0) {
200 	 free(rangename);
201 	 return STAT_NORETRY;
202       }
203       free(rangename);
204       xfd->para.socket.dorange = true;
205    }
206 #endif
207 
208 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
209    xio_retropt_tcpwrap(xfd, opts);
210 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
211 
212 #if WITH_TCP || WITH_UDP
213    if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) {
214       xfd->para.socket.ip.dosourceport = true;
215    }
216    retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
217 #endif /* WITH_TCP || WITH_UDP */
218 
219    applyopts(xfd->fd, opts, PH_PRELISTEN);
220    retropt_int(opts, OPT_BACKLOG, &backlog);
221    applyopts(xfd->fd, opts, PH_LISTEN);
222    if (Listen(xfd->fd, backlog) < 0) {
223       Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
224       return STAT_RETRYLATER;
225    }
226 
227    if (xioopts.logopt == 'm') {
228       Info("starting accept loop, switching to syslog");
229       diag_set('y', xioopts.syslogfac);  xioopts.logopt = 'y';
230    } else {
231       Info("starting accept loop");
232    }
233    while (true) {	/* but we only loop if fork option is set */
234       char peername[256];
235       char sockname[256];
236       int ps;		/* peer socket */
237 
238       pa = &_peername;
239       la = &_sockname;
240       salen = sizeof(struct sockaddr);
241       do {
242 	 /*? int level = E_ERROR;*/
243 	 Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
244 	 if (xfd->para.socket.accept_timeout.tv_sec > 0 ||
245 	     xfd->para.socket.accept_timeout.tv_usec > 0) {
246 	    fd_set rfd;
247 	    struct timeval tmo;
248 	    FD_ZERO(&rfd);
249 	    FD_SET(xfd->fd, &rfd);
250 	    tmo.tv_sec = xfd->para.socket.accept_timeout.tv_sec;
251 	    tmo.tv_usec = xfd->para.socket.accept_timeout.tv_usec;
252 	    while (1) {
253 	       if (Select(xfd->fd+1, &rfd, NULL, NULL, &tmo) < 0) {
254 		  if (errno != EINTR) {
255 		     Error5("Select(%d, &0x%lx, NULL, NULL, {%ld.%06ld}): %s", xfd->fd+1, 1L<<(xfd->fd+1),
256 			    xfd->para.socket.accept_timeout.tv_sec, xfd->para.socket.accept_timeout.tv_usec,
257 			    strerror(errno));
258 		  }
259 	       } else {
260 		  break;
261 	       }
262 	    }
263 	    if (!FD_ISSET(xfd->fd, &rfd)) {
264 	       struct sigaction act;
265 
266 	       Warn1("accept: %s", strerror(ETIMEDOUT));
267 	       Close(xfd->fd);
268 	       Notice("Waiting for child processes to terminate");
269 	       memset(&act, 0, sizeof(struct sigaction));
270 	       act.sa_flags   = SA_NOCLDSTOP/*|SA_RESTART*/
271 #ifdef SA_SIGINFO /* not on Linux 2.0(.33) */
272 		  |SA_SIGINFO
273 #endif
274 #ifdef SA_NOMASK
275 		  |SA_NOMASK
276 #endif
277 		  ;
278 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
279 	       act.sa_sigaction = 0;
280 #else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */
281 	       act.sa_handler = 0;
282 #endif
283 	       sigemptyset(&act.sa_mask);
284 	       Sigaction(SIGCHLD, &act, NULL);
285 	       wait(NULL);
286 	       Exit(0);
287 	    }
288 	 }
289 	 ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen);
290 	 if (ps >= 0) {
291 	    /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
292 	    break;	/* success, break out of loop */
293 	 }
294 	 if (errno == EINTR) {
295 	    continue;
296 	 }
297 	 if (errno == ECONNABORTED) {
298 	    Notice4("accept(%d, %p, {"F_socklen"}): %s",
299 		    xfd->fd, &sa, salen, strerror(errno));
300 	    continue;
301 	 }
302 	 Msg4(level, "accept(%d, %p, {"F_socklen"}): %s",
303 	      xfd->fd, &sa, salen, strerror(errno));
304 	 Close(xfd->fd);
305 	 return STAT_RETRYLATER;
306       } while (true);
307       applyopts_cloexec(ps, opts);
308       if (Getpeername(ps, &pa->soa, &pas) < 0) {
309 	 Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
310 	       ps, pa, pas, strerror(errno));
311 	 pa = NULL;
312       }
313       if (Getsockname(ps, &la->soa, &las) < 0) {
314 	 Warn4("getsockname(%d, %p, {"F_socklen"}): %s",
315 	       ps, la, las, strerror(errno));
316 	 la = NULL;
317       }
318       Notice2("accepting connection from %s on %s",
319 	      pa?
320 	      sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL",
321 	      la?
322 	      sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL");
323 
324       if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) {
325 	 if (Shutdown(ps, 2) < 0) {
326 	    Info2("shutdown(%d, 2): %s", ps, strerror(errno));
327 	 }
328 	 Close(ps);
329 	 continue;
330       }
331 
332       if (pa != NULL)
333 	 Info1("permitting connection from %s",
334 	       sockaddr_info((struct sockaddr *)pa, pas,
335 			     infobuff, sizeof(infobuff)));
336 
337       if (dofork) {
338 	 pid_t pid;	/* mostly int; only used with fork */
339          sigset_t mask_sigchld;
340 
341          /* we must prevent that the current packet triggers another fork;
342             therefore we wait for a signal from the recent child: USR1
343             indicates that is has consumed the last packet; CHLD means it has
344             terminated */
345          /* block SIGCHLD and SIGUSR1 until parent is ready to react */
346          sigemptyset(&mask_sigchld);
347          sigaddset(&mask_sigchld, SIGCHLD);
348          Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
349 
350 	 if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) {
351 	    Close(xfd->fd);
352 	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
353 	    return STAT_RETRYLATER;
354 	 }
355 	 if (pid == 0) {	/* child */
356 	    pid_t cpid = Getpid();
357 	    Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
358 
359 	    Info1("just born: child process "F_pid, cpid);
360 	    xiosetenvulong("PID", cpid, 1);
361 
362 	    if (Close(xfd->fd) < 0) {
363 	       Info2("close(%d): %s", xfd->fd, strerror(errno));
364 	    }
365 	    xfd->fd = ps;
366 
367 #if WITH_RETRY
368 	    /* !? */
369 	    xfd->forever = false;  xfd->retry = 0;
370 	    level = E_ERROR;
371 #endif /* WITH_RETRY */
372 
373 	    break;
374 	 }
375 
376 	 /* server: continue loop with listen */
377 	 /* shutdown() closes the socket even for the child process, but
378 	    close() does what we want */
379 	 if (Close(ps) < 0) {
380 	    Info2("close(%d): %s", ps, strerror(errno));
381 	 }
382 
383          /* now we are ready to handle signals */
384          Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
385 
386 	 while (maxchildren) {
387 	    if (num_child < maxchildren) break;
388 	    Notice("maxchildren are active, waiting");
389 	    /* UINT_MAX would even be nicer, but Openindiana works only
390 	       with 31 bits */
391 	    while (!Sleep(INT_MAX)) ;	/* any signal lets us continue */
392 	 }
393 	 Info("still listening");
394       } else {
395 	 if (Close(xfd->fd) < 0) {
396 	    Info2("close(%d): %s", xfd->fd, strerror(errno));
397 	 }
398 	 xfd->fd = ps;
399 	break;
400       }
401    }
402 
403    applyopts(xfd->fd, opts, PH_FD);
404    applyopts(xfd->fd, opts, PH_PASTSOCKET);
405    applyopts(xfd->fd, opts, PH_CONNECTED);
406    if ((result = _xio_openlate(xfd, opts)) < 0)
407       return result;
408 
409    /* set the env vars describing the local and remote sockets */
410    if (la != NULL)  xiosetsockaddrenv("SOCK", la, las, proto);
411    if (pa != NULL)  xiosetsockaddrenv("PEER", pa, pas, proto);
412 
413    return 0;
414 }
415 
416 #endif /* WITH_LISTEN */
417