version 1.7.3.0
[socat.git] / xio-ipapp.c
blobeeb18f1a629c2a2a7dd4508cb2459137c8eae787
1 /* source: xio-ipapp.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 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(int argc, const char *argv[], struct opt *opts,
25 int xioflags, xiofile_t *xxfd,
26 unsigned groups, int socktype, int ipproto,
27 int pf) {
28 struct single *xfd = &xxfd->stream;
29 struct opt *opts0 = NULL;
30 const char *hostname = argv[1], *portname = argv[2];
31 bool dofork = false;
32 union sockaddr_union us_sa, *us = &us_sa;
33 union sockaddr_union them_sa, *them = &them_sa;
34 socklen_t uslen = sizeof(us_sa);
35 socklen_t themlen = sizeof(them_sa);
36 bool needbind = false;
37 bool lowport = false;
38 int level;
39 int result;
41 if (argc != 3) {
42 Error2("%s: wrong number of parameters (%d instead of 2)", argv[0], argc-1);
45 xfd->howtoend = END_SHUTDOWN;
47 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
48 applyopts(-1, opts, PH_INIT);
50 retropt_bool(opts, OPT_FORK, &dofork);
52 if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
53 xfd->para.socket.ip.res_opts[1],
54 xfd->para.socket.ip.res_opts[0],
55 them, &themlen, us, &uslen, &needbind, &lowport,
56 socktype) != STAT_OK) {
57 return STAT_NORETRY;
60 if (dofork) {
61 xiosetchilddied(); /* set SIGCHLD handler */
64 if (xioopts.logopt == 'm') {
65 Info("starting connect loop, switching to syslog");
66 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
67 } else {
68 Info("starting connect loop");
71 do { /* loop over retries and forks */
73 #if WITH_RETRY
74 if (xfd->forever || xfd->retry) {
75 level = E_INFO;
76 } else
77 #endif /* WITH_RETRY */
78 level = E_ERROR;
80 result =
81 _xioopen_connect(xfd,
82 needbind?(struct sockaddr *)us:NULL, uslen,
83 (struct sockaddr *)them, themlen,
84 opts, pf, socktype, ipproto, lowport, level);
85 switch (result) {
86 case STAT_OK: break;
87 #if WITH_RETRY
88 case STAT_RETRYLATER:
89 case STAT_RETRYNOW:
90 if (xfd->forever || xfd->retry) {
91 --xfd->retry;
92 if (result == STAT_RETRYLATER) {
93 Nanosleep(&xfd->intervall, NULL);
95 dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
96 continue;
98 return STAT_NORETRY;
99 #endif /* WITH_RETRY */
100 default:
101 free(opts0);free(opts);
102 return result;
105 #if WITH_RETRY
106 if (dofork) {
107 pid_t pid;
108 int level = E_ERROR;
109 if (xfd->forever || xfd->retry) {
110 level = E_WARN; /* most users won't expect a problem here,
111 so Notice is too weak */
113 while ((pid = xio_fork(false, level)) < 0) {
114 if (xfd->forever || --xfd->retry) {
115 Nanosleep(&xfd->intervall, NULL); continue;
117 free(opts0);
118 return STAT_RETRYLATER;
121 if (pid == 0) { /* child process */
122 xfd->forever = false; xfd->retry = 0;
123 break;
126 /* parent process */
127 Close(xfd->fd);
128 /* with and without retry */
129 Nanosleep(&xfd->intervall, NULL);
130 dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL);
131 continue; /* with next socket() bind() connect() */
132 } else
133 #endif /* WITH_RETRY */
135 break;
137 } while (true);
138 /* only "active" process breaks (master without fork, or child) */
140 if ((result = _xio_openlate(xfd, opts)) < 0) {
141 free(opts0);free(opts);
142 return result;
144 free(opts0);free(opts);
145 return 0;
149 /* returns STAT_OK on success or some other value on failure
150 applies and consumes the following options:
151 PH_EARLY
152 OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
155 _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0,
156 const char *hostname,
157 const char *portname,
158 int *pf,
159 int protocol,
160 unsigned long res_opts0, unsigned long res_opts1,
161 union sockaddr_union *them, socklen_t *themlen,
162 union sockaddr_union *us, socklen_t *uslen,
163 bool *needbind, bool *lowport,
164 int socktype) {
165 uint16_t port;
166 char infobuff[256];
167 int result;
169 retropt_socket_pf(opts, pf);
171 if ((result =
172 xiogetaddrinfo(hostname, portname,
173 *pf, socktype, protocol,
174 (union sockaddr_union *)them, themlen,
175 res_opts0, res_opts1
177 != STAT_OK) {
178 return STAT_NORETRY; /*! STAT_RETRYLATER? */
180 if (*pf == PF_UNSPEC) {
181 *pf = them->soa.sa_family;
184 applyopts(-1, opts, PH_EARLY);
186 /* 3 means: IP address AND port accepted */
187 if (retropt_bind(opts, *pf, socktype, protocol, (struct sockaddr *)us, uslen, 3,
188 res_opts0, res_opts1)
189 != STAT_NOACTION) {
190 *needbind = true;
191 } else {
192 switch (*pf) {
193 #if WITH_IP4
194 case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break;
195 #endif /* WITH_IP4 */
196 #if WITH_IP6
197 case PF_INET6: socket_in6_init(&us->ip6); *uslen = sizeof(us->ip6); break;
198 #endif /* WITH_IP6 */
202 if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) {
203 switch (*pf) {
204 #if WITH_IP4
205 case PF_INET: us->ip4.sin_port = htons(port); break;
206 #endif /* WITH_IP4 */
207 #if WITH_IP6
208 case PF_INET6: us->ip6.sin6_port = htons(port); break;
209 #endif /* WITH_IP6 */
210 default: Error("unsupported protocol family");
212 *needbind = true;
215 retropt_bool(opts, OPT_LOWPORT, lowport);
217 *opts0 = copyopts(opts, GROUP_ALL);
219 Notice1("opening connection to %s",
220 sockaddr_info((struct sockaddr *)them, *themlen, infobuff, sizeof(infobuff)));
221 return STAT_OK;
223 #endif /* WITH_IP4 */
226 #if WITH_TCP && WITH_LISTEN
228 applies and consumes the following options:
229 OPT_PROTOCOL_FAMILY, OPT_BIND
231 int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0,
232 const char *portname, int *pf, int ipproto,
233 unsigned long res_opts0,
234 unsigned long res_opts1,
235 union sockaddr_union *us, socklen_t *uslen,
236 int socktype) {
237 char *bindname = NULL;
238 int result;
240 retropt_socket_pf(opts, pf);
242 retropt_string(opts, OPT_BIND, &bindname);
243 if ((result =
244 xiogetaddrinfo(bindname, portname, *pf, socktype, ipproto,
245 (union sockaddr_union *)us, uslen,
246 res_opts0, res_opts1))
247 != STAT_OK) {
248 /*! STAT_RETRY? */
249 return result;
252 *opts0 = copyopts(opts, GROUP_ALL);
253 return STAT_OK;
257 /* we expect the form: port */
258 /* currently only used for TCP4 */
259 int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts,
260 int xioflags, xiofile_t *fd,
261 unsigned groups, int socktype,
262 int ipproto, int pf) {
263 struct opt *opts0 = NULL;
264 union sockaddr_union us_sa, *us = &us_sa;
265 socklen_t uslen = sizeof(us_sa);
266 int result;
268 if (argc != 2) {
269 Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
272 if (pf == PF_UNSPEC) {
273 #if WITH_IP4 && WITH_IP6
274 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
275 #elif WITH_IP6
276 pf = PF_INET6;
277 #else
278 pf = PF_INET;
279 #endif
282 fd->stream.howtoend = END_SHUTDOWN;
284 if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1;
285 applyopts(-1, opts, PH_INIT);
286 applyopts(-1, opts, PH_EARLY);
288 if (_xioopen_ipapp_listen_prepare(opts, &opts0, argv[1], &pf, ipproto,
289 fd->stream.para.socket.ip.res_opts[1],
290 fd->stream.para.socket.ip.res_opts[0],
291 us, &uslen, socktype)
292 != STAT_OK) {
293 return STAT_NORETRY;
296 if ((result =
297 xioopen_listen(&fd->stream, xioflags,
298 (struct sockaddr *)us, uslen,
299 opts, opts0, pf, socktype, ipproto))
300 != 0)
301 return result;
302 return 0;
304 #endif /* WITH_IP4 && WITH_TCP && WITH_LISTEN */
306 #endif /* WITH_TCP || WITH_UDP */