add listen-timeout to function as an accept timeout
[socat/sam.git] / xio-ipapp.c
blobfe81399243eefa1811f806b31f3a43e9ea871afa
1 /* source: xio-ipapp.c */
2 /* Copyright Gerhard Rieger 2001-2008 */
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); opts = copyopts(opts0, GROUP_ALL);
96 continue;
98 return STAT_NORETRY;
99 #endif /* WITH_RETRY */
100 default:
101 return result;
104 #if WITH_RETRY
105 if (dofork) {
106 pid_t pid;
107 int level = E_ERROR;
108 if (xfd->forever || xfd->retry) {
109 level = E_WARN; /* most users won't expect a problem here,
110 so Notice is too weak */
112 while ((pid = xio_fork(false, level)) < 0) {
113 if (xfd->forever || --xfd->retry) {
114 Nanosleep(&xfd->intervall, NULL); continue;
116 return STAT_RETRYLATER;
119 if (pid == 0) { /* child process */
120 xfd->forever = false; xfd->retry = 0;
121 break;
124 /* parent process */
125 Close(xfd->fd);
126 /* with and without retry */
127 Nanosleep(&xfd->intervall, NULL);
128 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
129 continue; /* with next socket() bind() connect() */
130 } else
131 #endif /* WITH_RETRY */
133 break;
135 } while (true);
136 /* only "active" process breaks (master without fork, or child) */
138 if ((result = _xio_openlate(xfd, opts)) < 0) {
139 return result;
141 return 0;
145 /* returns STAT_OK on success or some other value on failure
146 applies and consumes the following options:
147 PH_EARLY
148 OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT
151 _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0,
152 const char *hostname,
153 const char *portname,
154 int *pf,
155 int protocol,
156 unsigned long res_opts0, unsigned long res_opts1,
157 union sockaddr_union *them, socklen_t *themlen,
158 union sockaddr_union *us, socklen_t *uslen,
159 bool *needbind, bool *lowport,
160 int socktype) {
161 uint16_t port;
162 char infobuff[256];
163 int result;
165 retropt_socket_pf(opts, pf);
167 if ((result =
168 xiogetaddrinfo(hostname, portname,
169 *pf, socktype, protocol,
170 (union sockaddr_union *)them, themlen,
171 res_opts0, res_opts1
173 != STAT_OK) {
174 return STAT_NORETRY; /*! STAT_RETRYLATER? */
176 if (*pf == PF_UNSPEC) {
177 *pf = them->soa.sa_family;
180 applyopts(-1, opts, PH_EARLY);
182 /* 3 means: IP address AND port accepted */
183 if (retropt_bind(opts, *pf, socktype, protocol, (struct sockaddr *)us, uslen, 3,
184 res_opts0, res_opts1)
185 != STAT_NOACTION) {
186 *needbind = true;
187 } else {
188 switch (*pf) {
189 #if WITH_IP4
190 case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break;
191 #endif /* WITH_IP4 */
192 #if WITH_IP6
193 case PF_INET6: socket_in6_init(&us->ip6); *uslen = sizeof(us->ip6); break;
194 #endif /* WITH_IP6 */
198 if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) {
199 switch (*pf) {
200 #if WITH_IP4
201 case PF_INET: us->ip4.sin_port = htons(port); break;
202 #endif /* WITH_IP4 */
203 #if WITH_IP6
204 case PF_INET6: us->ip6.sin6_port = htons(port); break;
205 #endif /* WITH_IP6 */
206 default: Error("unsupported protocol family");
208 *needbind = true;
211 retropt_bool(opts, OPT_LOWPORT, lowport);
213 *opts0 = copyopts(opts, GROUP_ALL);
215 Notice1("opening connection to %s",
216 sockaddr_info((struct sockaddr *)them, *themlen, infobuff, sizeof(infobuff)));
217 return STAT_OK;
219 #endif /* WITH_IP4 */
222 #if WITH_TCP && WITH_LISTEN
224 applies and consumes the following options:
225 OPT_PROTOCOL_FAMILY, OPT_BIND
227 int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0,
228 const char *portname, int *pf, int ipproto,
229 unsigned long res_opts0,
230 unsigned long res_opts1,
231 union sockaddr_union *us, socklen_t *uslen,
232 int socktype) {
233 char *bindname = NULL;
234 int result;
236 retropt_socket_pf(opts, pf);
238 retropt_string(opts, OPT_BIND, &bindname);
239 if ((result =
240 xiogetaddrinfo(bindname, portname, *pf, socktype, ipproto,
241 (union sockaddr_union *)us, uslen,
242 res_opts0, res_opts1))
243 != STAT_OK) {
244 /*! STAT_RETRY? */
245 return result;
248 *opts0 = copyopts(opts, GROUP_ALL);
249 return STAT_OK;
253 /* we expect the form: port */
254 /* currently only used for TCP4 */
255 int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts,
256 int xioflags, xiofile_t *fd,
257 unsigned groups, int socktype,
258 int ipproto, int pf) {
259 struct opt *opts0 = NULL;
260 union sockaddr_union us_sa, *us = &us_sa;
261 socklen_t uslen = sizeof(us_sa);
262 int result;
264 if (argc != 2) {
265 Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
268 if (pf == PF_UNSPEC) {
269 #if WITH_IP4 && WITH_IP6
270 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
271 #elif WITH_IP6
272 pf = PF_INET6;
273 #else
274 pf = PF_INET;
275 #endif
278 fd->stream.howtoend = END_SHUTDOWN;
280 if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1;
281 applyopts(-1, opts, PH_INIT);
282 applyopts(-1, opts, PH_EARLY);
284 if (_xioopen_ipapp_listen_prepare(opts, &opts0, argv[1], &pf, ipproto,
285 fd->stream.para.socket.ip.res_opts[1],
286 fd->stream.para.socket.ip.res_opts[0],
287 us, &uslen, socktype)
288 != STAT_OK) {
289 return STAT_NORETRY;
292 if ((result =
293 xioopen_listen(&fd->stream, xioflags,
294 (struct sockaddr *)us, uslen,
295 opts, opts0, pf, socktype, ipproto))
296 != 0)
297 return result;
298 return 0;
300 #endif /* WITH_IP4 && WITH_TCP && WITH_LISTEN */
302 #endif /* WITH_TCP || WITH_UDP */