version 1.7.3.0
[socat.git] / xio-socket.c
blobe75bfc189cb4ed274a89088f95f0d82c09070189
1 /* source: xio-socket.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 socket related functions, and the
6 implementation of generic socket addresses */
8 #include "xiosysincludes.h"
10 #if _WITH_SOCKET
12 #include "xioopen.h"
13 #include "xio-ascii.h"
14 #include "xio-socket.h"
15 #include "xio-named.h"
16 #include "xio-unix.h"
17 #if WITH_IP4
18 #include "xio-ip4.h"
19 #endif /* WITH_IP4 */
20 #if WITH_IP6
21 #include "xio-ip6.h"
22 #endif /* WITH_IP6 */
23 #include "xio-ip.h"
24 #include "xio-listen.h"
25 #include "xio-ipapp.h" /*! not clean */
26 #include "xio-tcpwrap.h"
29 static
30 int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts,
31 int xioflags, xiofile_t *xfd, unsigned groups,
32 int dummy1, int dummy2, int dummy3);
33 static
34 int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts,
35 int xioflags, xiofile_t *xfd, unsigned groups,
36 int dummy1, int dummy2, int dummy3);
37 static
38 int xioopen_socket_sendto(int argc, const char *argv[], struct opt *opts,
39 int xioflags, xiofile_t *xfd, unsigned groups,
40 int dummy1, int dummy2, int dummy3);
41 static
42 int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts,
43 int xioflags, xiofile_t *xfd, unsigned groups,
44 int dummy1, int dummy2, int dummy3);
45 static
46 int xioopen_socket_recvfrom(int argc, const char *argv[], struct opt *opts,
47 int xioflags, xiofile_t *xfd, unsigned groups,
48 int dummy1, int socktype, int dummy3);
49 static
50 int xioopen_socket_recv(int argc, const char *argv[], struct opt *opts,
51 int xioflags, xiofile_t *xfd, unsigned groups,
52 int dumy1, int dummy2, int dummy3);
54 static
55 int _xioopen_socket_sendto(const char *pfname, const char *type,
56 const char *proto, const char *address,
57 struct opt *opts, int xioflags, xiofile_t *xxfd,
58 unsigned groups);
60 static int
61 xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num,
62 char *typbuff, int typlen,
63 char *nambuff, int namlen,
64 char *envbuff, int envlen,
65 char *valbuff, int vallen);
68 #if WITH_GENERICSOCKET
69 /* generic socket addresses */
70 const struct addrdesc xioaddr_socket_connect = { "socket-connect", 1, xioopen_socket_connect, GROUP_FD|GROUP_SOCKET|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<domain>:<protocol>:<remote-address>") };
71 #if WITH_LISTEN
72 const struct addrdesc xioaddr_socket_listen = { "socket-listen", 1, xioopen_socket_listen, GROUP_FD|GROUP_SOCKET|GROUP_LISTEN|GROUP_RANGE|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<domain>:<protocol>:<local-address>") };
73 #endif /* WITH_LISTEN */
74 const struct addrdesc xioaddr_socket_sendto = { "socket-sendto", 3, xioopen_socket_sendto, GROUP_FD|GROUP_SOCKET, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<remote-address>") };
75 const struct addrdesc xioaddr_socket_datagram= { "socket-datagram", 3, xioopen_socket_datagram, GROUP_FD|GROUP_SOCKET|GROUP_RANGE, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<remote-address>") };
76 const struct addrdesc xioaddr_socket_recvfrom= { "socket-recvfrom", 3, xioopen_socket_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_RANGE|GROUP_CHILD, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<local-address>") };
77 const struct addrdesc xioaddr_socket_recv = { "socket-recv", 1, xioopen_socket_recv, GROUP_FD|GROUP_SOCKET|GROUP_RANGE, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<local-address>") };
78 #endif /* WITH_GENERICSOCKET */
81 /* the following options apply not only to generic socket addresses but to all
82 addresses that have anything to do with sockets */
83 const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DEBUG };
84 #ifdef SO_ACCEPTCONN /* AIX433 */
85 const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN};
86 #endif /* SO_ACCEPTCONN */
87 const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST};
88 const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR};
89 const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE};
90 #if HAVE_STRUCT_LINGER
91 const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER };
92 #else /* !HAVE_STRUCT_LINGER */
93 const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_LINGER };
94 #endif /* !HAVE_STRUCT_LINGER */
95 const struct optdesc opt_so_oobinline= { "so-oobinline", "oobinline", OPT_SO_OOBINLINE,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_OOBINLINE};
96 const struct optdesc opt_so_sndbuf = { "so-sndbuf", "sndbuf", OPT_SO_SNDBUF, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDBUF};
97 const struct optdesc opt_so_sndbuf_late={ "so-sndbuf-late","sndbuf-late",OPT_SO_SNDBUF_LATE,GROUP_SOCKET,PH_LATE,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SNDBUF };
98 const struct optdesc opt_so_rcvbuf = { "so-rcvbuf", "rcvbuf", OPT_SO_RCVBUF, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVBUF};
99 const struct optdesc opt_so_rcvbuf_late={"so-rcvbuf-late","rcvbuf-late",OPT_SO_RCVBUF_LATE,GROUP_SOCKET,PH_LATE,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_RCVBUF };
100 const struct optdesc opt_so_error = { "so-error", "error", OPT_SO_ERROR, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ERROR};
101 const struct optdesc opt_so_type = { "so-type", "type", OPT_SO_TYPE, GROUP_SOCKET, PH_SOCKET, TYPE_INT, OFUNC_SPEC, SOL_SOCKET, SO_TYPE };
102 const struct optdesc opt_so_dontroute= { "so-dontroute", "dontroute", OPT_SO_DONTROUTE,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DONTROUTE };
103 #ifdef SO_RCVLOWAT
104 const struct optdesc opt_so_rcvlowat = { "so-rcvlowat", "rcvlowat", OPT_SO_RCVLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVLOWAT };
105 #endif
106 #ifdef SO_RCVTIMEO
107 const struct optdesc opt_so_rcvtimeo = { "so-rcvtimeo", "rcvtimeo", OPT_SO_RCVTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL,OFUNC_SOCKOPT,SOL_SOCKET,SO_RCVTIMEO };
108 #endif
109 #ifdef SO_SNDLOWAT
110 const struct optdesc opt_so_sndlowat = { "so-sndlowat", "sndlowat", OPT_SO_SNDLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDLOWAT };
111 #endif
112 #ifdef SO_SNDTIMEO
113 const struct optdesc opt_so_sndtimeo = { "so-sndtimeo", "sndtimeo", OPT_SO_SNDTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL,OFUNC_SOCKOPT,SOL_SOCKET,SO_SNDTIMEO };
114 #endif
115 /* end of setsockopt options of UNIX98 standard */
117 #ifdef SO_AUDIT /* AIX 4.3.3 */
118 const struct optdesc opt_so_audit = { "so-audit", "audit", OPT_SO_AUDIT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_AUDIT };
119 #endif /* SO_AUDIT */
120 #ifdef SO_ATTACH_FILTER
121 const struct optdesc opt_so_attach_filter={"so-attach-filter","attachfilter",OPT_SO_ATTACH_FILTER,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_ATTACH_FILTER};
122 #endif
123 #ifdef SO_DETACH_FILTER
124 const struct optdesc opt_so_detach_filter={"so-detach-filter","detachfilter",OPT_SO_DETACH_FILTER,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DETACH_FILTER};
125 #endif
126 #ifdef SO_BINDTODEVICE /* Linux: man 7 socket */
127 const struct optdesc opt_so_bindtodevice={"so-bindtodevice","if",OPT_SO_BINDTODEVICE,GROUP_SOCKET,PH_PASTSOCKET,TYPE_NAME,OFUNC_SOCKOPT,SOL_SOCKET,SO_BINDTODEVICE};
128 #endif
129 #ifdef SO_BSDCOMPAT
130 const struct optdesc opt_so_bsdcompat= { "so-bsdcompat","bsdcompat",OPT_SO_BSDCOMPAT,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BSDCOMPAT };
131 #endif
132 #ifdef SO_CKSUMRECV
133 const struct optdesc opt_so_cksumrecv= { "so-cksumrecv","cksumrecv",OPT_SO_CKSUMRECV,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_CKSUMRECV };
134 #endif /* SO_CKSUMRECV */
135 #ifdef SO_TIMESTAMP
136 const struct optdesc opt_so_timestamp= { "so-timestamp","timestamp",OPT_SO_TIMESTAMP,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_TIMESTAMP };
137 #endif
138 #ifdef SO_KERNACCEPT /* AIX 4.3.3 */
139 const struct optdesc opt_so_kernaccept={ "so-kernaccept","kernaccept",OPT_SO_KERNACCEPT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KERNACCEPT};
140 #endif /* SO_KERNACCEPT */
141 #ifdef SO_NO_CHECK
142 const struct optdesc opt_so_no_check = { "so-no-check", "nocheck",OPT_SO_NO_CHECK, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_NO_CHECK };
143 #endif
144 #ifdef SO_NOREUSEADDR /* AIX 4.3.3 */
145 const struct optdesc opt_so_noreuseaddr={"so-noreuseaddr","noreuseaddr",OPT_SO_NOREUSEADDR,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET, SO_NOREUSEADDR};
146 #endif /* SO_NOREUSEADDR */
147 #ifdef SO_PASSCRED
148 const struct optdesc opt_so_passcred = { "so-passcred", "passcred", OPT_SO_PASSCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PASSCRED};
149 #endif
150 #ifdef SO_PEERCRED
151 const struct optdesc opt_so_peercred = { "so-peercred", "peercred", OPT_SO_PEERCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT3,OFUNC_SOCKOPT, SOL_SOCKET, SO_PEERCRED};
152 #endif
153 #ifdef SO_PRIORITY
154 const struct optdesc opt_so_priority = { "so-priority", "priority", OPT_SO_PRIORITY, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PRIORITY};
155 #endif
156 #ifdef SO_REUSEPORT /* AIX 4.3.3, BSD, HP-UX */
157 const struct optdesc opt_so_reuseport= { "so-reuseport","reuseport",OPT_SO_REUSEPORT,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEPORT };
158 #endif /* defined(SO_REUSEPORT) */
159 #ifdef SO_SECURITY_AUTHENTICATION
160 const struct optdesc opt_so_security_authentication={"so-security-authentication","securityauthentication",OPT_SO_SECURITY_AUTHENTICATION,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SECURITY_AUTHENTICATION};
161 #endif
162 #ifdef SO_SECURITY_ENCRYPTION_NETWORK
163 const struct optdesc opt_so_security_encryption_network={"so-security-encryption-network","securityencryptionnetwork",OPT_SO_SECURITY_ENCRYPTION_NETWORK,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SECURITY_ENCRYPTION_NETWORK};
164 #endif
165 #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT
166 const struct optdesc opt_so_security_encryption_transport={"so-security-encryption-transport","securityencryptiontransport",OPT_SO_SECURITY_ENCRYPTION_TRANSPORT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SECURITY_ENCRYPTION_TRANSPORT};
167 #endif
168 #ifdef SO_USE_IFBUFS
169 const struct optdesc opt_so_use_ifbufs={ "so-use-ifbufs","useifbufs",OPT_SO_USE_IFBUFS,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_USE_IFBUFS};
170 #endif /* SO_USE_IFBUFS */
171 #ifdef SO_USELOOPBACK /* AIX433, Solaris, HP-UX */
172 const struct optdesc opt_so_useloopback={"so-useloopback","useloopback",OPT_SO_USELOOPBACK,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT, SOL_SOCKET, SO_USELOOPBACK};
173 #endif /* SO_USELOOPBACK */
174 #ifdef SO_DGRAM_ERRIND /* Solaris */
175 const struct optdesc opt_so_dgram_errind={"so-dgram-errind","dgramerrind",OPT_SO_DGRAM_ERRIND,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DGRAM_ERRIND};
176 #endif /* SO_DGRAM_ERRIND */
177 #ifdef SO_DONTLINGER /* Solaris */
178 const struct optdesc opt_so_dontlinger = {"so-dontlinger", "dontlinger", OPT_SO_DONTLINGER, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DONTLINGER };
179 #endif
180 /* the SO_PROTOTYPE is OS defined on Solaris, HP-UX; we lend this for a more
181 general purpose */
182 const struct optdesc opt_so_prototype = {"so-prototype", "prototype", OPT_SO_PROTOTYPE, GROUP_SOCKET,PH_SOCKET, TYPE_INT,OFUNC_SPEC, SOL_SOCKET,SO_PROTOTYPE };
183 #ifdef FIOSETOWN
184 const struct optdesc opt_fiosetown = { "fiosetown", NULL, OPT_FIOSETOWN, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, FIOSETOWN };
185 #endif
186 #ifdef SIOCSPGRP
187 const struct optdesc opt_siocspgrp = { "siocspgrp", NULL, OPT_SIOCSPGRP, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, SIOCSPGRP };
188 #endif
189 const struct optdesc opt_bind = { "bind", NULL, OPT_BIND, GROUP_SOCKET, PH_BIND, TYPE_STRING,OFUNC_SPEC };
190 const struct optdesc opt_connect_timeout = { "connect-timeout", NULL, OPT_CONNECT_TIMEOUT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.connect_timeout) };
191 const struct optdesc opt_protocol_family = { "protocol-family", "pf", OPT_PROTOCOL_FAMILY, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC };
192 const struct optdesc opt_protocol = { "protocol", NULL, OPT_PROTOCOL, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC };
194 /* generic setsockopt() options */
195 const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int", OPT_SETSOCKOPT_INT, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_INT, OFUNC_SOCKOPT_GENERIC, 0, 0 };
196 const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
197 const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_STRING, OFUNC_SOCKOPT_GENERIC, 0, 0 };
199 const struct optdesc opt_null_eof = { "null-eof", NULL, OPT_NULL_EOF, GROUP_SOCKET, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.null_eof) };
202 #if WITH_GENERICSOCKET
204 static
205 int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts,
206 int xioflags, xiofile_t *xxfd, unsigned groups,
207 int dummy1, int dummy2, int dummy3) {
208 struct single *xfd = &xxfd->stream;
209 const char *pfname = argv[1];
210 const char *protname = argv[2];
211 const char *address = argv[3];
212 char *garbage;
213 int pf;
214 int proto;
215 int socktype = SOCK_STREAM;
216 int needbind = 0;
217 union sockaddr_union them; socklen_t themlen; size_t themsize;
218 union sockaddr_union us; socklen_t uslen = sizeof(us);
219 int result;
221 if (argc != 4) {
222 Error2("%s: wrong number of parameters (%d instead of 3)",
223 argv[0], argc-1);
224 return STAT_NORETRY;
227 pf = strtoul(pfname, &garbage, 0);
228 if (*garbage != '\0') {
229 Warn1("garbage in parameter: \"%s\"", garbage);
232 proto = strtoul(protname, &garbage, 0);
233 if (*garbage != '\0') {
234 Warn1("garbage in parameter: \"%s\"", garbage);
237 retropt_socket_pf(opts, &pf);
238 retropt_int(opts, OPT_SO_TYPE, &socktype);
239 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
240 xfd->howtoend = END_SHUTDOWN;
242 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
243 applyopts(-1, opts, PH_INIT);
244 applyopts(-1, opts, PH_EARLY);
246 themsize = 0;
247 if ((result =
248 dalan(address, (char *)&them.soa.sa_data, &themsize, sizeof(them)))
249 < 0) {
250 Error1("data too long: \"%s\"", address);
251 } else if (result > 0) {
252 Error1("syntax error in \"%s\"", address);
254 them.soa.sa_family = pf;
255 themlen = themsize +
256 #if HAVE_STRUCT_SOCKADDR_SALEN
257 sizeof(them.soa.sa_len) +
258 #endif
259 sizeof(them.soa.sa_family);
261 xfd->dtype = XIOREAD_STREAM|XIOWRITE_STREAM;
263 socket_init(0, &us);
264 if (retropt_bind(opts, 0 /*pf*/, socktype, proto, (struct sockaddr *)&us, &uslen, 3,
265 0, 0)
266 != STAT_NOACTION) {
267 needbind = true;
268 us.soa.sa_family = pf;
271 if ((result =
272 xioopen_connect(xfd,
273 needbind?(struct sockaddr *)&us:NULL, uslen,
274 (struct sockaddr *)&them, themlen,
275 opts, pf, socktype, proto, false)) != 0) {
276 return result;
278 if ((result = _xio_openlate(xfd, opts)) < 0) {
279 return result;
281 return STAT_OK;
284 #if WITH_LISTEN
285 static
286 int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts,
287 int xioflags, xiofile_t *xxfd, unsigned groups,
288 int dummy1, int dummy2, int dummy3) {
289 struct single *xfd = &xxfd->stream;
290 const char *pfname = argv[1];
291 const char *protname = argv[2];
292 const char *usname = argv[3];
293 char *garbage;
294 int pf;
295 int proto;
296 int socktype = SOCK_STREAM;
297 union sockaddr_union us; socklen_t uslen; size_t ussize;
298 struct opt *opts0;
299 int result;
301 if (argc != 4) {
302 Error2("%s: wrong number of parameters (%d instead of 3)",
303 argv[0], argc-1);
304 return STAT_NORETRY;
307 pf = strtoul(pfname, &garbage, 0);
308 if (*garbage != '\0') {
309 Warn1("garbage in parameter: \"%s\"", garbage);
312 proto = strtoul(protname, &garbage, 0);
313 if (*garbage != '\0') {
314 Warn1("garbage in parameter: \"%s\"", garbage);
317 retropt_socket_pf(opts, &pf);
318 retropt_int(opts, OPT_SO_TYPE, &socktype);
319 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
320 xfd->howtoend = END_SHUTDOWN;
322 socket_init(0, &us);
323 ussize = 0;
324 if ((result =
325 dalan(usname, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
326 < 0) {
327 Error1("data too long: \"%s\"", usname);
328 } else if (result > 0) {
329 Error1("syntax error in \"%s\"", usname);
331 uslen = ussize + sizeof(us.soa.sa_family)
332 #if HAVE_STRUCT_SOCKADDR_SALEN
333 + sizeof(us.soa.sa_len)
334 #endif
336 us.soa.sa_family = pf;
338 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
339 applyopts(-1, opts, PH_INIT);
340 applyopts(-1, opts, PH_EARLY);
342 opts0 = copyopts(opts, GROUP_ALL);
344 if ((result =
345 xioopen_listen(xfd, xioflags,
346 (struct sockaddr *)&us, uslen,
347 opts, opts0, 0/*instead of pf*/, socktype, proto))
348 != STAT_OK)
349 return result;
350 return STAT_OK;
352 #endif /* WITH_LISTEN */
354 /* we expect the form: ...:domain:type:protocol:remote-address */
355 static
356 int xioopen_socket_sendto(int argc, const char *argv[], struct opt *opts,
357 int xioflags, xiofile_t *xxfd, unsigned groups,
358 int dummy1, int dummy2, int dummy3) {
359 int result;
361 if (argc != 5) {
362 Error2("%s: wrong number of parameters (%d instead of 4)",
363 argv[0], argc-1);
364 return STAT_NORETRY;
366 if ((result =
367 _xioopen_socket_sendto(argv[1], argv[2], argv[3], argv[4],
368 opts, xioflags, xxfd, groups))
369 != STAT_OK) {
370 return result;
372 _xio_openlate(&xxfd->stream, opts);
373 return STAT_OK;
376 static
377 int _xioopen_socket_sendto(const char *pfname, const char *type,
378 const char *protname, const char *address,
379 struct opt *opts, int xioflags, xiofile_t *xxfd,
380 unsigned groups) {
381 xiosingle_t *xfd = &xxfd->stream;
382 char *garbage;
383 union sockaddr_union us = {{0}};
384 socklen_t uslen = 0; size_t ussize;
385 size_t themsize;
386 int pf;
387 int socktype = SOCK_RAW;
388 int proto;
389 bool needbind = false;
390 char *bindstring = NULL;
391 int result;
393 pf = strtoul(pfname, &garbage, 0);
394 if (*garbage != '\0') {
395 Warn1("garbage in parameter: \"%s\"", garbage);
398 socktype = strtoul(type, &garbage, 0);
399 if (*garbage != '\0') {
400 Warn1("garbage in parameter: \"%s\"", garbage);
403 proto = strtoul(protname, &garbage, 0);
404 if (*garbage != '\0') {
405 Warn1("garbage in parameter: \"%s\"", garbage);
408 retropt_socket_pf(opts, &pf);
409 retropt_int(opts, OPT_SO_TYPE, &socktype);
410 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
411 xfd->howtoend = END_SHUTDOWN;
413 xfd->peersa.soa.sa_family = pf;
414 themsize = 0;
415 if ((result =
416 dalan(address, (char *)&xfd->peersa.soa.sa_data, &themsize,
417 sizeof(xfd->peersa)))
418 < 0) {
419 Error1("data too long: \"%s\"", address);
420 } else if (result > 0) {
421 Error1("syntax error in \"%s\"", address);
423 xfd->salen = themsize + sizeof(sa_family_t)
424 #if HAVE_STRUCT_SOCKADDR_SALEN
425 + sizeof(xfd->peersa.soa.sa_len)
426 #endif
428 #if HAVE_STRUCT_SOCKADDR_SALEN
429 xfd->peersa.soa.sa_len =
430 sizeof(xfd->peersa.soa.sa_len) + sizeof(xfd->peersa.soa.sa_family) +
431 themsize;
432 #endif
434 /* ...res_opts[] */
435 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
436 applyopts(-1, opts, PH_INIT);
438 if (pf == PF_UNSPEC) {
439 pf = xfd->peersa.soa.sa_family;
442 xfd->dtype = XIODATA_RECVFROM;
444 if (retropt_string(opts, OPT_BIND, &bindstring) == 0) {
445 ussize = 0;
446 if ((result =
447 dalan(bindstring, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
448 < 0) {
449 Error1("data too long: \"%s\"", bindstring);
450 } else if (result > 0) {
451 Error1("syntax error in \"%s\"", bindstring);
453 us.soa.sa_family = pf;
454 uslen = ussize + sizeof(sa_family_t)
455 #if HAVE_STRUCT_SOCKADDR_SALEN
456 + sizeof(us.soa.sa_len)
457 #endif
459 needbind = true;
462 return
463 _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
464 opts, xioflags, xfd, groups, pf, socktype, proto);
468 /* we expect the form: ...:domain:socktype:protocol:local-address */
469 static
470 int xioopen_socket_recvfrom(int argc, const char *argv[], struct opt *opts,
471 int xioflags, xiofile_t *xxfd, unsigned groups,
472 int dummy, int summy2, int dummy3) {
473 struct single *xfd = &xxfd->stream;
474 const char *pfname = argv[1];
475 const char *typename = argv[2];
476 const char *protname = argv[3];
477 const char *address = argv[4];
478 char *garbage;
479 union sockaddr_union *us = &xfd->para.socket.la;
480 socklen_t uslen; size_t ussize;
481 int pf, socktype, proto;
482 char *rangename;
483 int result;
485 if (argc != 5) {
486 Error2("%s: wrong number of parameters (%d instead of 4)",
487 argv[0], argc-1);
488 return STAT_NORETRY;
491 pf = strtoul(pfname, &garbage, 0);
492 if (*garbage != '\0') {
493 Warn1("garbage in parameter: \"%s\"", garbage);
496 socktype = strtoul(typename, &garbage, 0);
497 if (*garbage != '\0') {
498 Warn1("garbage in parameter: \"%s\"", garbage);
501 proto = strtoul(protname, &garbage, 0);
502 if (*garbage != '\0') {
503 Warn1("garbage in parameter: \"%s\"", garbage);
506 retropt_socket_pf(opts, &pf);
507 retropt_int(opts, OPT_SO_TYPE, &socktype);
508 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
509 xfd->howtoend = END_NONE;
511 ussize = 0;
512 if ((result =
513 dalan(address, (char *)&us->soa.sa_data, &ussize, sizeof(*us)))
514 < 0) {
515 Error1("data too long: \"%s\"", address);
516 } else if (result > 0) {
517 Error1("syntax error in \"%s\"", address);
519 us->soa.sa_family = pf;
520 uslen = ussize + sizeof(us->soa.sa_family)
521 #if HAVE_STRUCT_SOCKADDR_SALEN
522 + sizeof(us->soa.sa_len);
523 #endif
525 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
527 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
528 if (xioparserange(rangename, 0, &xfd->para.socket.range) < 0) {
529 return STAT_NORETRY;
531 xfd->para.socket.dorange = true;
532 free(rangename);
535 if ((result =
536 _xioopen_dgram_recvfrom(xfd, xioflags, &us->soa, uslen,
537 opts, pf, socktype, proto, E_ERROR))
538 != STAT_OK) {
539 return result;
541 _xio_openlate(xfd, opts);
542 return STAT_OK;
545 /* we expect the form: ...:domain:type:protocol:local-address */
546 static
547 int xioopen_socket_recv(int argc, const char *argv[], struct opt *opts,
548 int xioflags, xiofile_t *xxfd, unsigned groups,
549 int dummy1, int dummy2, int dummy3) {
550 struct single *xfd = &xxfd->stream;
551 const char *pfname = argv[1];
552 const char *typename = argv[2];
553 const char *protname = argv[3];
554 const char *address = argv[4];
555 char *garbage;
556 union sockaddr_union us;
557 socklen_t uslen; size_t ussize;
558 int pf, socktype, proto;
559 char *rangename;
560 int result;
562 if (argc != 5) {
563 Error2("%s: wrong number of parameters (%d instead of 4)",
564 argv[0], argc-1);
565 return STAT_NORETRY;
568 pf = strtoul(pfname, &garbage, 0);
569 if (*garbage != '\0') {
570 Warn1("garbage in parameter: \"%s\"", garbage);
573 socktype = strtoul(typename, &garbage, 0);
574 if (*garbage != '\0') {
575 Warn1("garbage in parameter: \"%s\"", garbage);
578 proto = strtoul(protname, &garbage, 0);
579 if (*garbage != '\0') {
580 Warn1("garbage in parameter: \"%s\"", garbage);
583 retropt_socket_pf(opts, &pf);
584 retropt_int(opts, OPT_SO_TYPE, &socktype);
585 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
586 xfd->howtoend = END_NONE;
588 ussize = 0;
589 if ((result =
590 dalan(address, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
591 < 0) {
592 Error1("data too long: \"%s\"", address);
593 } else if (result > 0) {
594 Error1("syntax error in \"%s\"", address);
596 us.soa.sa_family = pf;
597 uslen = ussize + sizeof(sa_family_t)
598 #if HAVE_STRUCT_SOCKADDR_SALEN
599 +sizeof(us.soa.sa_len)
600 #endif
602 xfd->dtype = XIOREAD_RECV;
603 xfd->para.socket.la.soa.sa_family = pf;
605 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
606 if (xioparserange(rangename, 0, &xfd->para.socket.range) < 0) {
607 return STAT_NORETRY;
609 xfd->para.socket.dorange = true;
610 free(rangename);
613 if ((result =
614 _xioopen_dgram_recv(xfd, xioflags, &us.soa,
615 uslen, opts, pf, socktype, proto, E_ERROR))
616 != STAT_OK) {
617 return result;
619 _xio_openlate(xfd, opts);
620 return STAT_OK;
624 /* we expect the form: ...:domain:type:protocol:remote-address */
625 static
626 int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts,
627 int xioflags, xiofile_t *xxfd, unsigned groups,
628 int dummy1, int dummy2, int dummy3) {
629 xiosingle_t *xfd = &xxfd->stream;
630 const char *pfname = argv[1];
631 const char *typename = argv[2];
632 const char *protname = argv[3];
633 const char *address = argv[4];
634 char *garbage;
635 char *rangename;
636 size_t themsize;
637 int pf;
638 int result;
640 if (argc != 5) {
641 Error2("%s: wrong number of parameters (%d instead of 4)",
642 argv[0], argc-1);
643 return STAT_NORETRY;
646 pf = strtoul(pfname, &garbage, 0);
647 if (*garbage != '\0') {
648 Warn1("garbage in parameter: \"%s\"", garbage);
651 retropt_socket_pf(opts, &pf);
652 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
653 xfd->howtoend = END_SHUTDOWN;
655 xfd->peersa.soa.sa_family = pf;
656 themsize = 0;
657 if ((result =
658 dalan(address, (char *)&xfd->peersa.soa.sa_data, &themsize,
659 sizeof(xfd->peersa)))
660 < 0) {
661 Error1("data too long: \"%s\"", address);
662 } else if (result > 0) {
663 Error1("syntax error in \"%s\"", address);
665 xfd->salen = themsize + sizeof(sa_family_t);
666 #if HAVE_STRUCT_SOCKADDR_SALEN
667 xfd->peersa.soa.sa_len =
668 sizeof(xfd->peersa.soa.sa_len) + sizeof(xfd->peersa.soa.sa_family) +
669 themsize;
670 #endif
672 if ((result =
673 _xioopen_socket_sendto(pfname, typename, protname, address,
674 opts, xioflags, xxfd, groups))
675 != STAT_OK) {
676 return result;
679 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
681 xfd->para.socket.la.soa.sa_family = xfd->peersa.soa.sa_family;
683 /* which reply sockets will accept - determine by range option */
684 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
685 if (xioparserange(rangename, 0, &xfd->para.socket.range) < 0) {
686 free(rangename);
687 return STAT_NORETRY;
689 xfd->para.socket.dorange = true;
690 xfd->dtype |= XIOREAD_RECV_CHECKRANGE;
691 free(rangename);
694 _xio_openlate(xfd, opts);
695 return STAT_OK;
698 #endif /* WITH_GENERICSOCKET */
701 /* a subroutine that is common to all socket addresses that want to connect
702 to a peer address.
703 might fork.
704 applies and consumes the following options:
705 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
706 PH_CONNECTED, PH_LATE,
707 OFUNC_OFFSET,
708 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
709 returns 0 on success.
711 int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen,
712 struct sockaddr *them, size_t themlen,
713 struct opt *opts, int pf, int socktype, int protocol,
714 bool alt, int level) {
715 int fcntl_flags = 0;
716 char infobuff[256];
717 union sockaddr_union la;
718 socklen_t lalen = themlen;
719 int _errno;
720 int result;
722 if ((xfd->fd = xiosocket(opts, pf, socktype, protocol, level)) < 0) {
723 return STAT_RETRYLATER;
726 applyopts_offset(xfd, opts);
727 applyopts(xfd->fd, opts, PH_PASTSOCKET);
728 applyopts(xfd->fd, opts, PH_FD);
730 applyopts_cloexec(xfd->fd, opts);
732 applyopts(xfd->fd, opts, PH_PREBIND);
733 applyopts(xfd->fd, opts, PH_BIND);
734 #if WITH_TCP || WITH_UDP
735 if (alt) {
736 union sockaddr_union sin, *sinp;
737 unsigned short *port, i, N;
738 div_t dv;
740 /* prepare sockaddr for bind probing */
741 if (us) {
742 sinp = (union sockaddr_union *)us;
743 } else {
744 if (them->sa_family == AF_INET) {
745 socket_in_init(&sin.ip4);
746 #if WITH_IP6
747 } else {
748 socket_in6_init(&sin.ip6);
749 #endif
751 sinp = &sin;
753 if (them->sa_family == AF_INET) {
754 port = &sin.ip4.sin_port;
755 #if WITH_IP6
756 } else if (them->sa_family == AF_INET6) {
757 port = &sin.ip6.sin6_port;
758 #endif
759 } else {
760 port = 0; /* just to make compiler happy */
762 /* combine random+step variant to quickly find a free port when only
763 few are in use, and certainly find a free port in defined time even
764 if there are almost all in use */
765 /* dirt 1: having tcp/udp code in socket function */
766 /* dirt 2: using a time related system call for init of random */
768 /* generate a random port, with millisecond random init */
769 #if 0
770 struct timeb tb;
771 ftime(&tb);
772 srandom(tb.time*1000+tb.millitm);
773 #else
774 struct timeval tv;
775 struct timezone tz;
776 tz.tz_minuteswest = 0;
777 tz.tz_dsttime = 0;
778 if ((result = Gettimeofday(&tv, &tz)) < 0) {
779 Warn2("gettimeofday(%p, {0,0}): %s", &tv, strerror(errno));
781 srandom(tv.tv_sec*1000000+tv.tv_usec);
782 #endif
784 dv = div(random(), IPPORT_RESERVED-XIO_IPPORT_LOWER);
785 i = N = XIO_IPPORT_LOWER + dv.rem;
786 do { /* loop over lowport bind() attempts */
787 *port = htons(i);
788 if (Bind(xfd->fd, (struct sockaddr *)sinp, sizeof(*sinp)) < 0) {
789 Msg4(errno==EADDRINUSE?E_INFO:level,
790 "bind(%d, {%s}, "F_Zd"): %s", xfd->fd,
791 sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)),
792 sizeof(*sinp), strerror(errno));
793 if (errno != EADDRINUSE) {
794 Close(xfd->fd);
795 return STAT_RETRYLATER;
797 } else {
798 break; /* could bind to port, good, continue past loop */
800 --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1;
801 if (i == N) {
802 Msg(level, "no low port available");
803 /*errno = EADDRINUSE; still assigned */
804 Close(xfd->fd);
805 return STAT_RETRYLATER;
807 } while (i != N);
808 } else
809 #endif /* WITH_TCP || WITH_UDP */
811 if (us) {
812 if (Bind(xfd->fd, us, uslen) < 0) {
813 Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
814 xfd->fd, sockaddr_info(us, uslen, infobuff, sizeof(infobuff)),
815 uslen, strerror(errno));
816 Close(xfd->fd);
817 return STAT_RETRYLATER;
821 applyopts(xfd->fd, opts, PH_PASTBIND);
823 applyopts(xfd->fd, opts, PH_CONNECT);
825 if (xfd->para.socket.connect_timeout.tv_sec != 0 ||
826 xfd->para.socket.connect_timeout.tv_usec != 0) {
827 fcntl_flags = Fcntl(xfd->fd, F_GETFL);
828 Fcntl_l(xfd->fd, F_SETFL, fcntl_flags|O_NONBLOCK);
831 result = Connect(xfd->fd, (struct sockaddr *)them, themlen);
832 _errno = errno;
833 la.soa.sa_family = them->sa_family; lalen = sizeof(la);
834 if (Getsockname(xfd->fd, &la.soa, &lalen) < 0) {
835 Msg4(level-1, "getsockname(%d, %p, {%d}): %s",
836 xfd->fd, &la.soa, lalen, strerror(errno));
838 errno = _errno;
839 if (result < 0) {
840 if (errno == EINPROGRESS) {
841 if (xfd->para.socket.connect_timeout.tv_sec != 0 ||
842 xfd->para.socket.connect_timeout.tv_usec != 0) {
843 struct timeval timeout;
844 struct pollfd writefd;
845 int result;
847 Info4("connect(%d, %s, "F_Zd"): %s",
848 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
849 themlen, strerror(errno));
850 timeout = xfd->para.socket.connect_timeout;
851 writefd.fd = xfd->fd;
852 writefd.events = (POLLOUT|POLLERR);
853 result = xiopoll(&writefd, 1, &timeout);
854 if (result < 0) {
855 Msg4(level, "xiopoll({%d,POLLOUT|POLLERR},,{"F_tv_sec"."F_tv_usec"): %s",
856 xfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno));
857 return STAT_RETRYLATER;
859 if (result == 0) {
860 Msg2(level, "connecting to %s: %s",
861 sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
862 strerror(ETIMEDOUT));
863 return STAT_RETRYLATER;
865 if (writefd.revents & POLLERR) {
866 #if 0
867 unsigned char dummy[1];
868 Read(xfd->fd, &dummy, 1); /* get error message */
869 Msg2(level, "connecting to %s: %s",
870 sockaddr_info(them, infobuff, sizeof(infobuff)),
871 strerror(errno));
872 #else
873 Connect(xfd->fd, them, themlen); /* get error message */
874 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
875 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
876 themlen, strerror(errno));
877 #endif
878 return STAT_RETRYLATER;
880 /* otherwise OK */
881 Fcntl_l(xfd->fd, F_SETFL, fcntl_flags);
882 } else {
883 Warn4("connect(%d, %s, "F_Zd"): %s",
884 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
885 themlen, strerror(errno));
887 } else if (pf == PF_UNIX && errno == EPROTOTYPE) {
888 /* this is for UNIX domain sockets: a connect attempt seems to be
889 the only way to distinguish stream and datagram sockets */
890 int _errno = errno;
891 Info4("connect(%d, %s, "F_Zd"): %s",
892 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
893 themlen, strerror(errno));
894 #if 0
895 Info("assuming datagram socket");
896 xfd->dtype = DATA_RECVFROM;
897 xfd->salen = themlen;
898 memcpy(&xfd->peersa.soa, them, xfd->salen);
899 #endif
900 /*!!! and remove bind socket */
901 Close(xfd->fd); xfd->fd = -1;
902 errno = _errno;
903 return -1;
904 } else {
905 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
906 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
907 themlen, strerror(errno));
908 Close(xfd->fd);
909 return STAT_RETRYLATER;
911 } else { /* result >= 0 */
912 Notice1("successfully connected from local address %s",
913 sockaddr_info(&la.soa, themlen, infobuff, sizeof(infobuff)));
916 applyopts_fchown(xfd->fd, opts); /* OPT_USER, OPT_GROUP */
917 applyopts(xfd->fd, opts, PH_CONNECTED);
918 applyopts(xfd->fd, opts, PH_LATE);
920 return STAT_OK;
924 /* a subroutine that is common to all socket addresses that want to connect
925 to a peer address.
926 might fork.
927 applies and consumes the following option:
928 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
929 PH_CONNECTED, PH_LATE,
930 OFUNC_OFFSET,
931 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
932 returns 0 on success.
934 int xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen,
935 struct sockaddr *them, size_t themlen,
936 struct opt *opts, int pf, int socktype, int protocol,
937 bool alt) {
938 bool dofork = false;
939 struct opt *opts0;
940 char infobuff[256];
941 int level;
942 int result;
944 retropt_bool(opts, OPT_FORK, &dofork);
946 opts0 = copyopts(opts, GROUP_ALL);
948 Notice1("opening connection to %s",
949 sockaddr_info(them, themlen, infobuff, sizeof(infobuff)));
951 do { /* loop over retries and forks */
953 #if WITH_RETRY
954 if (xfd->forever || xfd->retry) {
955 level = E_INFO;
956 } else
957 #endif /* WITH_RETRY */
958 level = E_ERROR;
959 result =
960 _xioopen_connect(xfd, us, uslen, them, themlen, opts,
961 pf, socktype, protocol, alt, level);
962 switch (result) {
963 case STAT_OK: break;
964 #if WITH_RETRY
965 case STAT_RETRYLATER:
966 if (xfd->forever || xfd->retry) {
967 --xfd->retry;
968 if (result == STAT_RETRYLATER) {
969 Nanosleep(&xfd->intervall, NULL);
971 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
972 continue;
974 return STAT_NORETRY;
975 #endif /* WITH_RETRY */
976 default:
977 return result;
980 if (dofork) {
981 xiosetchilddied(); /* set SIGCHLD handler */
984 #if WITH_RETRY
985 if (dofork) {
986 pid_t pid;
987 int level = E_ERROR;
988 if (xfd->forever || xfd->retry) {
989 level = E_WARN; /* most users won't expect a problem here,
990 so Notice is too weak */
993 while ((pid = xio_fork(false, level)) < 0) {
994 --xfd->retry;
995 if (xfd->forever || xfd->retry) {
996 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
997 Nanosleep(&xfd->intervall, NULL); continue;
999 return STAT_RETRYLATER;
1002 if (pid == 0) { /* child process */
1003 break;
1006 /* parent process */
1007 Close(xfd->fd);
1008 /* with and without retry */
1009 Nanosleep(&xfd->intervall, NULL);
1010 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
1011 continue; /* with next socket() bind() connect() */
1012 } else
1013 #endif /* WITH_RETRY */
1015 break;
1017 #if 0
1018 if ((result = _xio_openlate(fd, opts)) < 0)
1019 return result;
1020 #endif
1021 } while (true);
1023 return 0;
1027 /* common to xioopen_udp_sendto, ..unix_sendto, ..rawip
1028 applies and consumes the following option:
1029 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
1030 OFUNC_OFFSET
1031 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
1033 int _xioopen_dgram_sendto(/* them is already in xfd->peersa */
1034 union sockaddr_union *us, socklen_t uslen,
1035 struct opt *opts,
1036 int xioflags, xiosingle_t *xfd, unsigned groups,
1037 int pf, int socktype, int ipproto) {
1038 int level = E_ERROR;
1039 union sockaddr_union la; socklen_t lalen = sizeof(la);
1040 char infobuff[256];
1042 if ((xfd->fd = xiosocket(opts, pf, socktype, ipproto, level)) < 0) {
1043 return STAT_RETRYLATER;
1046 applyopts_offset(xfd, opts);
1047 applyopts_single(xfd, opts, PH_PASTSOCKET);
1048 applyopts(xfd->fd, opts, PH_PASTSOCKET);
1049 applyopts(xfd->fd, opts, PH_FD);
1051 applyopts_cloexec(xfd->fd, opts);
1053 applyopts(xfd->fd, opts, PH_PREBIND);
1054 applyopts(xfd->fd, opts, PH_BIND);
1056 if (us) {
1057 if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
1058 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s",
1059 xfd->fd, sockaddr_info((struct sockaddr *)us, uslen, infobuff, sizeof(infobuff)),
1060 uslen, strerror(errno));
1061 Close(xfd->fd);
1062 return STAT_RETRYLATER;
1066 applyopts(xfd->fd, opts, PH_PASTBIND);
1068 /*applyopts(xfd->fd, opts, PH_CONNECT);*/
1070 if (Getsockname(xfd->fd, &la.soa, &lalen) < 0) {
1071 Warn4("getsockname(%d, %p, {%d}): %s",
1072 xfd->fd, &la.soa, lalen, strerror(errno));
1075 applyopts_fchown(xfd->fd, opts);
1076 applyopts(xfd->fd, opts, PH_CONNECTED);
1077 applyopts(xfd->fd, opts, PH_LATE);
1079 /* xfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */
1080 Notice1("successfully prepared local socket %s",
1081 sockaddr_info(&la.soa, lalen, infobuff, sizeof(infobuff)));
1083 return STAT_OK;
1087 /* when the recvfrom address (with option fork) receives a packet it keeps this
1088 packet in the IP stacks input queue and forks a sub process. The sub process
1089 then reads this packet for processing its data.
1090 There is a problem because the parent process would find the same packet
1091 again if it calls select()/poll() before the child process reads the
1092 packet.
1093 To solve this problem we implement the following mechanism:
1094 The sub process sends a SIGUSR1 when it has read the packet (or a SIGCHLD if
1095 it dies before). The parent process waits until it receives that signal and
1096 only then continues to listen.
1097 To prevent a signal from another process to trigger our loop, we pass the
1098 pid of the sub process to the signal handler in xio_waitingfor. The signal
1099 handler sets xio_hashappened if the pid matched.
1101 static pid_t xio_waitingfor; /* info from recv loop to signal handler:
1102 indicates the pid of the child process
1103 that should send us the USR1 signal */
1104 static bool xio_hashappened; /* info from signal handler to loop: child
1105 process has read ("consumed") the packet */
1106 /* this is the signal handler for USR1 and CHLD */
1107 void xiosigaction_hasread(int signum
1108 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1109 , siginfo_t *siginfo, void *ucontext
1110 #endif
1112 pid_t pid;
1113 int _errno;
1114 int status = 0;
1115 bool wassig = false;
1117 _errno = errno;
1118 diag_in_handler = 1;
1119 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1120 Debug5("xiosigaction_hasread(%d, {%d,%d,%d,"F_pid"}, )",
1121 signum, siginfo->si_signo, siginfo->si_errno, siginfo->si_code,
1122 siginfo->si_pid);
1123 #else
1124 Debug1("xiosigaction_hasread(%d)", signum);
1125 #endif
1126 if (signum == SIGCHLD) {
1127 do {
1128 pid = Waitpid(-1, &status, WNOHANG);
1129 if (pid == 0) {
1130 Msg(wassig?E_INFO:E_WARN,
1131 "waitpid(-1, {}, WNOHANG): no child has exited");
1132 Info("xiosigaction_hasread() finished");
1133 Debug("xiosigaction_hasread() ->");
1134 diag_in_handler = 0;
1135 errno = _errno;
1136 return;
1137 } else if (pid < 0 && errno == ECHILD) {
1138 Msg(wassig?E_INFO:E_WARN,
1139 "waitpid(-1, {}, WNOHANG): "F_strerror);
1140 Info("xiosigaction_hasread() finished");
1141 Debug("xiosigaction_hasread() ->");
1142 diag_in_handler = 0;
1143 errno = _errno;
1144 return;
1146 wassig = true;
1147 if (pid < 0) {
1148 Warn1("waitpid(-1, {%d}, WNOHANG): "F_strerror, status);
1149 Info("xiosigaction_hasread() finished");
1150 Debug("xiosigaction_hasread() ->");
1151 diag_in_handler = 0;
1152 errno = _errno;
1153 return;
1155 if (pid == xio_waitingfor) {
1156 xio_hashappened = true;
1157 Debug("xiosigaction_hasread() ->");
1158 diag_in_handler = 0;
1159 errno = _errno;
1160 return;
1162 } while (1);
1164 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1165 if (xio_waitingfor == siginfo->si_pid) {
1166 xio_hashappened = true;
1168 #else
1169 xio_hashappened = true;
1170 #endif
1171 #if !HAVE_SIGACTION
1172 Signal(sig, xiosigaction_hasread);
1173 #endif /* !HAVE_SIGACTION */
1174 Debug("xiosigaction_hasread() ->");
1175 diag_in_handler = 0;
1176 errno = _errno;
1177 return;
1181 /* waits for incoming packet, checks its source address and port. Depending
1182 on fork option, it may fork a subprocess.
1183 Returns STAT_OK if a the packet was accepted; with fork option, this is already in
1184 a new subprocess!
1185 Other return values indicate a problem; this can happen in the master
1186 process or in a subprocess.
1187 This function does not retry. If you need retries, handle this is a
1188 loop in the calling function.
1189 after fork, we set the forever/retry of the child process to 0
1190 applies and consumes the following options:
1191 PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD,
1192 PH_CONNECTED, PH_LATE, PH_LATE2
1193 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap
1195 int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
1196 struct sockaddr *us, socklen_t uslen,
1197 struct opt *opts,
1198 int pf, int socktype, int proto, int level) {
1199 char *rangename;
1200 bool dofork = false;
1201 pid_t pid; /* mostly int; only used with fork */
1202 char infobuff[256];
1203 char lisname[256];
1204 bool drop = false; /* true if current packet must be dropped */
1205 int result;
1207 retropt_bool(opts, OPT_FORK, &dofork);
1209 if (dofork) {
1210 if (!(xioflags & XIO_MAYFORK)) {
1211 Error("option fork not allowed here");
1212 return STAT_NORETRY;
1214 xfd->flags |= XIO_DOESFORK;
1217 if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1219 if ((xfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) {
1220 return STAT_RETRYLATER;
1223 applyopts_single(xfd, opts, PH_PASTSOCKET);
1224 applyopts(xfd->fd, opts, PH_PASTSOCKET);
1226 applyopts_cloexec(xfd->fd, opts);
1228 applyopts(xfd->fd, opts, PH_PREBIND);
1229 applyopts(xfd->fd, opts, PH_BIND);
1230 if ((us != NULL) && Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
1231 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
1232 sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
1233 strerror(errno));
1234 Close(xfd->fd);
1235 return STAT_RETRYLATER;
1238 #if WITH_UNIX
1239 if (pf == AF_UNIX && us != NULL) {
1240 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
1242 #endif
1244 applyopts(xfd->fd, opts, PH_PASTBIND);
1245 #if WITH_UNIX
1246 if (pf == AF_UNIX && us != NULL) {
1247 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
1248 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
1249 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
1251 #endif /* WITH_UNIX */
1253 /* for generic sockets, this has already been retrieved */
1254 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
1255 if (xioparserange(rangename, pf, &xfd->para.socket.range)
1256 < 0) {
1257 free(rangename);
1258 return STAT_NORETRY;
1260 free(rangename);
1261 xfd->para.socket.dorange = true;
1264 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1265 xio_retropt_tcpwrap(xfd, opts);
1266 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1268 if (xioopts.logopt == 'm') {
1269 Info("starting recvfrom loop, switching to syslog");
1270 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
1271 } else {
1272 Info("starting recvfrom loop");
1275 if (dofork) {
1276 #if HAVE_SIGACTION
1278 struct sigaction act;
1279 memset(&act, 0, sizeof(struct sigaction));
1280 act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/
1281 #ifdef SA_SIGINFO /* not on Linux 2.0(.33) */
1282 |SA_SIGINFO
1283 #endif
1284 #ifdef SA_NOMASK
1285 |SA_NOMASK
1286 #endif
1288 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1289 act.sa_sigaction = xiosigaction_hasread;
1290 #else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */
1291 act.sa_handler = xiosigaction_hasread;
1292 #endif
1293 sigfillset(&act.sa_mask);
1294 if (Sigaction(SIGUSR1, &act, NULL) < 0) {
1295 /*! Linux man does not explicitely say that errno is defined */
1296 Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno));
1298 if (Sigaction(SIGCHLD, &act, NULL) < 0) {
1299 /*! Linux man does not explicitely say that errno is defined */
1300 Warn1("sigaction(SIGCHLD, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno));
1303 #else /* !HAVE_SIGACTION */
1304 /*!!!*/
1305 if (Signal(SIGUSR1, xiosigaction_hasread) == SIG_ERR) {
1306 Warn1("signal(SIGUSR1, xiosigaction_hasread): %s", strerror(errno));
1308 if (Signal(SIGCHLD, xiosigaction_hasread) == SIG_ERR) {
1309 Warn1("signal(SIGCHLD, xiosigaction_hasread): %s", strerror(errno));
1311 #endif /* !HAVE_SIGACTION */
1314 while (true) { /* but we only loop if fork option is set */
1315 char peername[256];
1316 union sockaddr_union _peername;
1317 union sockaddr_union _sockname;
1318 union sockaddr_union *pa = &_peername; /* peer address */
1319 union sockaddr_union *la = &_sockname; /* local address */
1320 socklen_t palen = sizeof(_peername); /* peer address size */
1321 char ctrlbuff[1024]; /* ancillary messages */
1322 struct msghdr msgh = {0};
1324 socket_init(pf, pa);
1326 if (drop) {
1327 char *dummy[2];
1329 Recv(xfd->fd, dummy, sizeof(dummy), 0);
1330 drop = true;
1333 /* loop until select()/poll() returns valid */
1334 do {
1335 struct pollfd readfd;
1336 /*? int level = E_ERROR;*/
1337 if (us != NULL) {
1338 Notice1("receiving on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
1339 } else {
1340 Notice1("receiving IP protocol %u", proto);
1342 readfd.fd = xfd->fd;
1343 readfd.events = POLLIN;
1344 if (xiopoll(&readfd, 1, NULL) > 0) {
1345 break;
1348 if (errno == EINTR) {
1349 continue;
1352 Msg2(level, "poll({%d,,},,-1): %s", xfd->fd, strerror(errno));
1353 Close(xfd->fd);
1354 return STAT_RETRYLATER;
1355 } while (true);
1357 msgh.msg_name = pa;
1358 msgh.msg_namelen = palen;
1359 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
1360 msgh.msg_control = ctrlbuff;
1361 #endif
1362 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
1363 msgh.msg_controllen = sizeof(ctrlbuff);
1364 #endif
1365 if (xiogetpacketsrc(xfd->fd, &msgh) < 0) {
1366 return STAT_RETRYLATER;
1368 palen = msgh.msg_namelen;
1370 Notice1("receiving packet from %s"/*"src"*/,
1371 sockaddr_info((struct sockaddr *)pa, palen, peername, sizeof(peername))/*,
1372 sockaddr_info(&la->soa, sockname, sizeof(sockname))*/);
1374 xiodopacketinfo(&msgh, true, true);
1376 if (xiocheckpeer(xfd, pa, la) < 0) {
1377 /* drop packet */
1378 char buff[512];
1379 Recv(xfd->fd, buff, sizeof(buff), 0);
1380 continue;
1382 Info1("permitting packet from %s",
1383 sockaddr_info((struct sockaddr *)pa, palen,
1384 infobuff, sizeof(infobuff)));
1386 /* set the env vars describing the local and remote sockets */
1387 /*xiosetsockaddrenv("SOCK", la, lalen, proto);*/
1388 xiosetsockaddrenv("PEER", pa, palen, proto);
1390 applyopts(xfd->fd, opts, PH_FD);
1392 applyopts(xfd->fd, opts, PH_CONNECTED);
1394 xfd->peersa = *(union sockaddr_union *)pa;
1395 xfd->salen = palen;
1397 if (dofork) {
1398 sigset_t mask_sigchldusr1;
1400 /* we must prevent that the current packet triggers another fork;
1401 therefore we wait for a signal from the recent child: USR1
1402 indicates that is has consumed the last packet; CHLD means it has
1403 terminated */
1404 /* block SIGCHLD and SIGUSR1 until parent is ready to react */
1405 sigemptyset(&mask_sigchldusr1);
1406 sigaddset(&mask_sigchldusr1, SIGCHLD);
1407 sigaddset(&mask_sigchldusr1, SIGUSR1);
1408 Sigprocmask(SIG_BLOCK, &mask_sigchldusr1, NULL);
1410 if ((pid = xio_fork(false, level)) < 0) {
1411 Close(xfd->fd);
1412 Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
1413 return STAT_RETRYLATER;
1416 if (pid == 0) { /* child */
1417 /* no reason to block SIGCHLD in child process */
1418 Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
1419 xfd->ppid = Getppid(); /* send parent a signal when packet has
1420 been consumed */
1422 #if WITH_RETRY
1423 /* !? */
1424 xfd->retry = 0;
1425 xfd->forever = 0;
1426 level = E_ERROR;
1427 #endif /* WITH_RETRY */
1429 #if WITH_UNIX
1430 /* with UNIX sockets: only listening parent is allowed to remove
1431 the socket file */
1432 xfd->opt_unlink_close = false;
1433 #endif /* WITH_UNIX */
1435 break;
1438 /* server: continue loop with listen */
1439 xio_waitingfor = pid;
1440 /* now we are ready to handle signals */
1441 Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
1443 while (!xio_hashappened) {
1444 Sleep(UINT_MAX); /* any signal lets us continue */
1446 xio_waitingfor = 0; /* so this child will not set hashappened again */
1447 xio_hashappened = false;
1449 Info("continue listening");
1450 } else {
1451 break;
1454 if ((result = _xio_openlate(xfd, opts)) != 0)
1455 return STAT_NORETRY;
1457 return STAT_OK;
1461 /* returns STAT_* */
1462 int _xioopen_dgram_recv(struct single *xfd, int xioflags,
1463 struct sockaddr *us, socklen_t uslen,
1464 struct opt *opts, int pf, int socktype, int proto,
1465 int level) {
1466 char *rangename;
1467 char infobuff[256];
1469 if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1471 if ((xfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) {
1472 return STAT_RETRYLATER;
1475 applyopts_single(xfd, opts, PH_PASTSOCKET);
1476 applyopts(xfd->fd, opts, PH_PASTSOCKET);
1478 applyopts_cloexec(xfd->fd, opts);
1480 applyopts(xfd->fd, opts, PH_PREBIND);
1481 applyopts(xfd->fd, opts, PH_BIND);
1482 if ((us != NULL) && Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
1483 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
1484 sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
1485 strerror(errno));
1486 Close(xfd->fd);
1487 return STAT_RETRYLATER;
1490 #if WITH_UNIX
1491 if (pf == AF_UNIX && us != NULL) {
1492 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
1494 #endif
1496 applyopts(xfd->fd, opts, PH_PASTBIND);
1497 #if WITH_UNIX
1498 if (pf == AF_UNIX && us != NULL) {
1499 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
1500 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
1501 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
1503 #endif /* WITH_UNIX */
1505 #if WITH_IP4 /*|| WITH_IP6*/
1506 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
1507 if (xioparserange(rangename, pf, &xfd->para.socket.range)
1508 < 0) {
1509 free(rangename);
1510 return STAT_NORETRY;
1512 free(rangename);
1513 xfd->para.socket.dorange = true;
1515 #endif
1517 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1518 xio_retropt_tcpwrap(xfd, opts);
1519 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1521 if (xioopts.logopt == 'm') {
1522 Info("starting recvfrom loop, switching to syslog");
1523 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
1524 } else {
1525 Info("starting recvfrom loop");
1528 return STAT_OK;
1532 int retropt_socket_pf(struct opt *opts, int *pf) {
1533 char *pfname;
1535 if (retropt_string(opts, OPT_PROTOCOL_FAMILY, &pfname) >= 0) {
1536 if (isdigit(pfname[0])) {
1537 *pf = strtoul(pfname, NULL /*!*/, 0);
1538 #if WITH_IP4
1539 } else if (!strcasecmp("inet", pfname) ||
1540 !strcasecmp("inet4", pfname) ||
1541 !strcasecmp("ip4", pfname) ||
1542 !strcasecmp("ipv4", pfname) ||
1543 !strcasecmp("2", pfname)) {
1544 *pf = PF_INET;
1545 #endif /* WITH_IP4 */
1546 #if WITH_IP6
1547 } else if (!strcasecmp("inet6", pfname) ||
1548 !strcasecmp("ip6", pfname) ||
1549 !strcasecmp("ipv6", pfname) ||
1550 !strcasecmp("10", pfname)) {
1551 *pf = PF_INET6;
1552 #endif /* WITH_IP6 */
1553 } else {
1554 Error1("unknown protocol family \"%s\"", pfname);
1555 /*! Warn("falling back to INET");*/
1557 free(pfname);
1558 return 0;
1560 return -1;
1564 /* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
1565 the arriving packet. in msgh the msg_name pointer must refer to an (empty)
1566 sockaddr storage. */
1567 int xiogetpacketsrc(int fd, struct msghdr *msgh) {
1568 char peekbuff[1];
1569 #if HAVE_STRUCT_IOVEC
1570 struct iovec iovec;
1571 #endif
1573 #if HAVE_STRUCT_IOVEC
1574 iovec.iov_base = peekbuff;
1575 iovec.iov_len = sizeof(peekbuff);
1576 msgh->msg_iov = &iovec;
1577 msgh->msg_iovlen = 1;
1578 #endif
1579 #if HAVE_STRUCT_MSGHDR_MSGFLAGS
1580 msgh->msg_flags = 0;
1581 #endif
1582 if (Recvmsg(fd, msgh, MSG_PEEK
1583 #ifdef MSG_TRUNC
1584 |MSG_TRUNC
1585 #endif
1586 ) < 0) {
1587 Warn1("recvmsg(): %s", strerror(errno));
1588 return STAT_RETRYLATER;
1590 return STAT_OK;
1594 /* works through the ancillary messages found in the given socket header record
1595 and logs the relevant information (E_DEBUG, E_INFO).
1596 calls protocol/layer specific functions for handling the messages
1597 creates appropriate environment vars if withenv is set */
1598 int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) {
1599 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
1600 struct cmsghdr *cmsg;
1602 /* parse ancillary messages */
1603 cmsg = CMSG_FIRSTHDR(msgh);
1604 while (cmsg != NULL) {
1605 int num = 0; /* number of data components of a ancill.msg */
1606 int i;
1607 char typbuff[16], *typp;
1608 char nambuff[128], *namp;
1609 char valbuff[256], *valp;
1610 char envbuff[256], *envp;
1612 if (withlog) {
1613 xiodump(CMSG_DATA(cmsg),
1614 cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
1615 valbuff, sizeof(valbuff)-1, 0);
1616 Debug4("ancillary message: len="F_cmsg_len", level=%d, type=%d, data=%s",
1617 cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type,
1618 valbuff);
1621 /* try to get the anc.msg. contents in handy components, protocol/level
1622 dependent */
1623 switch (cmsg->cmsg_level) {
1624 case SOL_SOCKET:
1625 xiolog_ancillary_socket(cmsg, &num, typbuff, sizeof(typbuff)-1,
1626 nambuff, sizeof(nambuff)-1,
1627 envbuff, sizeof(envbuff)-1,
1628 valbuff, sizeof(valbuff)-1);
1629 break;
1630 #if WITH_IP4 || WITH_IP6
1631 case SOL_IP:
1632 xiolog_ancillary_ip(cmsg, &num, typbuff, sizeof(typbuff)-1,
1633 nambuff, sizeof(nambuff)-1,
1634 envbuff, sizeof(envbuff)-1,
1635 valbuff, sizeof(valbuff)-1);
1636 break;
1637 #endif /* WITH_IP4 || WITH_IP6 */
1638 #if WITH_IP6
1639 case SOL_IPV6:
1640 xiolog_ancillary_ip6(cmsg, &num, typbuff, sizeof(typbuff)-1,
1641 nambuff, sizeof(nambuff)-1,
1642 envbuff, sizeof(envbuff)-1,
1643 valbuff, sizeof(valbuff)-1);
1644 break;
1645 #endif /* WITH_IP6 */
1646 default:
1647 num = 1;
1648 snprintf(typbuff, sizeof(typbuff)-1, "LEVEL%u", cmsg->cmsg_level);
1649 snprintf(nambuff, sizeof(nambuff)-1, "type%u", cmsg->cmsg_type);
1650 xiodump(CMSG_DATA(cmsg),
1651 cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
1652 valbuff, sizeof(valbuff)-1, 0);
1654 /* here the info is in typbuff (one string), nambuff (num consecutive
1655 strings), and valbuff (num consecutive strings) */
1656 i = 0;
1657 typp = typbuff; namp = nambuff; envp = envbuff; valp = valbuff;
1658 while (i < num) {
1659 if (withlog) {
1660 Info3("ancillary message: %s: %s=%s", typp, namp, valp);
1662 if (withenv) {
1663 if (*envp) {
1664 xiosetenv(envp, valp, 1, NULL);
1665 } else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) {
1666 xiosetenv(typp, valp, 1, NULL);
1667 } else {
1668 xiosetenv2(typp, namp, valp, 1, NULL);
1671 if (++i == num) break;
1672 namp = strchr(namp, '\0')+1;
1673 envp = strchr(envp, '\0')+1;
1674 valp = strchr(valp, '\0')+1;
1676 cmsg = CMSG_NXTHDR(msgh, cmsg);
1678 return 0;
1679 #else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
1680 return -1;
1681 #endif /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
1685 /* check if peer address is within permitted range.
1686 return >= 0 if so. */
1687 int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) {
1688 switch (sa->soa.sa_family) {
1689 #if WITH_IP4
1690 case PF_INET:
1691 return
1692 xiocheckrange_ip4(&sa->ip4, range);
1693 #endif /* WITH_IP4 */
1694 #if WITH_IP6
1695 case PF_INET6:
1696 return
1697 xiocheckrange_ip6(&sa->ip6, range);
1698 #endif /* WITH_IP6 */
1699 #if 0
1700 case PF_UNSPEC:
1702 socklen_t i;
1703 for (i = 0; i < sizeof(sa->soa.sa_data); ++i) {
1704 if ((range->netmask.soa.sa_data[i] & sa->soa.sa_data[i]) != range->netaddr.soa.sa_data[i]) {
1705 return -1;
1708 return 0;
1710 #endif
1712 return -1;
1715 int xiocheckpeer(xiosingle_t *xfd,
1716 union sockaddr_union *pa, union sockaddr_union *la) {
1717 char infobuff[256];
1718 int result;
1720 #if WITH_IP4
1721 if (xfd->para.socket.dorange) {
1722 if (pa == NULL) { return -1; }
1723 if (xiocheckrange(pa, &xfd->para.socket.range) < 0) {
1724 char infobuff[256];
1725 Warn1("refusing connection from %s due to range option",
1726 sockaddr_info((struct sockaddr *)pa, 0,
1727 infobuff, sizeof(infobuff)));
1728 return -1;
1730 Info1("permitting connection from %s due to range option",
1731 sockaddr_info((struct sockaddr *)pa, 0,
1732 infobuff, sizeof(infobuff)));
1734 #endif /* WITH_IP4 */
1736 #if WITH_TCP || WITH_UDP
1737 if (xfd->para.socket.ip.dosourceport) {
1738 if (pa == NULL) { return -1; }
1739 #if WITH_IP4
1740 if (pa->soa.sa_family == AF_INET &&
1741 ntohs(((struct sockaddr_in *)pa)->sin_port) != xfd->para.socket.ip.sourceport) {
1742 Warn1("refusing connection from %s due to wrong sourceport",
1743 sockaddr_info((struct sockaddr *)pa, 0,
1744 infobuff, sizeof(infobuff)));
1745 return -1;
1747 #endif /* WITH_IP4 */
1748 #if WITH_IP6
1749 if (pa->soa.sa_family == AF_INET6 &&
1750 ntohs(((struct sockaddr_in6 *)pa)->sin6_port) != xfd->para.socket.ip.sourceport) {
1751 Warn1("refusing connection from %s due to wrong sourceport",
1752 sockaddr_info((struct sockaddr *)pa, 0,
1753 infobuff, sizeof(infobuff)));
1754 return -1;
1756 #endif /* WITH_IP6 */
1757 Info1("permitting connection from %s due to sourceport option",
1758 sockaddr_info((struct sockaddr *)pa, 0,
1759 infobuff, sizeof(infobuff)));
1760 } else if (xfd->para.socket.ip.lowport) {
1761 if (pa == NULL) { return -1; }
1762 if (pa->soa.sa_family == AF_INET &&
1763 ntohs(((struct sockaddr_in *)pa)->sin_port) >= IPPORT_RESERVED) {
1764 Warn1("refusing connection from %s due to lowport option",
1765 sockaddr_info((struct sockaddr *)pa, 0,
1766 infobuff, sizeof(infobuff)));
1767 return -1;
1769 #if WITH_IP6
1770 else if (pa->soa.sa_family == AF_INET6 &&
1771 ntohs(((struct sockaddr_in6 *)pa)->sin6_port) >=
1772 IPPORT_RESERVED) {
1773 Warn1("refusing connection from %s due to lowport option",
1774 sockaddr_info((struct sockaddr *)pa, 0,
1775 infobuff, sizeof(infobuff)));
1776 return -1;
1778 #endif /* WITH_IP6 */
1779 Info1("permitting connection from %s due to lowport option",
1780 sockaddr_info((struct sockaddr *)pa, 0,
1781 infobuff, sizeof(infobuff)));
1783 #endif /* WITH_TCP || WITH_UDP */
1785 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1786 result = xio_tcpwrap_check(xfd, la, pa);
1787 if (result < 0) {
1788 char infobuff[256];
1789 Warn1("refusing connection from %s due to tcpwrapper option",
1790 sockaddr_info((struct sockaddr *)pa, 0,
1791 infobuff, sizeof(infobuff)));
1792 return -1;
1793 } else if (result > 0) {
1794 Info1("permitting connection from %s due to tcpwrapper option",
1795 sockaddr_info((struct sockaddr *)pa, 0,
1796 infobuff, sizeof(infobuff)));
1798 #endif /* (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1800 return 0; /* permitted */
1804 #if HAVE_STRUCT_CMSGHDR
1805 /* converts the ancillary message in *cmsg into a form useable for further
1806 processing. knows the specifics of common message types.
1807 returns the number of resulting syntax elements in *num
1808 returns a sequence of \0 terminated type strings in *typbuff
1809 returns a sequence of \0 terminated name strings in *nambuff
1810 returns a sequence of \0 terminated value strings in *valbuff
1811 the respective len parameters specify the available space in the buffers
1812 returns STAT_OK or other STAT_*
1814 static int
1815 xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num,
1816 char *typbuff, int typlen,
1817 char *nambuff, int namlen,
1818 char *envbuff, int envlen,
1819 char *valbuff, int vallen) {
1820 const char *cmsgtype, *cmsgname, *cmsgenvn;
1821 size_t msglen;
1822 struct timeval *tv;
1823 int rc = STAT_OK;
1825 #if defined(CMSG_DATA)
1827 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
1828 switch (cmsg->cmsg_type) {
1829 #ifdef SO_PASSCRED
1830 case SO_PASSCRED: /* this is really a UNIX/LOCAL message */
1831 /*! needs implementation */
1832 #endif /* SO_PASSCRED */
1833 #ifdef SO_RIGHTS
1834 case SO_RIGHTS: /* this is really a UNIX/LOCAL message */
1835 /*! needs implementation */
1836 #endif
1837 default: /* binary data */
1838 snprintf(typbuff, typlen, "SOCKET.%u", cmsg->cmsg_type);
1839 nambuff[0] = '\0'; strncat(nambuff, "data", namlen-1);
1840 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
1841 return STAT_OK;
1842 #ifdef SO_TIMESTAMP
1843 # ifdef SCM_TIMESTAMP
1844 case SCM_TIMESTAMP:
1845 # else
1846 case SO_TIMESTAMP:
1847 # endif
1848 tv = (struct timeval *)CMSG_DATA(cmsg);
1849 cmsgtype =
1850 #ifdef SCM_TIMESTAMP
1851 "SCM_TIMESTAMP" /* FreeBSD */
1852 #else
1853 "SO_TIMESTAMP" /* Linux */
1854 #endif
1856 cmsgname = "timestamp";
1857 cmsgenvn = "TIMESTAMP";
1858 { time_t t = tv->tv_sec; ctime_r(&t, valbuff); }
1859 snprintf(strchr(valbuff, '\0')-1/*del \n*/, vallen-strlen(valbuff)+1, ", %06ld usecs", (long)tv->tv_usec);
1860 break;
1861 #endif /* defined(SO_TIMESTAMP) */
1864 /* when we come here we provide a single parameter
1865 with type in cmsgtype, name in cmsgname,
1866 and value already in valbuff */
1867 *num = 1;
1868 if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING;
1869 typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1);
1870 if (strlen(cmsgname) >= namlen) rc = STAT_WARNING;
1871 nambuff[0] = '\0'; strncat(nambuff, cmsgname, namlen-1);
1872 if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING;
1873 envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1);
1874 return rc;
1876 #else /* !defined(CMSG_DATA) */
1878 return STAT_NORETRY;
1880 #endif /* !defined(CMSG_DATA) */
1882 #endif /* HAVE_STRUCT_CMSGHDR */
1885 /* return the name of the interface with given index
1886 or NULL if is fails
1887 The system call requires an arbitrary socket; the calling program may
1888 provide one in parameter ins to avoid creation of a dummy socket. ins must
1889 be <0 if it does not specify a socket fd. */
1890 char *xiogetifname(int ind, char *val, int ins) {
1891 #if !HAVE_PROTOTYPE_LIB_if_indextoname
1892 int s;
1893 struct ifreq ifr;
1895 if (ins >= 0) {
1896 s = ins;
1897 } else {
1898 if ((s = Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
1899 Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno));
1900 return NULL;
1904 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1905 ifr.ifr_index = ind;
1906 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1907 ifr.ifr_ifindex = ind;
1908 #endif
1909 #ifdef SIOCGIFNAME
1910 if(Ioctl(s, SIOCGIFNAME, &ifr) < 0) {
1911 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1912 Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_index=%d, ...}: %s",
1913 s, ifr.ifr_index, strerror(errno));
1914 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1915 Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_ifindex=%d, ...}: %s",
1916 s, ifr.ifr_ifindex, strerror(errno));
1917 #endif
1918 if (ins < 0) Close(s);
1919 return NULL;
1921 #endif /* SIOCGIFNAME */
1922 if (ins < 0) Close(s);
1923 strcpy(val, ifr.ifr_name);
1924 return val;
1925 #else /* HAVE_PROTOTYPE_LIB_if_indextoname */
1926 return if_indextoname(ind, val);
1927 #endif /* HAVE_PROTOTYPE_LIB_if_indextoname */
1931 /* parses a network specification consisting of an address and a mask. */
1932 int xioparsenetwork(const char *rangename, int pf, struct xiorange *range) {
1933 size_t addrlen = 0, masklen = 0;
1934 int result;
1936 switch (pf) {
1937 #if WITH_IP4
1938 case PF_INET:
1939 return xioparsenetwork_ip4(rangename, range);
1940 break;
1941 #endif /* WITH_IP4 */
1942 #if WITH_IP6
1943 case PF_INET6:
1944 return xioparsenetwork_ip6(rangename, range);
1945 break;
1946 #endif /* WITH_IP6 */
1947 case PF_UNSPEC:
1949 char *addrname;
1950 const char *maskname;
1951 if ((maskname = strchr(rangename, ':')) == NULL) {
1952 Error1("syntax error in range \"%s\": use <addr>:<mask>", rangename);
1953 return STAT_NORETRY;
1955 ++maskname; /* skip ':' */
1956 if ((addrname = Malloc(maskname-rangename)) == NULL) {
1957 return STAT_NORETRY;
1959 strncpy(addrname, rangename, maskname-rangename-1); /* ok */
1960 addrname[maskname-rangename-1] = '\0';
1961 result =
1962 dalan(addrname, (char *)&range->netaddr.soa.sa_data, &addrlen,
1963 sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
1964 /* data length */);
1965 if (result < 0) {
1966 Error1("data too long: \"%s\"", addrname);
1967 free(addrname); return STAT_NORETRY;
1968 } else if (result > 0) {
1969 Error1("syntax error in \"%s\"", addrname);
1970 free(addrname); return STAT_NORETRY;
1972 free(addrname);
1973 result =
1974 dalan(maskname, (char *)&range->netmask.soa.sa_data, &masklen,
1975 sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
1976 /* data length */);
1977 if (result < 0) {
1978 Error1("data too long: \"%s\"", maskname);
1979 return STAT_NORETRY;
1980 } else if (result > 0) {
1981 Error1("syntax error in \"%s\"", maskname);
1982 return STAT_NORETRY;
1984 if (addrlen != masklen) {
1985 Error2("network address is "F_Zu" bytes long, mask is "F_Zu" bytes long",
1986 addrlen, masklen);
1987 /* recover by padding the shorter component with 0 */
1988 memset((char *)&range->netaddr.soa.sa_data+addrlen, 0,
1989 MAX(0, addrlen-masklen));
1990 memset((char *)&range->netmask.soa.sa_data+masklen, 0,
1991 MAX(0, masklen-addrlen));
1994 break;
1995 default:
1996 Error1("range option not supported with address family %d", pf);
1997 return STAT_NORETRY;
1999 return STAT_OK;
2003 /* parses a string of form address/bits or address:mask, and fills the fields
2004 of the range union. The addr component is masked with mask. */
2005 int xioparserange(const char *rangename, int pf, struct xiorange *range) {
2006 int i;
2007 if (xioparsenetwork(rangename, pf, range) < 0) {
2008 return -1;
2010 /* we have parsed the address and mask; now we make sure that the stored
2011 address has 0 where mask is 0, to simplify comparisions */
2012 switch (pf) {
2013 #if WITH_IP4
2014 case PF_INET:
2015 range->netaddr.ip4.sin_addr.s_addr &= range->netmask.ip4.sin_addr.s_addr;
2016 break;
2017 #endif /* WITH_IP4 */
2018 #if WITH_IP6
2019 case PF_INET6:
2020 return xiorange_ip6andmask(range);
2021 break;
2022 #endif /* WITH_IP6 */
2023 case PF_UNSPEC:
2024 for (i = 0; i < sizeof(range->netaddr); ++i) {
2025 ((char *)&range->netaddr)[i] &= ((char *)&range->netmask)[i];
2027 break;
2028 default:
2029 Error1("range option not supported with address family %d", pf);
2030 return STAT_NORETRY;
2032 return 0;
2036 /* set environment variables describing (part of) a socket address, e.g.
2037 SOCAT_SOCKADDR. lr (local/remote) specifies a string like "SOCK" or "PEER".
2038 proto should correspond to the third parameter of socket(2) and is used to
2039 determine the presence of port information. */
2040 int xiosetsockaddrenv(const char *lr,
2041 union sockaddr_union *sau, socklen_t salen,
2042 int proto) {
2043 # define XIOSOCKADDRENVLEN 256
2044 char namebuff[XIOSOCKADDRENVLEN];
2045 char valuebuff[XIOSOCKADDRENVLEN];
2046 int idx = 0, result;
2048 strcpy(namebuff, lr);
2049 switch (sau->soa.sa_family) {
2050 #if WITH_UNIX
2051 case PF_UNIX:
2052 result =
2053 xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2054 valuebuff, XIOSOCKADDRENVLEN,
2055 &sau->un, salen, proto);
2056 xiosetenv(namebuff, valuebuff, 1, NULL);
2057 break;
2058 #endif /* WITH_UNIX */
2059 #if WITH_IP4
2060 case PF_INET:
2061 do {
2062 result =
2063 xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2064 valuebuff, XIOSOCKADDRENVLEN,
2065 &sau->ip4, proto);
2066 xiosetenv(namebuff, valuebuff, 1, NULL);
2067 namebuff[strlen(lr)] = '\0'; ++idx;
2068 } while (result > 0);
2069 break;
2070 #endif /* WITH_IP4 */
2071 #if WITH_IP6
2072 case PF_INET6:
2073 strcpy(namebuff, lr);
2074 do {
2075 result =
2076 xiosetsockaddrenv_ip6(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2077 valuebuff, XIOSOCKADDRENVLEN,
2078 &sau->ip6, proto);
2079 xiosetenv(namebuff, valuebuff, 1, NULL);
2080 namebuff[strlen(lr)] = '\0'; ++idx;
2081 } while (result > 0);
2082 break;
2083 #endif /* WITH_IP6 */
2084 #if LATER
2085 case PF_PACKET:
2086 result = xiosetsockaddrenv_packet(lr, (void *)sau, proto); break;
2087 #endif
2088 default:
2089 result = -1;
2090 break;
2092 return result;
2093 # undef XIOSOCKADDRENVLEN
2096 #endif /* _WITH_SOCKET */
2098 /* these do sockets internally */
2100 /* retrieves options so-type and so-prototype from opts, calls socket, and
2101 ev. generates an appropriate error message.
2102 returns 0 on success or -1 if an error occurred. */
2103 int
2104 xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
2105 int result;
2107 retropt_int(opts, OPT_SO_TYPE, &socktype);
2108 retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
2109 result = Socket(pf, socktype, proto);
2110 if (result < 0) {
2111 Msg4(msglevel, "socket(%d, %d, %d): %s",
2112 pf, socktype, proto, strerror(errno));
2113 return -1;
2115 return result;
2118 /* retrieves options so-type and so-prototype from opts, calls socketpair, and
2119 ev. generates an appropriate error message.
2120 returns 0 on success or -1 if an error occurred. */
2121 int
2122 xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]) {
2123 int result;
2125 retropt_int(opts, OPT_SO_TYPE, &socktype);
2126 retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
2127 result = Socketpair(pf, socktype, proto, sv);
2128 if (result < 0) {
2129 Error5("socketpair(%d, %d, %d, %p): %s",
2130 pf, socktype, proto, sv, strerror(errno));
2131 return -1;
2133 return result;