test.sh: Archlinux: No which; changes on ip,ss commands
[socat.git] / xio-socket.c
blobf5d520e2d1b099c04f8b50e7cb70ecd89f77a3e6
1 /* source: xio-socket.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 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_SNDLOWAT
107 const struct optdesc opt_so_sndlowat = { "so-sndlowat", "sndlowat", OPT_SO_SNDLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDLOWAT };
108 #endif
109 /* end of setsockopt options of UNIX98 standard */
111 #ifdef SO_AUDIT /* AIX 4.3.3 */
112 const struct optdesc opt_so_audit = { "so-audit", "audit", OPT_SO_AUDIT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_AUDIT };
113 #endif /* SO_AUDIT */
114 #ifdef SO_ATTACH_FILTER
115 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};
116 #endif
117 #ifdef SO_DETACH_FILTER
118 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};
119 #endif
120 #ifdef SO_BINDTODEVICE /* Linux: man 7 socket */
121 const struct optdesc opt_so_bindtodevice={"so-bindtodevice","if",OPT_SO_BINDTODEVICE,GROUP_SOCKET,PH_PASTSOCKET,TYPE_NAME,OFUNC_SOCKOPT,SOL_SOCKET,SO_BINDTODEVICE};
122 #endif
123 #ifdef SO_BSDCOMPAT
124 const struct optdesc opt_so_bsdcompat= { "so-bsdcompat","bsdcompat",OPT_SO_BSDCOMPAT,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BSDCOMPAT };
125 #endif
126 #ifdef SO_CKSUMRECV
127 const struct optdesc opt_so_cksumrecv= { "so-cksumrecv","cksumrecv",OPT_SO_CKSUMRECV,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_CKSUMRECV };
128 #endif /* SO_CKSUMRECV */
129 #ifdef SO_TIMESTAMP
130 const struct optdesc opt_so_timestamp= { "so-timestamp","timestamp",OPT_SO_TIMESTAMP,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_TIMESTAMP };
131 #endif
132 #ifdef SO_KERNACCEPT /* AIX 4.3.3 */
133 const struct optdesc opt_so_kernaccept={ "so-kernaccept","kernaccept",OPT_SO_KERNACCEPT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KERNACCEPT};
134 #endif /* SO_KERNACCEPT */
135 #ifdef SO_NO_CHECK
136 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 };
137 #endif
138 #ifdef SO_NOREUSEADDR /* AIX 4.3.3 */
139 const struct optdesc opt_so_noreuseaddr={"so-noreuseaddr","noreuseaddr",OPT_SO_NOREUSEADDR,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET, SO_NOREUSEADDR};
140 #endif /* SO_NOREUSEADDR */
141 #ifdef SO_PASSCRED
142 const struct optdesc opt_so_passcred = { "so-passcred", "passcred", OPT_SO_PASSCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PASSCRED};
143 #endif
144 #ifdef SO_PEERCRED
145 const struct optdesc opt_so_peercred = { "so-peercred", "peercred", OPT_SO_PEERCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT3,OFUNC_SOCKOPT, SOL_SOCKET, SO_PEERCRED};
146 #endif
147 #ifdef SO_PRIORITY
148 const struct optdesc opt_so_priority = { "so-priority", "priority", OPT_SO_PRIORITY, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PRIORITY};
149 #endif
150 #ifdef SO_REUSEPORT /* AIX 4.3.3, BSD, HP-UX, Linux >=3.9 */
151 const struct optdesc opt_so_reuseport= { "so-reuseport","reuseport",OPT_SO_REUSEPORT,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEPORT };
152 #endif /* defined(SO_REUSEPORT) */
153 #ifdef SO_SECURITY_AUTHENTICATION
154 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};
155 #endif
156 #ifdef SO_SECURITY_ENCRYPTION_NETWORK
157 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};
158 #endif
159 #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT
160 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};
161 #endif
162 #ifdef SO_USE_IFBUFS
163 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};
164 #endif /* SO_USE_IFBUFS */
165 #ifdef SO_USELOOPBACK /* AIX433, Solaris, HP-UX */
166 const struct optdesc opt_so_useloopback={"so-useloopback","useloopback",OPT_SO_USELOOPBACK,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT, SOL_SOCKET, SO_USELOOPBACK};
167 #endif /* SO_USELOOPBACK */
168 #ifdef SO_DGRAM_ERRIND /* Solaris */
169 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};
170 #endif /* SO_DGRAM_ERRIND */
171 #ifdef SO_DONTLINGER /* Solaris */
172 const struct optdesc opt_so_dontlinger = {"so-dontlinger", "dontlinger", OPT_SO_DONTLINGER, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DONTLINGER };
173 #endif
174 /* the SO_PROTOTYPE is OS defined on Solaris, HP-UX; we lend this for a more
175 general purpose */
176 const struct optdesc opt_so_prototype = {"so-prototype", "prototype", OPT_SO_PROTOTYPE, GROUP_SOCKET,PH_SOCKET, TYPE_INT,OFUNC_SPEC, SOL_SOCKET,SO_PROTOTYPE };
177 #ifdef FIOSETOWN
178 const struct optdesc opt_fiosetown = { "fiosetown", NULL, OPT_FIOSETOWN, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, FIOSETOWN };
179 #endif
180 #ifdef SIOCSPGRP
181 const struct optdesc opt_siocspgrp = { "siocspgrp", NULL, OPT_SIOCSPGRP, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, SIOCSPGRP };
182 #endif
183 const struct optdesc opt_bind = { "bind", NULL, OPT_BIND, GROUP_SOCKET, PH_BIND, TYPE_STRING,OFUNC_SPEC };
184 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) };
185 const struct optdesc opt_protocol_family = { "protocol-family", "pf", OPT_PROTOCOL_FAMILY, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC };
186 const struct optdesc opt_protocol = { "protocol", NULL, OPT_PROTOCOL, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC };
188 /* generic setsockopt() options */
189 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 };
190 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 };
191 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 };
193 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) };
196 #if WITH_GENERICSOCKET
198 static
199 int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts,
200 int xioflags, xiofile_t *xxfd, unsigned groups,
201 int dummy1, int dummy2, int dummy3) {
202 struct single *xfd = &xxfd->stream;
203 const char *pfname = argv[1];
204 const char *protname = argv[2];
205 const char *address = argv[3];
206 char *garbage;
207 int pf;
208 int proto;
209 int socktype = SOCK_STREAM;
210 int needbind = 0;
211 union sockaddr_union them; socklen_t themlen; size_t themsize;
212 union sockaddr_union us; socklen_t uslen = sizeof(us);
213 int result;
215 if (argc != 4) {
216 Error2("%s: wrong number of parameters (%d instead of 3)",
217 argv[0], argc-1);
218 return STAT_NORETRY;
221 pf = strtoul(pfname, &garbage, 0);
222 if (*garbage != '\0') {
223 Warn1("garbage in parameter: \"%s\"", garbage);
226 proto = strtoul(protname, &garbage, 0);
227 if (*garbage != '\0') {
228 Warn1("garbage in parameter: \"%s\"", garbage);
231 retropt_socket_pf(opts, &pf);
232 retropt_int(opts, OPT_SO_TYPE, &socktype);
233 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
234 xfd->howtoend = END_SHUTDOWN;
236 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
237 applyopts(-1, opts, PH_INIT);
238 applyopts(-1, opts, PH_EARLY);
240 themsize = 0;
241 if ((result =
242 dalan(address, (char *)&them.soa.sa_data, &themsize, sizeof(them)))
243 < 0) {
244 Error1("data too long: \"%s\"", address);
245 } else if (result > 0) {
246 Error1("syntax error in \"%s\"", address);
248 them.soa.sa_family = pf;
249 themlen = themsize +
250 #if HAVE_STRUCT_SOCKADDR_SALEN
251 sizeof(them.soa.sa_len) +
252 #endif
253 sizeof(them.soa.sa_family);
255 xfd->dtype = XIOREAD_STREAM|XIOWRITE_STREAM;
257 socket_init(0, &us);
258 if (retropt_bind(opts, 0 /*pf*/, socktype, proto, (struct sockaddr *)&us, &uslen, 3,
259 0, 0)
260 != STAT_NOACTION) {
261 needbind = true;
262 us.soa.sa_family = pf;
265 if ((result =
266 xioopen_connect(xfd,
267 needbind?(struct sockaddr *)&us:NULL, uslen,
268 (struct sockaddr *)&them, themlen,
269 opts, pf, socktype, proto, false)) != 0) {
270 return result;
272 if ((result = _xio_openlate(xfd, opts)) < 0) {
273 return result;
275 return STAT_OK;
278 #if WITH_LISTEN
279 static
280 int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts,
281 int xioflags, xiofile_t *xxfd, unsigned groups,
282 int dummy1, int dummy2, int dummy3) {
283 struct single *xfd = &xxfd->stream;
284 const char *pfname = argv[1];
285 const char *protname = argv[2];
286 const char *usname = argv[3];
287 char *garbage;
288 int pf;
289 int proto;
290 int socktype = SOCK_STREAM;
291 union sockaddr_union us; socklen_t uslen; size_t ussize;
292 struct opt *opts0;
293 int result;
295 if (argc != 4) {
296 Error2("%s: wrong number of parameters (%d instead of 3)",
297 argv[0], argc-1);
298 return STAT_NORETRY;
301 pf = strtoul(pfname, &garbage, 0);
302 if (*garbage != '\0') {
303 Warn1("garbage in parameter: \"%s\"", garbage);
306 proto = strtoul(protname, &garbage, 0);
307 if (*garbage != '\0') {
308 Warn1("garbage in parameter: \"%s\"", garbage);
311 retropt_socket_pf(opts, &pf);
312 retropt_int(opts, OPT_SO_TYPE, &socktype);
313 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
314 xfd->howtoend = END_SHUTDOWN;
316 socket_init(0, &us);
317 ussize = 0;
318 if ((result =
319 dalan(usname, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
320 < 0) {
321 Error1("data too long: \"%s\"", usname);
322 } else if (result > 0) {
323 Error1("syntax error in \"%s\"", usname);
325 uslen = ussize + sizeof(us.soa.sa_family)
326 #if HAVE_STRUCT_SOCKADDR_SALEN
327 + sizeof(us.soa.sa_len)
328 #endif
330 us.soa.sa_family = pf;
332 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
333 applyopts(-1, opts, PH_INIT);
334 applyopts(-1, opts, PH_EARLY);
336 opts0 = copyopts(opts, GROUP_ALL);
338 if ((result =
339 xioopen_listen(xfd, xioflags,
340 (struct sockaddr *)&us, uslen,
341 opts, opts0, 0/*instead of pf*/, socktype, proto))
342 != STAT_OK)
343 return result;
344 return STAT_OK;
346 #endif /* WITH_LISTEN */
348 /* we expect the form: ...:domain:type:protocol:remote-address */
349 static
350 int xioopen_socket_sendto(int argc, const char *argv[], struct opt *opts,
351 int xioflags, xiofile_t *xxfd, unsigned groups,
352 int dummy1, int dummy2, int dummy3) {
353 int result;
355 if (argc != 5) {
356 Error2("%s: wrong number of parameters (%d instead of 4)",
357 argv[0], argc-1);
358 return STAT_NORETRY;
360 if ((result =
361 _xioopen_socket_sendto(argv[1], argv[2], argv[3], argv[4],
362 opts, xioflags, xxfd, groups))
363 != STAT_OK) {
364 return result;
366 _xio_openlate(&xxfd->stream, opts);
367 return STAT_OK;
370 static
371 int _xioopen_socket_sendto(const char *pfname, const char *type,
372 const char *protname, const char *address,
373 struct opt *opts, int xioflags, xiofile_t *xxfd,
374 unsigned groups) {
375 xiosingle_t *xfd = &xxfd->stream;
376 char *garbage;
377 union sockaddr_union us = {{0}};
378 socklen_t uslen = 0; size_t ussize;
379 size_t themsize;
380 int pf;
381 int socktype = SOCK_RAW;
382 int proto;
383 bool needbind = false;
384 char *bindstring = NULL;
385 int result;
387 pf = strtoul(pfname, &garbage, 0);
388 if (*garbage != '\0') {
389 Warn1("garbage in parameter: \"%s\"", garbage);
392 socktype = strtoul(type, &garbage, 0);
393 if (*garbage != '\0') {
394 Warn1("garbage in parameter: \"%s\"", garbage);
397 proto = strtoul(protname, &garbage, 0);
398 if (*garbage != '\0') {
399 Warn1("garbage in parameter: \"%s\"", garbage);
402 retropt_socket_pf(opts, &pf);
403 retropt_int(opts, OPT_SO_TYPE, &socktype);
404 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
405 xfd->howtoend = END_SHUTDOWN;
407 xfd->peersa.soa.sa_family = pf;
408 themsize = 0;
409 if ((result =
410 dalan(address, (char *)&xfd->peersa.soa.sa_data, &themsize,
411 sizeof(xfd->peersa)))
412 < 0) {
413 Error1("data too long: \"%s\"", address);
414 } else if (result > 0) {
415 Error1("syntax error in \"%s\"", address);
417 xfd->salen = themsize + sizeof(sa_family_t)
418 #if HAVE_STRUCT_SOCKADDR_SALEN
419 + sizeof(xfd->peersa.soa.sa_len)
420 #endif
422 #if HAVE_STRUCT_SOCKADDR_SALEN
423 xfd->peersa.soa.sa_len =
424 sizeof(xfd->peersa.soa.sa_len) + sizeof(xfd->peersa.soa.sa_family) +
425 themsize;
426 #endif
428 /* ...res_opts[] */
429 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
430 applyopts(-1, opts, PH_INIT);
432 if (pf == PF_UNSPEC) {
433 pf = xfd->peersa.soa.sa_family;
436 xfd->dtype = XIODATA_RECVFROM;
438 if (retropt_string(opts, OPT_BIND, &bindstring) == 0) {
439 ussize = 0;
440 if ((result =
441 dalan(bindstring, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
442 < 0) {
443 Error1("data too long: \"%s\"", bindstring);
444 } else if (result > 0) {
445 Error1("syntax error in \"%s\"", bindstring);
447 us.soa.sa_family = pf;
448 uslen = ussize + sizeof(sa_family_t)
449 #if HAVE_STRUCT_SOCKADDR_SALEN
450 + sizeof(us.soa.sa_len)
451 #endif
453 needbind = true;
456 return
457 _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
458 opts, xioflags, xfd, groups, pf, socktype, proto);
462 /* we expect the form: ...:domain:socktype:protocol:local-address */
463 static
464 int xioopen_socket_recvfrom(int argc, const char *argv[], struct opt *opts,
465 int xioflags, xiofile_t *xxfd, unsigned groups,
466 int dummy, int summy2, int dummy3) {
467 struct single *xfd = &xxfd->stream;
468 const char *pfname = argv[1];
469 const char *typename = argv[2];
470 const char *protname = argv[3];
471 const char *address = argv[4];
472 char *garbage;
473 union sockaddr_union *us = &xfd->para.socket.la;
474 socklen_t uslen; size_t ussize;
475 int pf, socktype, proto;
476 char *rangename;
477 int result;
479 if (argc != 5) {
480 Error2("%s: wrong number of parameters (%d instead of 4)",
481 argv[0], argc-1);
482 return STAT_NORETRY;
485 pf = strtoul(pfname, &garbage, 0);
486 if (*garbage != '\0') {
487 Warn1("garbage in parameter: \"%s\"", garbage);
490 socktype = strtoul(typename, &garbage, 0);
491 if (*garbage != '\0') {
492 Warn1("garbage in parameter: \"%s\"", garbage);
495 proto = strtoul(protname, &garbage, 0);
496 if (*garbage != '\0') {
497 Warn1("garbage in parameter: \"%s\"", garbage);
500 retropt_socket_pf(opts, &pf);
501 retropt_int(opts, OPT_SO_TYPE, &socktype);
502 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
503 xfd->howtoend = END_NONE;
505 ussize = 0;
506 if ((result =
507 dalan(address, (char *)&us->soa.sa_data, &ussize, sizeof(*us)))
508 < 0) {
509 Error1("data too long: \"%s\"", address);
510 } else if (result > 0) {
511 Error1("syntax error in \"%s\"", address);
513 us->soa.sa_family = pf;
514 uslen = ussize + sizeof(us->soa.sa_family)
515 #if HAVE_STRUCT_SOCKADDR_SALEN
516 + sizeof(us->soa.sa_len);
517 #endif
519 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
521 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
522 if (xioparserange(rangename, 0, &xfd->para.socket.range) < 0) {
523 return STAT_NORETRY;
525 xfd->para.socket.dorange = true;
526 free(rangename);
529 if ((result =
530 _xioopen_dgram_recvfrom(xfd, xioflags, &us->soa, uslen,
531 opts, pf, socktype, proto, E_ERROR))
532 != STAT_OK) {
533 return result;
535 _xio_openlate(xfd, opts);
536 return STAT_OK;
539 /* we expect the form: ...:domain:type:protocol:local-address */
540 static
541 int xioopen_socket_recv(int argc, const char *argv[], struct opt *opts,
542 int xioflags, xiofile_t *xxfd, unsigned groups,
543 int dummy1, int dummy2, int dummy3) {
544 struct single *xfd = &xxfd->stream;
545 const char *pfname = argv[1];
546 const char *typename = argv[2];
547 const char *protname = argv[3];
548 const char *address = argv[4];
549 char *garbage;
550 union sockaddr_union us;
551 socklen_t uslen; size_t ussize;
552 int pf, socktype, proto;
553 char *rangename;
554 int result;
556 if (argc != 5) {
557 Error2("%s: wrong number of parameters (%d instead of 4)",
558 argv[0], argc-1);
559 return STAT_NORETRY;
562 pf = strtoul(pfname, &garbage, 0);
563 if (*garbage != '\0') {
564 Warn1("garbage in parameter: \"%s\"", garbage);
567 socktype = strtoul(typename, &garbage, 0);
568 if (*garbage != '\0') {
569 Warn1("garbage in parameter: \"%s\"", garbage);
572 proto = strtoul(protname, &garbage, 0);
573 if (*garbage != '\0') {
574 Warn1("garbage in parameter: \"%s\"", garbage);
577 retropt_socket_pf(opts, &pf);
578 retropt_int(opts, OPT_SO_TYPE, &socktype);
579 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
580 xfd->howtoend = END_NONE;
582 ussize = 0;
583 if ((result =
584 dalan(address, (char *)&us.soa.sa_data, &ussize, sizeof(us)))
585 < 0) {
586 Error1("data too long: \"%s\"", address);
587 } else if (result > 0) {
588 Error1("syntax error in \"%s\"", address);
590 us.soa.sa_family = pf;
591 uslen = ussize + sizeof(sa_family_t)
592 #if HAVE_STRUCT_SOCKADDR_SALEN
593 +sizeof(us.soa.sa_len)
594 #endif
596 xfd->dtype = XIOREAD_RECV;
597 xfd->para.socket.la.soa.sa_family = pf;
599 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
600 if (xioparserange(rangename, 0, &xfd->para.socket.range) < 0) {
601 return STAT_NORETRY;
603 xfd->para.socket.dorange = true;
604 free(rangename);
607 if ((result =
608 _xioopen_dgram_recv(xfd, xioflags, &us.soa,
609 uslen, opts, pf, socktype, proto, E_ERROR))
610 != STAT_OK) {
611 return result;
613 _xio_openlate(xfd, opts);
614 return STAT_OK;
618 /* we expect the form: ...:domain:type:protocol:remote-address */
619 static
620 int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts,
621 int xioflags, xiofile_t *xxfd, unsigned groups,
622 int dummy1, int dummy2, int dummy3) {
623 xiosingle_t *xfd = &xxfd->stream;
624 const char *pfname = argv[1];
625 const char *typename = argv[2];
626 const char *protname = argv[3];
627 const char *address = argv[4];
628 char *garbage;
629 char *rangename;
630 size_t themsize;
631 int pf;
632 int result;
634 if (argc != 5) {
635 Error2("%s: wrong number of parameters (%d instead of 4)",
636 argv[0], argc-1);
637 return STAT_NORETRY;
640 pf = strtoul(pfname, &garbage, 0);
641 if (*garbage != '\0') {
642 Warn1("garbage in parameter: \"%s\"", garbage);
645 retropt_socket_pf(opts, &pf);
646 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
647 xfd->howtoend = END_SHUTDOWN;
649 xfd->peersa.soa.sa_family = pf;
650 themsize = 0;
651 if ((result =
652 dalan(address, (char *)&xfd->peersa.soa.sa_data, &themsize,
653 sizeof(xfd->peersa)))
654 < 0) {
655 Error1("data too long: \"%s\"", address);
656 } else if (result > 0) {
657 Error1("syntax error in \"%s\"", address);
659 xfd->salen = themsize + sizeof(sa_family_t);
660 #if HAVE_STRUCT_SOCKADDR_SALEN
661 xfd->peersa.soa.sa_len =
662 sizeof(xfd->peersa.soa.sa_len) + sizeof(xfd->peersa.soa.sa_family) +
663 themsize;
664 #endif
666 if ((result =
667 _xioopen_socket_sendto(pfname, typename, protname, address,
668 opts, xioflags, xxfd, groups))
669 != STAT_OK) {
670 return result;
673 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
675 xfd->para.socket.la.soa.sa_family = xfd->peersa.soa.sa_family;
677 /* which reply sockets will accept - determine by range option */
678 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
679 if (xioparserange(rangename, 0, &xfd->para.socket.range) < 0) {
680 free(rangename);
681 return STAT_NORETRY;
683 xfd->para.socket.dorange = true;
684 xfd->dtype |= XIOREAD_RECV_CHECKRANGE;
685 free(rangename);
688 _xio_openlate(xfd, opts);
689 return STAT_OK;
692 #endif /* WITH_GENERICSOCKET */
695 /* a subroutine that is common to all socket addresses that want to connect
696 to a peer address.
697 might fork.
698 applies and consumes the following options:
699 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
700 PH_CONNECTED, PH_LATE,
701 OFUNC_OFFSET,
702 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
703 returns 0 on success.
705 int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen,
706 struct sockaddr *them, size_t themlen,
707 struct opt *opts, int pf, int socktype, int protocol,
708 bool alt, int level) {
709 int fcntl_flags = 0;
710 char infobuff[256];
711 union sockaddr_union la;
712 socklen_t lalen = themlen;
713 int _errno;
714 int result;
716 if ((xfd->fd = xiosocket(opts, pf, socktype, protocol, level)) < 0) {
717 return STAT_RETRYLATER;
720 applyopts_offset(xfd, opts);
721 applyopts(xfd->fd, opts, PH_PASTSOCKET);
722 applyopts(xfd->fd, opts, PH_FD);
724 applyopts_cloexec(xfd->fd, opts);
726 applyopts(xfd->fd, opts, PH_PREBIND);
727 applyopts(xfd->fd, opts, PH_BIND);
728 #if WITH_TCP || WITH_UDP
729 if (alt) {
730 union sockaddr_union sin, *sinp;
731 unsigned short *port, i, N;
732 div_t dv;
734 /* prepare sockaddr for bind probing */
735 if (us) {
736 sinp = (union sockaddr_union *)us;
737 } else {
738 if (them->sa_family == AF_INET) {
739 socket_in_init(&sin.ip4);
740 #if WITH_IP6
741 } else {
742 socket_in6_init(&sin.ip6);
743 #endif
745 sinp = &sin;
747 if (them->sa_family == AF_INET) {
748 port = &sin.ip4.sin_port;
749 #if WITH_IP6
750 } else if (them->sa_family == AF_INET6) {
751 port = &sin.ip6.sin6_port;
752 #endif
753 } else {
754 port = 0; /* just to make compiler happy */
756 /* combine random+step variant to quickly find a free port when only
757 few are in use, and certainly find a free port in defined time even
758 if there are almost all in use */
759 /* dirt 1: having tcp/udp code in socket function */
760 /* dirt 2: using a time related system call for init of random */
762 /* generate a random port, with millisecond random init */
763 #if 0
764 struct timeb tb;
765 ftime(&tb);
766 srandom(tb.time*1000+tb.millitm);
767 #else
768 struct timeval tv;
769 struct timezone tz;
770 tz.tz_minuteswest = 0;
771 tz.tz_dsttime = 0;
772 if ((result = Gettimeofday(&tv, &tz)) < 0) {
773 Warn2("gettimeofday(%p, {0,0}): %s", &tv, strerror(errno));
775 srandom(tv.tv_sec*1000000+tv.tv_usec);
776 #endif
778 dv = div(random(), IPPORT_RESERVED-XIO_IPPORT_LOWER);
779 i = N = XIO_IPPORT_LOWER + dv.rem;
780 do { /* loop over lowport bind() attempts */
781 *port = htons(i);
782 if (Bind(xfd->fd, (struct sockaddr *)sinp, sizeof(*sinp)) < 0) {
783 Msg4(errno==EADDRINUSE?E_INFO:level,
784 "bind(%d, {%s}, "F_Zd"): %s", xfd->fd,
785 sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)),
786 sizeof(*sinp), strerror(errno));
787 if (errno != EADDRINUSE) {
788 Close(xfd->fd);
789 return STAT_RETRYLATER;
791 } else {
792 break; /* could bind to port, good, continue past loop */
794 --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1;
795 if (i == N) {
796 Msg(level, "no low port available");
797 /*errno = EADDRINUSE; still assigned */
798 Close(xfd->fd);
799 return STAT_RETRYLATER;
801 } while (i != N);
802 } else
803 #endif /* WITH_TCP || WITH_UDP */
805 if (us) {
806 if (Bind(xfd->fd, us, uslen) < 0) {
807 Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
808 xfd->fd, sockaddr_info(us, uslen, infobuff, sizeof(infobuff)),
809 uslen, strerror(errno));
810 Close(xfd->fd);
811 return STAT_RETRYLATER;
815 applyopts(xfd->fd, opts, PH_PASTBIND);
817 applyopts(xfd->fd, opts, PH_CONNECT);
819 if (xfd->para.socket.connect_timeout.tv_sec != 0 ||
820 xfd->para.socket.connect_timeout.tv_usec != 0) {
821 fcntl_flags = Fcntl(xfd->fd, F_GETFL);
822 Fcntl_l(xfd->fd, F_SETFL, fcntl_flags|O_NONBLOCK);
825 result = Connect(xfd->fd, (struct sockaddr *)them, themlen);
826 _errno = errno;
827 la.soa.sa_family = them->sa_family; lalen = sizeof(la);
828 if (Getsockname(xfd->fd, &la.soa, &lalen) < 0) {
829 Msg4(level-1, "getsockname(%d, %p, {%d}): %s",
830 xfd->fd, &la.soa, lalen, strerror(errno));
832 errno = _errno;
833 if (result < 0) {
834 if (errno == EINPROGRESS) {
835 if (xfd->para.socket.connect_timeout.tv_sec != 0 ||
836 xfd->para.socket.connect_timeout.tv_usec != 0) {
837 struct timeval timeout;
838 struct pollfd writefd;
839 int result;
841 Info4("connect(%d, %s, "F_Zd"): %s",
842 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
843 themlen, strerror(errno));
844 timeout = xfd->para.socket.connect_timeout;
845 writefd.fd = xfd->fd;
846 writefd.events = (POLLOUT|POLLERR);
847 result = xiopoll(&writefd, 1, &timeout);
848 if (result < 0) {
849 Msg4(level, "xiopoll({%d,POLLOUT|POLLERR},,{"F_tv_sec"."F_tv_usec"): %s",
850 xfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno));
851 return STAT_RETRYLATER;
853 if (result == 0) {
854 Msg2(level, "connecting to %s: %s",
855 sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
856 strerror(ETIMEDOUT));
857 return STAT_RETRYLATER;
859 if (writefd.revents & POLLERR) {
860 #if 0
861 unsigned char dummy[1];
862 Read(xfd->fd, &dummy, 1); /* get error message */
863 Msg2(level, "connecting to %s: %s",
864 sockaddr_info(them, infobuff, sizeof(infobuff)),
865 strerror(errno));
866 #else
867 Connect(xfd->fd, them, themlen); /* get error message */
868 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
869 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
870 themlen, strerror(errno));
871 #endif
872 return STAT_RETRYLATER;
874 /* otherwise OK */
875 Fcntl_l(xfd->fd, F_SETFL, fcntl_flags);
876 } else {
877 Warn4("connect(%d, %s, "F_Zd"): %s",
878 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
879 themlen, strerror(errno));
881 } else if (pf == PF_UNIX && errno == EPROTOTYPE) {
882 /* this is for UNIX domain sockets: a connect attempt seems to be
883 the only way to distinguish stream and datagram sockets */
884 int _errno = errno;
885 Info4("connect(%d, %s, "F_Zd"): %s",
886 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
887 themlen, strerror(errno));
888 #if 0
889 Info("assuming datagram socket");
890 xfd->dtype = DATA_RECVFROM;
891 xfd->salen = themlen;
892 memcpy(&xfd->peersa.soa, them, xfd->salen);
893 #endif
894 /*!!! and remove bind socket */
895 Close(xfd->fd); xfd->fd = -1;
896 errno = _errno;
897 return -1;
898 } else {
899 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
900 xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
901 themlen, strerror(errno));
902 Close(xfd->fd);
903 return STAT_RETRYLATER;
905 } else { /* result >= 0 */
906 Notice1("successfully connected from local address %s",
907 sockaddr_info(&la.soa, themlen, infobuff, sizeof(infobuff)));
910 applyopts_fchown(xfd->fd, opts); /* OPT_USER, OPT_GROUP */
911 applyopts(xfd->fd, opts, PH_CONNECTED);
912 applyopts(xfd->fd, opts, PH_LATE);
914 return STAT_OK;
918 /* a subroutine that is common to all socket addresses that want to connect
919 to a peer address.
920 might fork.
921 applies and consumes the following option:
922 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
923 PH_CONNECTED, PH_LATE,
924 OFUNC_OFFSET,
925 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
926 returns 0 on success.
928 int xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen,
929 struct sockaddr *them, size_t themlen,
930 struct opt *opts, int pf, int socktype, int protocol,
931 bool alt) {
932 bool dofork = false;
933 struct opt *opts0;
934 char infobuff[256];
935 int level;
936 int result;
938 retropt_bool(opts, OPT_FORK, &dofork);
940 opts0 = copyopts(opts, GROUP_ALL);
942 Notice1("opening connection to %s",
943 sockaddr_info(them, themlen, infobuff, sizeof(infobuff)));
945 do { /* loop over retries and forks */
947 #if WITH_RETRY
948 if (xfd->forever || xfd->retry) {
949 level = E_INFO;
950 } else
951 #endif /* WITH_RETRY */
952 level = E_ERROR;
953 result =
954 _xioopen_connect(xfd, us, uslen, them, themlen, opts,
955 pf, socktype, protocol, alt, level);
956 switch (result) {
957 case STAT_OK: break;
958 #if WITH_RETRY
959 case STAT_RETRYLATER:
960 if (xfd->forever || xfd->retry) {
961 --xfd->retry;
962 if (result == STAT_RETRYLATER) {
963 Nanosleep(&xfd->intervall, NULL);
965 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
966 continue;
968 return STAT_NORETRY;
969 #endif /* WITH_RETRY */
970 default:
971 return result;
974 if (dofork) {
975 xiosetchilddied(); /* set SIGCHLD handler */
978 #if WITH_RETRY
979 if (dofork) {
980 pid_t pid;
981 int level = E_ERROR;
982 if (xfd->forever || xfd->retry) {
983 level = E_WARN; /* most users won't expect a problem here,
984 so Notice is too weak */
987 while ((pid = xio_fork(false, level)) < 0) {
988 --xfd->retry;
989 if (xfd->forever || xfd->retry) {
990 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
991 Nanosleep(&xfd->intervall, NULL); continue;
993 return STAT_RETRYLATER;
996 if (pid == 0) { /* child process */
997 break;
1000 /* parent process */
1001 Close(xfd->fd);
1002 /* with and without retry */
1003 Nanosleep(&xfd->intervall, NULL);
1004 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
1005 continue; /* with next socket() bind() connect() */
1006 } else
1007 #endif /* WITH_RETRY */
1009 break;
1011 #if 0
1012 if ((result = _xio_openlate(fd, opts)) < 0)
1013 return result;
1014 #endif
1015 } while (true);
1017 return 0;
1021 /* common to xioopen_udp_sendto, ..unix_sendto, ..rawip
1022 applies and consumes the following option:
1023 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
1024 OFUNC_OFFSET
1025 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
1027 int _xioopen_dgram_sendto(/* them is already in xfd->peersa */
1028 union sockaddr_union *us, socklen_t uslen,
1029 struct opt *opts,
1030 int xioflags, xiosingle_t *xfd, unsigned groups,
1031 int pf, int socktype, int ipproto) {
1032 int level = E_ERROR;
1033 union sockaddr_union la; socklen_t lalen = sizeof(la);
1034 char infobuff[256];
1036 if ((xfd->fd = xiosocket(opts, pf, socktype, ipproto, level)) < 0) {
1037 return STAT_RETRYLATER;
1040 applyopts_offset(xfd, opts);
1041 applyopts_single(xfd, opts, PH_PASTSOCKET);
1042 applyopts(xfd->fd, opts, PH_PASTSOCKET);
1043 applyopts(xfd->fd, opts, PH_FD);
1045 applyopts_cloexec(xfd->fd, opts);
1047 applyopts(xfd->fd, opts, PH_PREBIND);
1048 applyopts(xfd->fd, opts, PH_BIND);
1050 if (us) {
1051 if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
1052 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s",
1053 xfd->fd, sockaddr_info((struct sockaddr *)us, uslen, infobuff, sizeof(infobuff)),
1054 uslen, strerror(errno));
1055 Close(xfd->fd);
1056 return STAT_RETRYLATER;
1060 applyopts(xfd->fd, opts, PH_PASTBIND);
1062 /*applyopts(xfd->fd, opts, PH_CONNECT);*/
1064 if (Getsockname(xfd->fd, &la.soa, &lalen) < 0) {
1065 Warn4("getsockname(%d, %p, {%d}): %s",
1066 xfd->fd, &la.soa, lalen, strerror(errno));
1069 applyopts_fchown(xfd->fd, opts);
1070 applyopts(xfd->fd, opts, PH_CONNECTED);
1071 applyopts(xfd->fd, opts, PH_LATE);
1073 /* xfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */
1074 Notice1("successfully prepared local socket %s",
1075 sockaddr_info(&la.soa, lalen, infobuff, sizeof(infobuff)));
1077 return STAT_OK;
1081 /* when the recvfrom address (with option fork) receives a packet it keeps this
1082 packet in the IP stacks input queue and forks a sub process. The sub process
1083 then reads this packet for processing its data.
1084 There is a problem because the parent process would find the same packet
1085 again if it calls select()/poll() before the child process reads the
1086 packet.
1087 To solve this problem we implement the following mechanism:
1088 The sub process sends a SIGUSR1 when it has read the packet (or a SIGCHLD if
1089 it dies before). The parent process waits until it receives that signal and
1090 only then continues to listen.
1091 To prevent a signal from another process to trigger our loop, we pass the
1092 pid of the sub process to the signal handler in xio_waitingfor. The signal
1093 handler sets xio_hashappened if the pid matched.
1095 static pid_t xio_waitingfor; /* info from recv loop to signal handler:
1096 indicates the pid of the child process
1097 that should send us the USR1 signal */
1098 static bool xio_hashappened; /* info from signal handler to loop: child
1099 process has read ("consumed") the packet */
1100 /* this is the signal handler for USR1 and CHLD */
1101 void xiosigaction_hasread(int signum
1102 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1103 , siginfo_t *siginfo, void *ucontext
1104 #endif
1106 pid_t pid;
1107 int _errno;
1108 int status = 0;
1109 bool wassig = false;
1111 _errno = errno;
1112 diag_in_handler = 1;
1113 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1114 Debug5("xiosigaction_hasread(%d, {%d,%d,%d,"F_pid"}, )",
1115 signum, siginfo->si_signo, siginfo->si_errno, siginfo->si_code,
1116 siginfo->si_pid);
1117 #else
1118 Debug1("xiosigaction_hasread(%d)", signum);
1119 #endif
1120 if (signum == SIGCHLD) {
1121 do {
1122 pid = Waitpid(-1, &status, WNOHANG);
1123 if (pid == 0) {
1124 Msg(wassig?E_INFO:E_WARN,
1125 "waitpid(-1, {}, WNOHANG): no child has exited");
1126 Info("xiosigaction_hasread() finished");
1127 Debug("xiosigaction_hasread() ->");
1128 diag_in_handler = 0;
1129 errno = _errno;
1130 return;
1131 } else if (pid < 0 && errno == ECHILD) {
1132 Msg(wassig?E_INFO:E_WARN,
1133 "waitpid(-1, {}, WNOHANG): "F_strerror);
1134 Info("xiosigaction_hasread() finished");
1135 Debug("xiosigaction_hasread() ->");
1136 diag_in_handler = 0;
1137 errno = _errno;
1138 return;
1140 wassig = true;
1141 if (pid < 0) {
1142 Warn1("waitpid(-1, {%d}, WNOHANG): "F_strerror, status);
1143 Info("xiosigaction_hasread() finished");
1144 Debug("xiosigaction_hasread() ->");
1145 diag_in_handler = 0;
1146 errno = _errno;
1147 return;
1149 if (pid == xio_waitingfor) {
1150 xio_hashappened = true;
1151 Debug("xiosigaction_hasread() ->");
1152 diag_in_handler = 0;
1153 errno = _errno;
1154 return;
1156 } while (1);
1158 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1159 if (xio_waitingfor == siginfo->si_pid) {
1160 xio_hashappened = true;
1162 #else
1163 xio_hashappened = true;
1164 #endif
1165 #if !HAVE_SIGACTION
1166 Signal(sig, xiosigaction_hasread);
1167 #endif /* !HAVE_SIGACTION */
1168 Debug("xiosigaction_hasread() ->");
1169 diag_in_handler = 0;
1170 errno = _errno;
1171 return;
1175 /* waits for incoming packet, checks its source address and port. Depending
1176 on fork option, it may fork a subprocess.
1177 Returns STAT_OK if a the packet was accepted; with fork option, this is already in
1178 a new subprocess!
1179 Other return values indicate a problem; this can happen in the master
1180 process or in a subprocess.
1181 This function does not retry. If you need retries, handle this is a
1182 loop in the calling function.
1183 after fork, we set the forever/retry of the child process to 0
1184 applies and consumes the following options:
1185 PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD,
1186 PH_CONNECTED, PH_LATE, PH_LATE2
1187 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap
1189 int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
1190 struct sockaddr *us, socklen_t uslen,
1191 struct opt *opts,
1192 int pf, int socktype, int proto, int level) {
1193 char *rangename;
1194 bool dofork = false;
1195 pid_t pid; /* mostly int; only used with fork */
1196 char infobuff[256];
1197 char lisname[256];
1198 bool drop = false; /* true if current packet must be dropped */
1199 int result;
1201 retropt_bool(opts, OPT_FORK, &dofork);
1203 if (dofork) {
1204 if (!(xioflags & XIO_MAYFORK)) {
1205 Error("option fork not allowed here");
1206 return STAT_NORETRY;
1208 xfd->flags |= XIO_DOESFORK;
1211 if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1213 if ((xfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) {
1214 return STAT_RETRYLATER;
1217 applyopts_single(xfd, opts, PH_PASTSOCKET);
1218 applyopts(xfd->fd, opts, PH_PASTSOCKET);
1220 applyopts_cloexec(xfd->fd, opts);
1222 applyopts(xfd->fd, opts, PH_PREBIND);
1223 applyopts(xfd->fd, opts, PH_BIND);
1224 if ((us != NULL) && Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
1225 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
1226 sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
1227 strerror(errno));
1228 Close(xfd->fd);
1229 return STAT_RETRYLATER;
1232 #if WITH_UNIX
1233 if (pf == AF_UNIX && us != NULL) {
1234 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
1236 #endif
1238 applyopts(xfd->fd, opts, PH_PASTBIND);
1239 #if WITH_UNIX
1240 if (pf == AF_UNIX && us != NULL) {
1241 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
1242 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
1243 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
1245 #endif /* WITH_UNIX */
1247 /* for generic sockets, this has already been retrieved */
1248 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
1249 if (xioparserange(rangename, pf, &xfd->para.socket.range)
1250 < 0) {
1251 free(rangename);
1252 return STAT_NORETRY;
1254 free(rangename);
1255 xfd->para.socket.dorange = true;
1258 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1259 xio_retropt_tcpwrap(xfd, opts);
1260 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1262 if (xioopts.logopt == 'm') {
1263 Info("starting recvfrom loop, switching to syslog");
1264 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
1265 } else {
1266 Info("starting recvfrom loop");
1269 if (dofork) {
1270 #if HAVE_SIGACTION
1272 struct sigaction act;
1273 memset(&act, 0, sizeof(struct sigaction));
1274 act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/
1275 #ifdef SA_SIGINFO /* not on Linux 2.0(.33) */
1276 |SA_SIGINFO
1277 #endif
1278 #ifdef SA_NOMASK
1279 |SA_NOMASK
1280 #endif
1282 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
1283 act.sa_sigaction = xiosigaction_hasread;
1284 #else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */
1285 act.sa_handler = xiosigaction_hasread;
1286 #endif
1287 sigfillset(&act.sa_mask);
1288 if (Sigaction(SIGUSR1, &act, NULL) < 0) {
1289 /*! Linux man does not explicitely say that errno is defined */
1290 Warn1("sigaction(SIGUSR1, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno));
1292 if (Sigaction(SIGCHLD, &act, NULL) < 0) {
1293 /*! Linux man does not explicitely say that errno is defined */
1294 Warn1("sigaction(SIGCHLD, {&xiosigaction_subaddr_ok}, NULL): %s", strerror(errno));
1297 #else /* !HAVE_SIGACTION */
1298 /*!!!*/
1299 if (Signal(SIGUSR1, xiosigaction_hasread) == SIG_ERR) {
1300 Warn1("signal(SIGUSR1, xiosigaction_hasread): %s", strerror(errno));
1302 if (Signal(SIGCHLD, xiosigaction_hasread) == SIG_ERR) {
1303 Warn1("signal(SIGCHLD, xiosigaction_hasread): %s", strerror(errno));
1305 #endif /* !HAVE_SIGACTION */
1308 while (true) { /* but we only loop if fork option is set */
1309 char peername[256];
1310 union sockaddr_union _peername;
1311 union sockaddr_union _sockname;
1312 union sockaddr_union *pa = &_peername; /* peer address */
1313 union sockaddr_union *la = &_sockname; /* local address */
1314 socklen_t palen = sizeof(_peername); /* peer address size */
1315 char ctrlbuff[1024]; /* ancillary messages */
1316 struct msghdr msgh = {0};
1318 socket_init(pf, pa);
1320 if (drop) {
1321 char *dummy[2];
1323 Recv(xfd->fd, dummy, sizeof(dummy), 0);
1324 drop = true;
1327 /* loop until select()/poll() returns valid */
1328 do {
1329 struct pollfd readfd;
1330 /*? int level = E_ERROR;*/
1331 if (us != NULL) {
1332 Notice1("receiving on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
1333 } else {
1334 Notice1("receiving IP protocol %u", proto);
1336 readfd.fd = xfd->fd;
1337 readfd.events = POLLIN;
1338 if (xiopoll(&readfd, 1, NULL) > 0) {
1339 break;
1342 if (errno == EINTR) {
1343 continue;
1346 Msg2(level, "poll({%d,,},,-1): %s", xfd->fd, strerror(errno));
1347 Close(xfd->fd);
1348 return STAT_RETRYLATER;
1349 } while (true);
1351 msgh.msg_name = pa;
1352 msgh.msg_namelen = palen;
1353 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
1354 msgh.msg_control = ctrlbuff;
1355 #endif
1356 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
1357 msgh.msg_controllen = sizeof(ctrlbuff);
1358 #endif
1359 if (xiogetpacketsrc(xfd->fd, &msgh) < 0) {
1360 return STAT_RETRYLATER;
1362 palen = msgh.msg_namelen;
1364 Notice1("receiving packet from %s"/*"src"*/,
1365 sockaddr_info((struct sockaddr *)pa, palen, peername, sizeof(peername))/*,
1366 sockaddr_info(&la->soa, sockname, sizeof(sockname))*/);
1368 xiodopacketinfo(&msgh, true, true);
1370 if (xiocheckpeer(xfd, pa, la) < 0) {
1371 /* drop packet */
1372 char buff[512];
1373 Recv(xfd->fd, buff, sizeof(buff), 0);
1374 continue;
1376 Info1("permitting packet from %s",
1377 sockaddr_info((struct sockaddr *)pa, palen,
1378 infobuff, sizeof(infobuff)));
1380 /* set the env vars describing the local and remote sockets */
1381 /*xiosetsockaddrenv("SOCK", la, lalen, proto);*/
1382 xiosetsockaddrenv("PEER", pa, palen, proto);
1384 applyopts(xfd->fd, opts, PH_FD);
1386 applyopts(xfd->fd, opts, PH_CONNECTED);
1388 xfd->peersa = *(union sockaddr_union *)pa;
1389 xfd->salen = palen;
1391 if (dofork) {
1392 sigset_t mask_sigchldusr1;
1394 /* we must prevent that the current packet triggers another fork;
1395 therefore we wait for a signal from the recent child: USR1
1396 indicates that is has consumed the last packet; CHLD means it has
1397 terminated */
1398 /* block SIGCHLD and SIGUSR1 until parent is ready to react */
1399 sigemptyset(&mask_sigchldusr1);
1400 sigaddset(&mask_sigchldusr1, SIGCHLD);
1401 sigaddset(&mask_sigchldusr1, SIGUSR1);
1402 Sigprocmask(SIG_BLOCK, &mask_sigchldusr1, NULL);
1404 if ((pid = xio_fork(false, level)) < 0) {
1405 Close(xfd->fd);
1406 Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
1407 return STAT_RETRYLATER;
1410 if (pid == 0) { /* child */
1411 /* no reason to block SIGCHLD in child process */
1412 Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
1413 xfd->ppid = Getppid(); /* send parent a signal when packet has
1414 been consumed */
1416 #if WITH_RETRY
1417 /* !? */
1418 xfd->retry = 0;
1419 xfd->forever = 0;
1420 level = E_ERROR;
1421 #endif /* WITH_RETRY */
1423 #if WITH_UNIX
1424 /* with UNIX sockets: only listening parent is allowed to remove
1425 the socket file */
1426 xfd->opt_unlink_close = false;
1427 #endif /* WITH_UNIX */
1429 break;
1432 /* server: continue loop with listen */
1433 xio_waitingfor = pid;
1434 /* now we are ready to handle signals */
1435 Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
1437 while (!xio_hashappened) {
1438 Sleep(UINT_MAX); /* any signal lets us continue */
1440 xio_waitingfor = 0; /* so this child will not set hashappened again */
1441 xio_hashappened = false;
1443 Info("continue listening");
1444 } else {
1445 break;
1448 if ((result = _xio_openlate(xfd, opts)) != 0)
1449 return STAT_NORETRY;
1451 return STAT_OK;
1455 /* returns STAT_* */
1456 int _xioopen_dgram_recv(struct single *xfd, int xioflags,
1457 struct sockaddr *us, socklen_t uslen,
1458 struct opt *opts, int pf, int socktype, int proto,
1459 int level) {
1460 char *rangename;
1461 char infobuff[256];
1463 if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1465 if ((xfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) {
1466 return STAT_RETRYLATER;
1469 applyopts_single(xfd, opts, PH_PASTSOCKET);
1470 applyopts(xfd->fd, opts, PH_PASTSOCKET);
1472 applyopts_cloexec(xfd->fd, opts);
1474 applyopts(xfd->fd, opts, PH_PREBIND);
1475 applyopts(xfd->fd, opts, PH_BIND);
1476 if ((us != NULL) && Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
1477 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
1478 sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
1479 strerror(errno));
1480 Close(xfd->fd);
1481 return STAT_RETRYLATER;
1484 #if WITH_UNIX
1485 if (pf == AF_UNIX && us != NULL) {
1486 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
1488 #endif
1490 applyopts_single(xfd, opts, PH_PASTBIND);
1491 applyopts(xfd->fd, opts, PH_PASTBIND);
1492 #if WITH_UNIX
1493 if (pf == AF_UNIX && us != NULL) {
1494 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
1495 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
1496 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
1498 #endif /* WITH_UNIX */
1500 #if WITH_IP4 /*|| WITH_IP6*/
1501 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
1502 if (xioparserange(rangename, pf, &xfd->para.socket.range)
1503 < 0) {
1504 free(rangename);
1505 return STAT_NORETRY;
1507 free(rangename);
1508 xfd->para.socket.dorange = true;
1510 #endif
1512 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1513 xio_retropt_tcpwrap(xfd, opts);
1514 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1516 if (xioopts.logopt == 'm') {
1517 Info("starting recvfrom loop, switching to syslog");
1518 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
1519 } else {
1520 Info("starting recvfrom loop");
1523 return STAT_OK;
1527 int retropt_socket_pf(struct opt *opts, int *pf) {
1528 char *pfname;
1530 if (retropt_string(opts, OPT_PROTOCOL_FAMILY, &pfname) >= 0) {
1531 if (isdigit(pfname[0])) {
1532 *pf = strtoul(pfname, NULL /*!*/, 0);
1533 #if WITH_IP4
1534 } else if (!strcasecmp("inet", pfname) ||
1535 !strcasecmp("inet4", pfname) ||
1536 !strcasecmp("ip4", pfname) ||
1537 !strcasecmp("ipv4", pfname) ||
1538 !strcasecmp("2", pfname)) {
1539 *pf = PF_INET;
1540 #endif /* WITH_IP4 */
1541 #if WITH_IP6
1542 } else if (!strcasecmp("inet6", pfname) ||
1543 !strcasecmp("ip6", pfname) ||
1544 !strcasecmp("ipv6", pfname) ||
1545 !strcasecmp("10", pfname)) {
1546 *pf = PF_INET6;
1547 #endif /* WITH_IP6 */
1548 } else {
1549 Error1("unknown protocol family \"%s\"", pfname);
1550 /*! Warn("falling back to INET");*/
1552 free(pfname);
1553 return 0;
1555 return -1;
1559 /* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
1560 the arriving packet. in msgh the msg_name pointer must refer to an (empty)
1561 sockaddr storage. */
1562 int xiogetpacketsrc(int fd, struct msghdr *msgh) {
1563 char peekbuff[1];
1564 #if HAVE_STRUCT_IOVEC
1565 struct iovec iovec;
1566 #endif
1568 #if HAVE_STRUCT_IOVEC
1569 iovec.iov_base = peekbuff;
1570 iovec.iov_len = sizeof(peekbuff);
1571 msgh->msg_iov = &iovec;
1572 msgh->msg_iovlen = 1;
1573 #endif
1574 #if HAVE_STRUCT_MSGHDR_MSGFLAGS
1575 msgh->msg_flags = 0;
1576 #endif
1577 if (Recvmsg(fd, msgh, MSG_PEEK
1578 #ifdef MSG_TRUNC
1579 |MSG_TRUNC
1580 #endif
1581 ) < 0) {
1582 Warn1("recvmsg(): %s", strerror(errno));
1583 return STAT_RETRYLATER;
1585 return STAT_OK;
1589 /* works through the ancillary messages found in the given socket header record
1590 and logs the relevant information (E_DEBUG, E_INFO).
1591 calls protocol/layer specific functions for handling the messages
1592 creates appropriate environment vars if withenv is set */
1593 int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) {
1594 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
1595 struct cmsghdr *cmsg;
1597 /* parse ancillary messages */
1598 cmsg = CMSG_FIRSTHDR(msgh);
1599 while (cmsg != NULL) {
1600 int num = 0; /* number of data components of a ancill.msg */
1601 int i;
1602 char typbuff[16], *typp;
1603 char nambuff[128], *namp;
1604 char valbuff[256], *valp;
1605 char envbuff[256], *envp;
1607 if (withlog) {
1608 xiodump(CMSG_DATA(cmsg),
1609 cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
1610 valbuff, sizeof(valbuff)-1, 0);
1611 Debug4("ancillary message: len="F_cmsg_len", level=%d, type=%d, data=%s",
1612 cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type,
1613 valbuff);
1616 /* try to get the anc.msg. contents in handy components, protocol/level
1617 dependent */
1618 switch (cmsg->cmsg_level) {
1619 case SOL_SOCKET:
1620 xiolog_ancillary_socket(cmsg, &num, typbuff, sizeof(typbuff)-1,
1621 nambuff, sizeof(nambuff)-1,
1622 envbuff, sizeof(envbuff)-1,
1623 valbuff, sizeof(valbuff)-1);
1624 break;
1625 #if WITH_IP4 || WITH_IP6
1626 case SOL_IP:
1627 xiolog_ancillary_ip(cmsg, &num, typbuff, sizeof(typbuff)-1,
1628 nambuff, sizeof(nambuff)-1,
1629 envbuff, sizeof(envbuff)-1,
1630 valbuff, sizeof(valbuff)-1);
1631 break;
1632 #endif /* WITH_IP4 || WITH_IP6 */
1633 #if WITH_IP6
1634 case SOL_IPV6:
1635 xiolog_ancillary_ip6(cmsg, &num, typbuff, sizeof(typbuff)-1,
1636 nambuff, sizeof(nambuff)-1,
1637 envbuff, sizeof(envbuff)-1,
1638 valbuff, sizeof(valbuff)-1);
1639 break;
1640 #endif /* WITH_IP6 */
1641 default:
1642 num = 1;
1643 snprintf(typbuff, sizeof(typbuff)-1, "LEVEL%u", cmsg->cmsg_level);
1644 snprintf(nambuff, sizeof(nambuff)-1, "type%u", cmsg->cmsg_type);
1645 xiodump(CMSG_DATA(cmsg),
1646 cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
1647 valbuff, sizeof(valbuff)-1, 0);
1649 /* here the info is in typbuff (one string), nambuff (num consecutive
1650 strings), and valbuff (num consecutive strings) */
1651 i = 0;
1652 typp = typbuff; namp = nambuff; envp = envbuff; valp = valbuff;
1653 while (i < num) {
1654 if (withlog) {
1655 Info3("ancillary message: %s: %s=%s", typp, namp, valp);
1657 if (withenv) {
1658 if (*envp) {
1659 xiosetenv(envp, valp, 1, NULL);
1660 } else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) {
1661 xiosetenv(typp, valp, 1, NULL);
1662 } else {
1663 xiosetenv2(typp, namp, valp, 1, NULL);
1666 if (++i == num) break;
1667 namp = strchr(namp, '\0')+1;
1668 envp = strchr(envp, '\0')+1;
1669 valp = strchr(valp, '\0')+1;
1671 cmsg = CMSG_NXTHDR(msgh, cmsg);
1673 return 0;
1674 #else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
1675 return -1;
1676 #endif /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
1680 /* check if peer address is within permitted range.
1681 return >= 0 if so. */
1682 int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) {
1683 switch (sa->soa.sa_family) {
1684 #if WITH_IP4
1685 case PF_INET:
1686 return
1687 xiocheckrange_ip4(&sa->ip4, range);
1688 #endif /* WITH_IP4 */
1689 #if WITH_IP6
1690 case PF_INET6:
1691 return
1692 xiocheckrange_ip6(&sa->ip6, range);
1693 #endif /* WITH_IP6 */
1694 #if 0
1695 case PF_UNSPEC:
1697 socklen_t i;
1698 for (i = 0; i < sizeof(sa->soa.sa_data); ++i) {
1699 if ((range->netmask.soa.sa_data[i] & sa->soa.sa_data[i]) != range->netaddr.soa.sa_data[i]) {
1700 return -1;
1703 return 0;
1705 #endif
1707 return -1;
1710 int xiocheckpeer(xiosingle_t *xfd,
1711 union sockaddr_union *pa, union sockaddr_union *la) {
1712 char infobuff[256];
1713 int result;
1715 #if WITH_IP4
1716 if (xfd->para.socket.dorange) {
1717 if (pa == NULL) { return -1; }
1718 if (xiocheckrange(pa, &xfd->para.socket.range) < 0) {
1719 char infobuff[256];
1720 Warn1("refusing connection from %s due to range option",
1721 sockaddr_info((struct sockaddr *)pa, 0,
1722 infobuff, sizeof(infobuff)));
1723 return -1;
1725 Info1("permitting connection from %s due to range option",
1726 sockaddr_info((struct sockaddr *)pa, 0,
1727 infobuff, sizeof(infobuff)));
1729 #endif /* WITH_IP4 */
1731 #if WITH_TCP || WITH_UDP
1732 if (xfd->para.socket.ip.dosourceport) {
1733 if (pa == NULL) { return -1; }
1734 #if WITH_IP4
1735 if (pa->soa.sa_family == AF_INET &&
1736 ntohs(((struct sockaddr_in *)pa)->sin_port) != xfd->para.socket.ip.sourceport) {
1737 Warn1("refusing connection from %s due to wrong sourceport",
1738 sockaddr_info((struct sockaddr *)pa, 0,
1739 infobuff, sizeof(infobuff)));
1740 return -1;
1742 #endif /* WITH_IP4 */
1743 #if WITH_IP6
1744 if (pa->soa.sa_family == AF_INET6 &&
1745 ntohs(((struct sockaddr_in6 *)pa)->sin6_port) != xfd->para.socket.ip.sourceport) {
1746 Warn1("refusing connection from %s due to wrong sourceport",
1747 sockaddr_info((struct sockaddr *)pa, 0,
1748 infobuff, sizeof(infobuff)));
1749 return -1;
1751 #endif /* WITH_IP6 */
1752 Info1("permitting connection from %s due to sourceport option",
1753 sockaddr_info((struct sockaddr *)pa, 0,
1754 infobuff, sizeof(infobuff)));
1755 } else if (xfd->para.socket.ip.lowport) {
1756 if (pa == NULL) { return -1; }
1757 if (pa->soa.sa_family == AF_INET &&
1758 ntohs(((struct sockaddr_in *)pa)->sin_port) >= IPPORT_RESERVED) {
1759 Warn1("refusing connection from %s due to lowport option",
1760 sockaddr_info((struct sockaddr *)pa, 0,
1761 infobuff, sizeof(infobuff)));
1762 return -1;
1764 #if WITH_IP6
1765 else if (pa->soa.sa_family == AF_INET6 &&
1766 ntohs(((struct sockaddr_in6 *)pa)->sin6_port) >=
1767 IPPORT_RESERVED) {
1768 Warn1("refusing connection from %s due to lowport option",
1769 sockaddr_info((struct sockaddr *)pa, 0,
1770 infobuff, sizeof(infobuff)));
1771 return -1;
1773 #endif /* WITH_IP6 */
1774 Info1("permitting connection from %s due to lowport option",
1775 sockaddr_info((struct sockaddr *)pa, 0,
1776 infobuff, sizeof(infobuff)));
1778 #endif /* WITH_TCP || WITH_UDP */
1780 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1781 result = xio_tcpwrap_check(xfd, la, pa);
1782 if (result < 0) {
1783 char infobuff[256];
1784 Warn1("refusing connection from %s due to tcpwrapper option",
1785 sockaddr_info((struct sockaddr *)pa, 0,
1786 infobuff, sizeof(infobuff)));
1787 return -1;
1788 } else if (result > 0) {
1789 Info1("permitting connection from %s due to tcpwrapper option",
1790 sockaddr_info((struct sockaddr *)pa, 0,
1791 infobuff, sizeof(infobuff)));
1793 #endif /* (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1795 return 0; /* permitted */
1799 #if HAVE_STRUCT_CMSGHDR
1800 /* converts the ancillary message in *cmsg into a form useable for further
1801 processing. knows the specifics of common message types.
1802 returns the number of resulting syntax elements in *num
1803 returns a sequence of \0 terminated type strings in *typbuff
1804 returns a sequence of \0 terminated name strings in *nambuff
1805 returns a sequence of \0 terminated value strings in *valbuff
1806 the respective len parameters specify the available space in the buffers
1807 returns STAT_OK or other STAT_*
1809 static int
1810 xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num,
1811 char *typbuff, int typlen,
1812 char *nambuff, int namlen,
1813 char *envbuff, int envlen,
1814 char *valbuff, int vallen) {
1815 const char *cmsgtype, *cmsgname, *cmsgenvn;
1816 size_t msglen;
1817 struct timeval *tv;
1818 int rc = STAT_OK;
1820 #if defined(CMSG_DATA)
1822 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
1823 switch (cmsg->cmsg_type) {
1824 #ifdef SO_PASSCRED
1825 case SO_PASSCRED: /* this is really a UNIX/LOCAL message */
1826 /*! needs implementation */
1827 #endif /* SO_PASSCRED */
1828 #ifdef SO_RIGHTS
1829 case SO_RIGHTS: /* this is really a UNIX/LOCAL message */
1830 /*! needs implementation */
1831 #endif
1832 default: /* binary data */
1833 snprintf(typbuff, typlen, "SOCKET.%u", cmsg->cmsg_type);
1834 nambuff[0] = '\0'; strncat(nambuff, "data", namlen-1);
1835 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
1836 return STAT_OK;
1837 #ifdef SO_TIMESTAMP
1838 # ifdef SCM_TIMESTAMP
1839 case SCM_TIMESTAMP:
1840 # else
1841 case SO_TIMESTAMP:
1842 # endif
1843 tv = (struct timeval *)CMSG_DATA(cmsg);
1844 cmsgtype =
1845 #ifdef SCM_TIMESTAMP
1846 "SCM_TIMESTAMP" /* FreeBSD */
1847 #else
1848 "SO_TIMESTAMP" /* Linux */
1849 #endif
1851 cmsgname = "timestamp";
1852 cmsgenvn = "TIMESTAMP";
1853 { time_t t = tv->tv_sec; ctime_r(&t, valbuff); }
1854 snprintf(strchr(valbuff, '\0')-1/*del \n*/, vallen-strlen(valbuff)+1, ", %06ld usecs", (long)tv->tv_usec);
1855 break;
1856 #endif /* defined(SO_TIMESTAMP) */
1859 /* when we come here we provide a single parameter
1860 with type in cmsgtype, name in cmsgname,
1861 and value already in valbuff */
1862 *num = 1;
1863 if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING;
1864 typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1);
1865 if (strlen(cmsgname) >= namlen) rc = STAT_WARNING;
1866 nambuff[0] = '\0'; strncat(nambuff, cmsgname, namlen-1);
1867 if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING;
1868 envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1);
1869 return rc;
1871 #else /* !defined(CMSG_DATA) */
1873 return STAT_NORETRY;
1875 #endif /* !defined(CMSG_DATA) */
1877 #endif /* HAVE_STRUCT_CMSGHDR */
1880 /* return the name of the interface with given index
1881 or NULL if is fails
1882 The system call requires an arbitrary socket; the calling program may
1883 provide one in parameter ins to avoid creation of a dummy socket. ins must
1884 be <0 if it does not specify a socket fd. */
1885 char *xiogetifname(int ind, char *val, int ins) {
1886 #if !HAVE_PROTOTYPE_LIB_if_indextoname
1887 int s;
1888 struct ifreq ifr;
1890 if (ins >= 0) {
1891 s = ins;
1892 } else {
1893 if ((s = Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
1894 Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno));
1895 return NULL;
1899 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1900 ifr.ifr_index = ind;
1901 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1902 ifr.ifr_ifindex = ind;
1903 #endif
1904 #ifdef SIOCGIFNAME
1905 if(Ioctl(s, SIOCGIFNAME, &ifr) < 0) {
1906 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1907 Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_index=%d, ...}: %s",
1908 s, ifr.ifr_index, strerror(errno));
1909 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1910 Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_ifindex=%d, ...}: %s",
1911 s, ifr.ifr_ifindex, strerror(errno));
1912 #endif
1913 if (ins < 0) Close(s);
1914 return NULL;
1916 #endif /* SIOCGIFNAME */
1917 if (ins < 0) Close(s);
1918 strcpy(val, ifr.ifr_name);
1919 return val;
1920 #else /* HAVE_PROTOTYPE_LIB_if_indextoname */
1921 return if_indextoname(ind, val);
1922 #endif /* HAVE_PROTOTYPE_LIB_if_indextoname */
1926 /* parses a network specification consisting of an address and a mask. */
1927 int xioparsenetwork(const char *rangename, int pf, struct xiorange *range) {
1928 size_t addrlen = 0, masklen = 0;
1929 int result;
1931 switch (pf) {
1932 #if WITH_IP4
1933 case PF_INET:
1934 return xioparsenetwork_ip4(rangename, range);
1935 break;
1936 #endif /* WITH_IP4 */
1937 #if WITH_IP6
1938 case PF_INET6:
1939 return xioparsenetwork_ip6(rangename, range);
1940 break;
1941 #endif /* WITH_IP6 */
1942 case PF_UNSPEC:
1944 char *addrname;
1945 const char *maskname;
1946 if ((maskname = strchr(rangename, ':')) == NULL) {
1947 Error1("syntax error in range \"%s\": use <addr>:<mask>", rangename);
1948 return STAT_NORETRY;
1950 ++maskname; /* skip ':' */
1951 if ((addrname = Malloc(maskname-rangename)) == NULL) {
1952 return STAT_NORETRY;
1954 strncpy(addrname, rangename, maskname-rangename-1); /* ok */
1955 addrname[maskname-rangename-1] = '\0';
1956 result =
1957 dalan(addrname, (char *)&range->netaddr.soa.sa_data, &addrlen,
1958 sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
1959 /* data length */);
1960 if (result < 0) {
1961 Error1("data too long: \"%s\"", addrname);
1962 free(addrname); return STAT_NORETRY;
1963 } else if (result > 0) {
1964 Error1("syntax error in \"%s\"", addrname);
1965 free(addrname); return STAT_NORETRY;
1967 free(addrname);
1968 result =
1969 dalan(maskname, (char *)&range->netmask.soa.sa_data, &masklen,
1970 sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
1971 /* data length */);
1972 if (result < 0) {
1973 Error1("data too long: \"%s\"", maskname);
1974 return STAT_NORETRY;
1975 } else if (result > 0) {
1976 Error1("syntax error in \"%s\"", maskname);
1977 return STAT_NORETRY;
1979 if (addrlen != masklen) {
1980 Error2("network address is "F_Zu" bytes long, mask is "F_Zu" bytes long",
1981 addrlen, masklen);
1982 /* recover by padding the shorter component with 0 */
1983 memset((char *)&range->netaddr.soa.sa_data+addrlen, 0,
1984 MAX(0, addrlen-masklen));
1985 memset((char *)&range->netmask.soa.sa_data+masklen, 0,
1986 MAX(0, masklen-addrlen));
1989 break;
1990 default:
1991 Error1("range option not supported with address family %d", pf);
1992 return STAT_NORETRY;
1994 return STAT_OK;
1998 /* parses a string of form address/bits or address:mask, and fills the fields
1999 of the range union. The addr component is masked with mask. */
2000 int xioparserange(const char *rangename, int pf, struct xiorange *range) {
2001 int i;
2002 if (xioparsenetwork(rangename, pf, range) < 0) {
2003 return -1;
2005 /* we have parsed the address and mask; now we make sure that the stored
2006 address has 0 where mask is 0, to simplify comparisions */
2007 switch (pf) {
2008 #if WITH_IP4
2009 case PF_INET:
2010 range->netaddr.ip4.sin_addr.s_addr &= range->netmask.ip4.sin_addr.s_addr;
2011 break;
2012 #endif /* WITH_IP4 */
2013 #if WITH_IP6
2014 case PF_INET6:
2015 return xiorange_ip6andmask(range);
2016 break;
2017 #endif /* WITH_IP6 */
2018 case PF_UNSPEC:
2019 for (i = 0; i < sizeof(range->netaddr); ++i) {
2020 ((char *)&range->netaddr)[i] &= ((char *)&range->netmask)[i];
2022 break;
2023 default:
2024 Error1("range option not supported with address family %d", pf);
2025 return STAT_NORETRY;
2027 return 0;
2031 /* set environment variables describing (part of) a socket address, e.g.
2032 SOCAT_SOCKADDR. lr (local/remote) specifies a string like "SOCK" or "PEER".
2033 proto should correspond to the third parameter of socket(2) and is used to
2034 determine the presence of port information. */
2035 int xiosetsockaddrenv(const char *lr,
2036 union sockaddr_union *sau, socklen_t salen,
2037 int proto) {
2038 # define XIOSOCKADDRENVLEN 256
2039 char namebuff[XIOSOCKADDRENVLEN];
2040 char valuebuff[XIOSOCKADDRENVLEN];
2041 int idx = 0, result;
2043 strcpy(namebuff, lr);
2044 switch (sau->soa.sa_family) {
2045 #if WITH_UNIX
2046 case PF_UNIX:
2047 result =
2048 xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2049 valuebuff, XIOSOCKADDRENVLEN,
2050 &sau->un, salen, proto);
2051 xiosetenv(namebuff, valuebuff, 1, NULL);
2052 break;
2053 #endif /* WITH_UNIX */
2054 #if WITH_IP4
2055 case PF_INET:
2056 do {
2057 result =
2058 xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2059 valuebuff, XIOSOCKADDRENVLEN,
2060 &sau->ip4, proto);
2061 xiosetenv(namebuff, valuebuff, 1, NULL);
2062 namebuff[strlen(lr)] = '\0'; ++idx;
2063 } while (result > 0);
2064 break;
2065 #endif /* WITH_IP4 */
2066 #if WITH_IP6
2067 case PF_INET6:
2068 strcpy(namebuff, lr);
2069 do {
2070 result =
2071 xiosetsockaddrenv_ip6(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2072 valuebuff, XIOSOCKADDRENVLEN,
2073 &sau->ip6, proto);
2074 xiosetenv(namebuff, valuebuff, 1, NULL);
2075 namebuff[strlen(lr)] = '\0'; ++idx;
2076 } while (result > 0);
2077 break;
2078 #endif /* WITH_IP6 */
2079 #if LATER
2080 case PF_PACKET:
2081 result = xiosetsockaddrenv_packet(lr, (void *)sau, proto); break;
2082 #endif
2083 default:
2084 result = -1;
2085 break;
2087 return result;
2088 # undef XIOSOCKADDRENVLEN
2091 #endif /* _WITH_SOCKET */
2093 /* these do sockets internally */
2095 /* retrieves options so-type and so-prototype from opts, calls socket, and
2096 ev. generates an appropriate error message.
2097 returns 0 on success or -1 if an error occurred. */
2098 int
2099 xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
2100 int result;
2102 retropt_int(opts, OPT_SO_TYPE, &socktype);
2103 retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
2104 result = Socket(pf, socktype, proto);
2105 if (result < 0) {
2106 Msg4(msglevel, "socket(%d, %d, %d): %s",
2107 pf, socktype, proto, strerror(errno));
2108 return -1;
2110 return result;
2113 /* retrieves options so-type and so-prototype from opts, calls socketpair, and
2114 ev. generates an appropriate error message.
2115 returns 0 on success or -1 if an error occurred. */
2116 int
2117 xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]) {
2118 int result;
2120 retropt_int(opts, OPT_SO_TYPE, &socktype);
2121 retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
2122 result = Socketpair(pf, socktype, proto, sv);
2123 if (result < 0) {
2124 Error5("socketpair(%d, %d, %d, %p): %s",
2125 pf, socktype, proto, sv, strerror(errno));
2126 return -1;
2128 return result;