version 1.7.3.0
[socat.git] / xio-listen.c
blob11a6b9a0bdb938741e21ca8febf6278b9c4ddc4f
1 /* source: xio-listen.c */
2 /* Copyright Gerhard Rieger */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for listen socket options */
7 #include "xiosysincludes.h"
9 #if WITH_LISTEN
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"
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
30 applies and consumes the following option:
31 PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
32 PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
33 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
34 OPT_SOURCEPORT, OPT_LOWPORT, cloexec
36 int
37 xioopen_listen(struct single *xfd, int xioflags,
38 struct sockaddr *us, socklen_t uslen,
39 struct opt *opts, struct opt *opts0,
40 int pf, int socktype, int proto) {
41 int level;
42 int result;
44 #if WITH_RETRY
45 if (xfd->forever || xfd->retry) {
46 level = E_INFO;
47 } else
48 #endif /* WITH_RETRY */
49 level = E_ERROR;
51 while (true) { /* loop over failed attempts */
53 /* tcp listen; this can fork() for us; it only returns on error or on
54 successful establishment of tcp connection */
55 result = _xioopen_listen(xfd, xioflags,
56 (struct sockaddr *)us, uslen,
57 opts, pf, socktype, proto, level);
58 /*! not sure if we should try again on retry/forever */
59 switch (result) {
60 case STAT_OK: break;
61 #if WITH_RETRY
62 case STAT_RETRYLATER:
63 case STAT_RETRYNOW:
64 if (xfd->forever || xfd->retry) {
65 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
66 if (result == STAT_RETRYLATER) {
67 Nanosleep(&xfd->intervall, NULL);
69 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
70 --xfd->retry;
71 continue;
73 return STAT_NORETRY;
74 #endif /* WITH_RETRY */
75 default:
76 return result;
79 break;
80 } /* drop out on success */
82 return result;
86 /* creates the listening socket, bind, applies options; waits for incoming
87 connection, checks its source address and port. Depending on fork option, it
88 may fork a subprocess.
89 pf specifies the syntax expected for range option. In the case of generic
90 socket it is 0 (expecting raw binary data), and the real pf can be obtained
91 from us->af_family; for other socket types pf == us->af_family
92 Returns 0 if a connection was accepted; with fork option, this is always in
93 a subprocess!
94 Other return values indicate a problem; this can happen in the master
95 process or in a subprocess.
96 This function does not retry. If you need retries, handle this in a
97 loop in the calling function (and always provide the options...)
98 After fork, we set the forever/retry of the child process to 0
99 applies and consumes the following option:
100 PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
101 PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
102 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
103 OPT_SOURCEPORT, OPT_LOWPORT, cloexec
105 int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
106 struct opt *opts, int pf, int socktype, int proto, int level) {
107 struct sockaddr sa;
108 socklen_t salen;
109 int backlog = 5; /* why? 1 seems to cause problems under some load */
110 char *rangename;
111 bool dofork = false;
112 int maxchildren = 0;
113 char infobuff[256];
114 char lisname[256];
115 union sockaddr_union _peername;
116 union sockaddr_union _sockname;
117 union sockaddr_union *pa = &_peername; /* peer address */
118 union sockaddr_union *la = &_sockname; /* local address */
119 socklen_t pas = sizeof(_peername); /* peer address size */
120 socklen_t las = sizeof(_sockname); /* local address size */
121 int result;
123 retropt_bool(opts, OPT_FORK, &dofork);
125 if (dofork) {
126 if (!(xioflags & XIO_MAYFORK)) {
127 Error("option fork not allowed here");
128 return STAT_NORETRY;
130 xfd->flags |= XIO_DOESFORK;
133 retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
135 if (! dofork && maxchildren) {
136 Error("option max-children not allowed without option fork");
137 return STAT_NORETRY;
140 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
142 if (dofork) {
143 xiosetchilddied(); /* set SIGCHLD handler */
146 if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
147 return STAT_RETRYLATER;
150 applyopts_cloexec(xfd->fd, opts);
152 applyopts(xfd->fd, opts, PH_PREBIND);
153 applyopts(xfd->fd, opts, PH_BIND);
154 if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
155 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
156 sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
157 strerror(errno));
158 Close(xfd->fd);
159 return STAT_RETRYLATER;
162 #if WITH_UNIX
163 if (us->sa_family == AF_UNIX) {
164 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
166 #endif
167 /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
168 fields that we want to know. */
169 salen = sizeof(sa);
170 if (Getsockname(xfd->fd, us, &uslen) < 0) {
171 Warn4("getsockname(%d, %p, {%d}): %s",
172 xfd->fd, &us, uslen, strerror(errno));
175 applyopts(xfd->fd, opts, PH_PASTBIND);
176 #if WITH_UNIX
177 if (us->sa_family == AF_UNIX) {
178 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
179 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
180 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
182 #endif /* WITH_UNIX */
184 #if WITH_IP4 /*|| WITH_IP6*/
185 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
186 if (xioparserange(rangename, pf, &xfd->para.socket.range)
187 < 0) {
188 free(rangename);
189 return STAT_NORETRY;
191 free(rangename);
192 xfd->para.socket.dorange = true;
194 #endif
196 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
197 xio_retropt_tcpwrap(xfd, opts);
198 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
200 #if WITH_TCP || WITH_UDP
201 if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) {
202 xfd->para.socket.ip.dosourceport = true;
204 retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
205 #endif /* WITH_TCP || WITH_UDP */
207 applyopts(xfd->fd, opts, PH_PRELISTEN);
208 retropt_int(opts, OPT_BACKLOG, &backlog);
209 if (Listen(xfd->fd, backlog) < 0) {
210 Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
211 return STAT_RETRYLATER;
214 if (xioopts.logopt == 'm') {
215 Info("starting accept loop, switching to syslog");
216 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
217 } else {
218 Info("starting accept loop");
220 while (true) { /* but we only loop if fork option is set */
221 char peername[256];
222 char sockname[256];
223 int ps; /* peer socket */
225 pa = &_peername;
226 la = &_sockname;
227 salen = sizeof(struct sockaddr);
228 do {
229 /*? int level = E_ERROR;*/
230 Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
231 ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen);
232 if (ps >= 0) {
233 /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
234 break; /* success, break out of loop */
236 if (errno == EINTR) {
237 continue;
239 if (errno == ECONNABORTED) {
240 Notice4("accept(%d, %p, {"F_socklen"}): %s",
241 xfd->fd, &sa, salen, strerror(errno));
242 continue;
244 Msg4(level, "accept(%d, %p, {"F_socklen"}): %s",
245 xfd->fd, &sa, salen, strerror(errno));
246 Close(xfd->fd);
247 return STAT_RETRYLATER;
248 } while (true);
249 applyopts_cloexec(ps, opts);
250 if (Getpeername(ps, &pa->soa, &pas) < 0) {
251 Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
252 ps, pa, pas, strerror(errno));
253 pa = NULL;
255 if (Getsockname(ps, &la->soa, &las) < 0) {
256 Warn4("getsockname(%d, %p, {"F_socklen"}): %s",
257 ps, la, las, strerror(errno));
258 la = NULL;
260 Notice2("accepting connection from %s on %s",
262 sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL",
264 sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL");
266 if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) {
267 if (Shutdown(ps, 2) < 0) {
268 Info2("shutdown(%d, 2): %s", ps, strerror(errno));
270 Close(ps);
271 continue;
274 if (pa != NULL)
275 Info1("permitting connection from %s",
276 sockaddr_info((struct sockaddr *)pa, pas,
277 infobuff, sizeof(infobuff)));
279 if (dofork) {
280 pid_t pid; /* mostly int; only used with fork */
281 sigset_t mask_sigchld;
283 /* we must prevent that the current packet triggers another fork;
284 therefore we wait for a signal from the recent child: USR1
285 indicates that is has consumed the last packet; CHLD means it has
286 terminated */
287 /* block SIGCHLD and SIGUSR1 until parent is ready to react */
288 sigemptyset(&mask_sigchld);
289 sigaddset(&mask_sigchld, SIGCHLD);
290 Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
292 if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) {
293 Close(xfd->fd);
294 Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
295 return STAT_RETRYLATER;
297 if (pid == 0) { /* child */
298 pid_t cpid = Getpid();
299 Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
301 Info1("just born: child process "F_pid, cpid);
302 xiosetenvulong("PID", cpid, 1);
304 if (Close(xfd->fd) < 0) {
305 Info2("close(%d): %s", xfd->fd, strerror(errno));
307 xfd->fd = ps;
309 #if WITH_RETRY
310 /* !? */
311 xfd->forever = false; xfd->retry = 0;
312 level = E_ERROR;
313 #endif /* WITH_RETRY */
315 break;
318 /* server: continue loop with listen */
319 /* shutdown() closes the socket even for the child process, but
320 close() does what we want */
321 if (Close(ps) < 0) {
322 Info2("close(%d): %s", ps, strerror(errno));
325 /* now we are ready to handle signals */
326 Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
328 while (maxchildren) {
329 if (num_child < maxchildren) break;
330 Notice("maxchildren are active, waiting");
331 /* UINT_MAX would even be nicer, but Openindiana works only
332 with 31 bits */
333 while (!Sleep(INT_MAX)) ; /* any signal lets us continue */
335 Info("still listening");
336 } else {
337 if (Close(xfd->fd) < 0) {
338 Info2("close(%d): %s", xfd->fd, strerror(errno));
340 xfd->fd = ps;
341 break;
345 applyopts(xfd->fd, opts, PH_FD);
346 applyopts(xfd->fd, opts, PH_PASTSOCKET);
347 applyopts(xfd->fd, opts, PH_CONNECTED);
348 if ((result = _xio_openlate(xfd, opts)) < 0)
349 return result;
351 /* set the env vars describing the local and remote sockets */
352 if (la != NULL) xiosetsockaddrenv("SOCK", la, las, proto);
353 if (pa != NULL) xiosetsockaddrenv("PEER", pa, pas, proto);
355 return 0;
358 #endif /* WITH_LISTEN */