Version 1.8.0.0
[socat.git] / xio-ipapp.c
blobfaa522b572cf221682fe0d376ade18e1a082868c
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
11 #include "xioopen.h"
12 #include "xio-socket.h"
13 #include "xio-ip.h"
14 #include "xio-listen.h"
15 #include "xio-ip6.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 };
22 #if WITH_IP4
23 /* we expect the form "host:port" */
24 int xioopen_ipapp_connect(
25 int argc,
26 const char *argv[],
27 struct opt *opts,
28 int xioflags,
29 xiofile_t *xxfd,
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];
38 bool dofork = false;
39 int maxchildren = 0;
40 union sockaddr_union us_sa, *us = &us_sa;
41 socklen_t uslen = sizeof(us_sa);
42 struct addrinfo *themlist, *themp;
43 char infobuff[256];
44 bool needbind = false;
45 bool lowport = false;
46 int level;
47 struct addrinfo **ai_sorted;
48 int i;
49 int result;
51 if (argc != 3) {
52 xio_syntax(argv[0], 2, argc-1, addrdesc->syntax);
53 return STAT_NORETRY;
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)
61 return -1;
62 applyopts(sfd, -1, opts, PH_INIT);
64 retropt_bool(opts, OPT_FORK, &dofork);
65 if (dofork) {
66 if (!(xioflags & XIO_MAYFORK)) {
67 Error("option fork not allowed here");
68 return STAT_NORETRY;
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");
77 return STAT_NORETRY;
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) {
84 return STAT_NORETRY;
87 if (dofork) {
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';
94 } else {
95 Info("starting connect loop");
98 /* Count addrinfo entries */
99 themp = themlist;
100 i = 0;
101 while (themp != NULL) {
102 ++i;
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;
115 i = 0;
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)));
122 #if WITH_RETRY
123 if (sfd->forever || sfd->retry || ai_sorted[i] != NULL) {
124 level = E_INFO;
125 } else
126 #endif /* WITH_RETRY */
127 level = E_ERROR;
129 result =
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,
134 lowport, level);
135 if (result == STAT_OK)
136 break;
137 themp = ai_sorted[i++];
138 if (themp == NULL) {
139 result = STAT_RETRYLATER;
142 switch (result) {
143 case STAT_OK: break;
144 #if WITH_RETRY
145 case STAT_RETRYLATER:
146 case STAT_RETRYNOW:
147 if (sfd->forever || sfd->retry) {
148 --sfd->retry;
149 if (result == STAT_RETRYLATER) {
150 Nanosleep(&sfd->intervall, NULL);
152 dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
153 continue;
155 #endif /* WITH_RETRY */
156 default:
157 free(ai_sorted);
158 free(opts0);free(opts);
159 return result;
162 #if WITH_RETRY
163 if (dofork) {
164 pid_t pid;
165 int level = E_ERROR;
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;
174 free(ai_sorted);
175 free(opts0);
176 return STAT_RETRYLATER;
179 if (pid == 0) { /* child process */
180 sfd->forever = false; sfd->retry = 0;
181 break;
184 /* parent process */
185 Close(sfd->fd);
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() */
194 } else
195 #endif /* WITH_RETRY */
197 break;
199 } while (true);
200 /* only "active" process breaks (master without fork, or child) */
201 free(ai_sorted);
202 xiofreeaddrinfo(themlist);
204 if ((result = _xio_openlate(sfd, opts)) < 0) {
205 free(opts0);free(opts);
206 return result;
208 free(opts0); free(opts);
209 return 0;
213 /* returns STAT_OK on success or some other value on failure
214 applies and consumes the following options:
215 PH_EARLY
216 OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
219 _xioopen_ipapp_prepare(
220 struct opt *opts,
221 struct opt **opts0,
222 const char *hostname,
223 const char *portname,
224 int *pf,
225 int protocol,
226 const int ai_flags[2],
227 struct addrinfo **themlist,
228 union sockaddr_union *us,
229 socklen_t *uslen,
230 bool *needbind,
231 bool *lowport,
232 int socktype) {
233 uint16_t port;
234 int result;
236 retropt_socket_pf(opts, pf);
238 if (hostname != NULL || portname != NULL) {
239 if ((result =
240 xiogetaddrinfo(hostname, portname,
241 *pf, socktype, protocol,
242 themlist, ai_flags))
243 != STAT_OK) {
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,
253 ai_flags)
254 != STAT_NOACTION) {
255 *needbind = true;
256 } else {
257 switch ((*pf!=PF_UNSPEC)?*pf:(*themlist)->ai_family) {
258 #if WITH_IP4
259 case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break;
260 #endif /* WITH_IP4 */
261 #if WITH_IP6
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) {
270 #if WITH_IP4
271 case PF_INET: us->ip4.sin_port = htons(port); break;
272 #endif /* WITH_IP4 */
273 #if WITH_IP6
274 case PF_INET6: us->ip6.sin6_port = htons(port); break;
275 #endif /* WITH_IP6 */
276 default: Error("unsupported protocol family");
278 *needbind = true;
281 retropt_bool(opts, OPT_LOWPORT, lowport);
283 *opts0 = copyopts(opts, GROUP_ALL);
285 return STAT_OK;
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(
296 struct opt *opts,
297 struct opt **opts0,
298 const char *portname,
299 int *pf,
300 int ipproto,
301 const int ai_flags[2],
302 union sockaddr_union *us,
303 socklen_t *uslen,
304 int socktype)
306 char *bindname = NULL;
307 int ai_flags2[2];
308 int result;
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;
320 result =
321 xioresolve(bindname, portname, *pf, socktype, ipproto,
322 us, uslen, ai_flags2);
323 if (result != STAT_OK) {
324 /*! STAT_RETRY? */
325 return result;
327 *opts0 = copyopts(opts, GROUP_ALL);
328 return STAT_OK;
332 /* we expect the form: port */
333 /* currently only used for TCP4 */
334 int xioopen_ipapp_listen(
335 int argc,
336 const char *argv[],
337 struct opt *opts,
338 int xioflags,
339 xiofile_t *xfd,
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);
349 int result;
351 if (argc != 2) {
352 xio_syntax(argv[0], 2, argc-1, addrdesc->syntax);
353 return STAT_NORETRY;
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 */
364 #elif WITH_IP6
365 pf = PF_INET6;
366 #else
367 pf = PF_INET;
368 #endif
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)
381 != STAT_OK) {
382 return STAT_NORETRY;
385 if ((result =
386 xioopen_listen(sfd, xioflags,
387 (struct sockaddr *)us, uslen,
388 opts, opts0, pf, socktype, ipproto))
389 != 0)
390 return result;
391 return 0;
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;
405 int i;
406 int ipv[3];
407 int ipi = 0;
409 /* Make a simple array of IP version preferences */
410 switch (xioparms.preferred_ip) {
411 case '0':
412 ipv[0] = PF_UNSPEC;
413 ipv[1] = -1;
414 break;
415 case '4':
416 ipv[0] = PF_INET;
417 ipv[1] = PF_INET6;
418 ipv[2] = -1;
419 break;
420 case '6':
421 ipv[0] = PF_INET6;
422 ipv[1] = PF_INET;
423 ipv[2] = -1;
424 break;
425 default:
426 Error("INTERNAL: undefined preferred_ip value");
427 return -1;
430 /* Create the sorted list */
431 ipi = 0;
432 i = 0;
433 while (ipv[ipi] >= 0) {
434 themp = themlist;
435 while (themp != NULL) {
436 if (ipv[ipi] == PF_UNSPEC) {
437 ai_sorted[i] = themp;
438 ++i;
439 } else if (ipv[ipi] == themp->ai_family) {
440 ai_sorted[i] = themp;
441 ++i;
443 themp = themp->ai_next;
445 ++ipi;
447 ai_sorted[i] = NULL;
448 return 0;
451 #endif /* WITH_TCP || WITH_UDP */