1 /* source: xio-ipapp.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for TCP and UDP related options */
7 #include "xiosysincludes.h"
9 #if WITH_TCP || WITH_UDP
12 #include "xio-socket.h"
14 #include "xio-listen.h"
16 #include "xio-ipapp.h"
18 const struct optdesc opt_sourceport
= { "sourceport", "sp", OPT_SOURCEPORT
, GROUP_IPAPP
, PH_LATE
,TYPE_2BYTE
, OFUNC_SPEC
};
19 /*const struct optdesc opt_port = { "port", NULL, OPT_PORT, GROUP_IPAPP, PH_BIND, TYPE_USHORT, OFUNC_SPEC };*/
20 const struct optdesc opt_lowport
= { "lowport", NULL
, OPT_LOWPORT
, GROUP_IPAPP
, PH_LATE
, TYPE_BOOL
, OFUNC_SPEC
};
23 /* we expect the form "host:port" */
24 int xioopen_ipapp_connect(
30 const struct addrdesc
*addrdesc
)
32 struct single
*sfd
= &xxfd
->stream
;
33 struct opt
*opts0
= NULL
;
34 int socktype
= addrdesc
->arg1
;
35 int ipproto
= addrdesc
->arg2
;
36 int pf
= addrdesc
->arg3
;
37 const char *hostname
= argv
[1], *portname
= argv
[2];
40 union sockaddr_union us_sa
, *us
= &us_sa
;
41 socklen_t uslen
= sizeof(us_sa
);
42 struct addrinfo
*themlist
, *themp
;
44 bool needbind
= false;
47 struct addrinfo
**ai_sorted
;
52 xio_syntax(argv
[0], 2, argc
-1, addrdesc
->syntax
);
56 xioinit_ip(&pf
, xioparms
.default_ip
);
57 if (sfd
->howtoend
== END_UNSPEC
)
58 sfd
->howtoend
= END_SHUTDOWN
;
60 if (applyopts_single(sfd
, opts
, PH_INIT
) < 0)
62 applyopts(sfd
, -1, opts
, PH_INIT
);
64 retropt_bool(opts
, OPT_FORK
, &dofork
);
66 if (!(xioflags
& XIO_MAYFORK
)) {
67 Error("option fork not allowed here");
70 sfd
->flags
|= XIO_DOESFORK
;
73 retropt_int(opts
, OPT_MAX_CHILDREN
, &maxchildren
);
75 if (! dofork
&& maxchildren
) {
76 Error("option max-children not allowed without option fork");
80 if (_xioopen_ipapp_prepare(opts
, &opts0
, hostname
, portname
, &pf
, ipproto
,
81 sfd
->para
.socket
.ip
.ai_flags
,
82 &themlist
, us
, &uslen
, &needbind
, &lowport
,
83 socktype
) != STAT_OK
) {
88 xiosetchilddied(); /* set SIGCHLD handler */
91 if (xioparms
.logopt
== 'm') {
92 Info("starting connect loop, switching to syslog");
93 diag_set('y', xioparms
.syslogfac
); xioparms
.logopt
= 'y';
95 Info("starting connect loop");
98 /* Count addrinfo entries */
101 while (themp
!= NULL
) {
103 themp
= themp
->ai_next
;
105 ai_sorted
= Calloc((i
+1), sizeof(struct addrinfo
*));
106 if (ai_sorted
== NULL
)
107 return STAT_RETRYLATER
;
108 /* Generate a list of addresses sorted by preferred ip version */
109 _xio_sort_ip_addresses(themlist
, ai_sorted
);
111 do { /* loop over retries, and forks */
113 /* Loop over themlist - no, over ai_sorted */
114 result
= STAT_RETRYLATER
;
116 themp
= ai_sorted
[i
++];
117 while (themp
!= NULL
) {
118 Notice1("opening connection to %s",
119 sockaddr_info(themp
->ai_addr
, themp
->ai_addrlen
,
120 infobuff
, sizeof(infobuff
)));
123 if (sfd
->forever
|| sfd
->retry
|| ai_sorted
[i
] != NULL
) {
126 #endif /* WITH_RETRY */
130 _xioopen_connect(sfd
,
131 needbind
?us
:NULL
, uslen
,
132 themp
->ai_addr
, themp
->ai_addrlen
,
133 opts
, pf
?pf
:themp
->ai_family
, socktype
, ipproto
,
135 if (result
== STAT_OK
)
137 themp
= ai_sorted
[i
++];
139 result
= STAT_RETRYLATER
;
145 case STAT_RETRYLATER
:
147 if (sfd
->forever
|| sfd
->retry
) {
149 if (result
== STAT_RETRYLATER
) {
150 Nanosleep(&sfd
->intervall
, NULL
);
152 dropopts(opts
, PH_ALL
); free(opts
); opts
= copyopts(opts0
, GROUP_ALL
);
155 #endif /* WITH_RETRY */
158 free(opts0
);free(opts
);
166 if (sfd
->forever
|| sfd
->retry
) {
167 level
= E_WARN
; /* most users won't expect a problem here,
168 so Notice is too weak */
170 while ((pid
= xio_fork(false, level
, sfd
->shutup
)) < 0) {
171 if (sfd
->forever
|| --sfd
->retry
) {
172 Nanosleep(&sfd
->intervall
, NULL
); continue;
176 return STAT_RETRYLATER
;
179 if (pid
== 0) { /* child process */
180 sfd
->forever
= false; sfd
->retry
= 0;
186 /* with and without retry */
187 Nanosleep(&sfd
->intervall
, NULL
);
188 while (maxchildren
> 0 && num_child
>= maxchildren
) {
189 Info1("all %d allowed children are active, waiting", maxchildren
);
190 Nanosleep(&sfd
->intervall
, NULL
);
192 dropopts(opts
, PH_ALL
); free(opts
); opts
= copyopts(opts0
, GROUP_ALL
);
193 continue; /* with next socket() bind() connect() */
195 #endif /* WITH_RETRY */
200 /* only "active" process breaks (master without fork, or child) */
202 xiofreeaddrinfo(themlist
);
204 if ((result
= _xio_openlate(sfd
, opts
)) < 0) {
205 free(opts0
);free(opts
);
208 free(opts0
); free(opts
);
213 /* returns STAT_OK on success or some other value on failure
214 applies and consumes the following options:
216 OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
219 _xioopen_ipapp_prepare(
222 const char *hostname
,
223 const char *portname
,
226 const int ai_flags
[2],
227 struct addrinfo
**themlist
,
228 union sockaddr_union
*us
,
236 retropt_socket_pf(opts
, pf
);
238 if (hostname
!= NULL
|| portname
!= NULL
) {
240 xiogetaddrinfo(hostname
, portname
,
241 *pf
, socktype
, protocol
,
244 return STAT_NORETRY
; /*! STAT_RETRYLATER? */
248 applyopts(NULL
, -1, opts
, PH_EARLY
);
250 /* 3 means: IP address AND port accepted */
251 if (retropt_bind(opts
, (*pf
!=PF_UNSPEC
)?*pf
:(*themlist
)->ai_family
,
252 socktype
, protocol
, (struct sockaddr
*)us
, uslen
, 3,
257 switch ((*pf
!=PF_UNSPEC
)?*pf
:(*themlist
)->ai_family
) {
259 case PF_INET
: socket_in_init(&us
->ip4
); *uslen
= sizeof(us
->ip4
); break;
260 #endif /* WITH_IP4 */
262 case PF_INET6
: socket_in6_init(&us
->ip6
); *uslen
= sizeof(us
->ip6
); break;
263 #endif /* WITH_IP6 */
264 default: Error("unsupported protocol family");
268 if (retropt_2bytes(opts
, OPT_SOURCEPORT
, &port
) >= 0) {
269 switch ((*pf
!=PF_UNSPEC
)?*pf
:(*themlist
)->ai_family
) {
271 case PF_INET
: us
->ip4
.sin_port
= htons(port
); break;
272 #endif /* WITH_IP4 */
274 case PF_INET6
: us
->ip6
.sin6_port
= htons(port
); break;
275 #endif /* WITH_IP6 */
276 default: Error("unsupported protocol family");
281 retropt_bool(opts
, OPT_LOWPORT
, lowport
);
283 *opts0
= copyopts(opts
, GROUP_ALL
);
287 #endif /* WITH_IP4 */
290 #if WITH_TCP && WITH_LISTEN
292 applies and consumes the following options:
293 OPT_PROTOCOL_FAMILY, OPT_BIND
295 int _xioopen_ipapp_listen_prepare(
298 const char *portname
,
301 const int ai_flags
[2],
302 union sockaddr_union
*us
,
306 char *bindname
= NULL
;
310 retropt_socket_pf(opts
, pf
);
312 retropt_string(opts
, OPT_BIND
, &bindname
);
314 /* Set AI_PASSIVE, except when it is explicitely disabled */
315 ai_flags2
[0] = ai_flags
[0];
316 ai_flags2
[1] = ai_flags
[1];
317 if (!(ai_flags2
[1] & AI_PASSIVE
))
318 ai_flags2
[0] |= AI_PASSIVE
;
321 xioresolve(bindname
, portname
, *pf
, socktype
, ipproto
,
322 us
, uslen
, ai_flags2
);
323 if (result
!= STAT_OK
) {
327 *opts0
= copyopts(opts
, GROUP_ALL
);
332 /* we expect the form: port */
333 /* currently only used for TCP4 */
334 int xioopen_ipapp_listen(
340 const struct addrdesc
*addrdesc
)
342 struct single
*sfd
= &xfd
->stream
;
343 struct opt
*opts0
= NULL
;
344 int socktype
= addrdesc
->arg1
;
345 int ipproto
= addrdesc
->arg2
;
346 int pf
= addrdesc
->arg3
;
347 union sockaddr_union us_sa
, *us
= &us_sa
;
348 socklen_t uslen
= sizeof(us_sa
);
352 xio_syntax(argv
[0], 2, argc
-1, addrdesc
->syntax
);
356 xioinit_ip(&pf
, xioparms
.default_ip
);
357 if (pf
== PF_UNSPEC
) {
358 #if WITH_IP4 && WITH_IP6
359 switch (xioparms
.default_ip
) {
360 case '4': pf
= PF_INET
; break;
361 case '6': pf
= PF_INET6
; break;
362 default: break; /* includes \0 */
371 if (sfd
->howtoend
== END_UNSPEC
)
372 sfd
->howtoend
= END_SHUTDOWN
;
374 if (applyopts_single(sfd
, opts
, PH_INIT
) < 0) return -1;
375 applyopts(sfd
, -1, opts
, PH_INIT
);
376 applyopts(sfd
, -1, opts
, PH_EARLY
);
378 if (_xioopen_ipapp_listen_prepare(opts
, &opts0
, argv
[1], &pf
, ipproto
,
379 sfd
->para
.socket
.ip
.ai_flags
,
380 us
, &uslen
, socktype
)
386 xioopen_listen(sfd
, xioflags
,
387 (struct sockaddr
*)us
, uslen
,
388 opts
, opts0
, pf
, socktype
, ipproto
))
393 #endif /* WITH_IP4 && WITH_TCP && WITH_LISTEN */
396 /* Sort the records of an addrinfo list themp (as returned by getaddrinfo),
397 return the sorted list in the array ai_sorted (takes at most n entries
398 including the terminating NULL)
399 Returns 0 on success. */
400 int _xio_sort_ip_addresses(
401 struct addrinfo
*themlist
,
402 struct addrinfo
**ai_sorted
)
404 struct addrinfo
*themp
;
409 /* Make a simple array of IP version preferences */
410 switch (xioparms
.preferred_ip
) {
426 Error("INTERNAL: undefined preferred_ip value");
430 /* Create the sorted list */
433 while (ipv
[ipi
] >= 0) {
435 while (themp
!= NULL
) {
436 if (ipv
[ipi
] == PF_UNSPEC
) {
437 ai_sorted
[i
] = themp
;
439 } else if (ipv
[ipi
] == themp
->ai_family
) {
440 ai_sorted
[i
] = themp
;
443 themp
= themp
->ai_next
;
451 #endif /* WITH_TCP || WITH_UDP */