Version 1.8.0.0
[socat.git] / xio-socket.c
blobc869b6dc4ffff9a1e16f5214752b0db72c1084e2
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_VSOCK
18 #include "xio-vsock.h"
19 #endif
20 #if WITH_IP4
21 #include "xio-ip4.h"
22 #endif /* WITH_IP4 */
23 #if WITH_IP6
24 #include "xio-ip6.h"
25 #endif /* WITH_IP6 */
26 #include "xio-ip.h"
27 #include "xio-listen.h"
28 #include "xio-interface.h"
29 #include "xio-ipapp.h" /*! not clean */
30 #include "xio-tcpwrap.h"
33 static int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
34 static int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
35 static int xioopen_socket_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
36 static int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
37 static int xioopen_socket_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
38 static int xioopen_socket_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc);
40 static
41 int _xioopen_socket_sendto(const char *pfname, const char *type,
42 const char *proto, const char *address,
43 struct opt *opts, int xioflags, xiofile_t *xxfd,
44 groups_t groups);
46 static int
47 xiolog_ancillary_socket(struct single *sfd, struct cmsghdr *cmsg, int *num,
48 char *typbuff, int typlen,
49 char *nambuff, int namlen,
50 char *envbuff, int envlen,
51 char *valbuff, int vallen);
52 static int xiobind(
53 struct single *sfd,
54 union sockaddr_union *us,
55 size_t uslen,
56 struct opt *opts,
57 int pf,
58 bool alt,
59 int level);
62 #if WITH_GENERICSOCKET
63 /* generic socket addresses */
64 const struct addrdesc xioaddr_socket_connect = { "SOCKET-CONNECT", 1+XIO_RDWR, xioopen_socket_connect, GROUP_FD|GROUP_SOCKET|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<domain>:<protocol>:<remote-address>") };
65 #if WITH_LISTEN
66 const struct addrdesc xioaddr_socket_listen = { "SOCKET-LISTEN", 1+XIO_RDWR, xioopen_socket_listen, GROUP_FD|GROUP_SOCKET|GROUP_LISTEN|GROUP_RANGE|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<domain>:<protocol>:<local-address>") };
67 #endif /* WITH_LISTEN */
68 const struct addrdesc xioaddr_socket_sendto = { "SOCKET-SENDTO", 1+XIO_RDWR, xioopen_socket_sendto, GROUP_FD|GROUP_SOCKET, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<remote-address>") };
69 const struct addrdesc xioaddr_socket_datagram= { "SOCKET-DATAGRAM", 1+XIO_RDWR, xioopen_socket_datagram, GROUP_FD|GROUP_SOCKET|GROUP_RANGE, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<remote-address>") };
70 const struct addrdesc xioaddr_socket_recvfrom= { "SOCKET-RECVFROM", 1+XIO_RDWR, xioopen_socket_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_RANGE|GROUP_CHILD, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<local-address>") };
71 const struct addrdesc xioaddr_socket_recv = { "SOCKET-RECV", 1+XIO_RDONLY, xioopen_socket_recv, GROUP_FD|GROUP_SOCKET|GROUP_RANGE, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<local-address>") };
72 #endif /* WITH_GENERICSOCKET */
75 /* the following options apply not only to generic socket addresses but to all
76 addresses that have anything to do with sockets */
77 const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DEBUG };
78 #ifdef SO_ACCEPTCONN /* AIX433 */
79 const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN};
80 #endif /* SO_ACCEPTCONN */
81 const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST};
82 const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT_NULL, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR};
83 const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE};
84 #if HAVE_STRUCT_LINGER
85 const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER };
86 #else /* !HAVE_STRUCT_LINGER */
87 const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_LINGER };
88 #endif /* !HAVE_STRUCT_LINGER */
89 const struct optdesc opt_so_oobinline= { "so-oobinline", "oobinline", OPT_SO_OOBINLINE,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_OOBINLINE};
90 const struct optdesc opt_so_sndbuf = { "so-sndbuf", "sndbuf", OPT_SO_SNDBUF, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDBUF};
91 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 };
92 const struct optdesc opt_so_rcvbuf = { "so-rcvbuf", "rcvbuf", OPT_SO_RCVBUF, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVBUF};
93 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 };
94 const struct optdesc opt_so_error = { "so-error", "error", OPT_SO_ERROR, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ERROR};
95 const struct optdesc opt_so_type = { "so-type", "type", OPT_SO_TYPE, GROUP_SOCKET, PH_SOCKET, TYPE_INT, OFUNC_SPEC, SOL_SOCKET, SO_TYPE };
96 const struct optdesc opt_so_dontroute= { "so-dontroute", "dontroute", OPT_SO_DONTROUTE,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DONTROUTE };
97 #ifdef SO_RCVLOWAT
98 const struct optdesc opt_so_rcvlowat = { "so-rcvlowat", "rcvlowat", OPT_SO_RCVLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVLOWAT };
99 #endif
100 #ifdef SO_SNDLOWAT
101 const struct optdesc opt_so_sndlowat = { "so-sndlowat", "sndlowat", OPT_SO_SNDLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDLOWAT };
102 #endif
103 /* end of setsockopt options of UNIX98 standard */
105 #ifdef SO_RCVTIMEO
106 const struct optdesc opt_so_rcvtimeo = { "so-rcvtimeo", "rcvtimeo", OPT_SO_RCVTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVTIMEO };
107 #endif
108 #ifdef SO_SNDTIMEO
109 const struct optdesc opt_so_sndtimeo = { "so-sndtimeo", "sndtimeo", OPT_SO_SNDTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDTIMEO };
110 #endif
112 #ifdef SO_AUDIT /* AIX 4.3.3 */
113 const struct optdesc opt_so_audit = { "so-audit", "audit", OPT_SO_AUDIT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_AUDIT };
114 #endif /* SO_AUDIT */
115 #ifdef SO_ATTACH_FILTER
116 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};
117 #endif
118 #ifdef SO_DETACH_FILTER
119 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};
120 #endif
121 #ifdef SO_BINDTODEVICE /* Linux: man 7 socket */
122 const struct optdesc opt_so_bindtodevice={"so-bindtodevice","if",OPT_SO_BINDTODEVICE,GROUP_SOCKET,PH_PASTSOCKET,TYPE_NAME,OFUNC_SOCKOPT,SOL_SOCKET,SO_BINDTODEVICE};
123 #endif
124 #ifdef SO_BSDCOMPAT
125 const struct optdesc opt_so_bsdcompat= { "so-bsdcompat","bsdcompat",OPT_SO_BSDCOMPAT,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BSDCOMPAT };
126 #endif
127 #ifdef SO_CKSUMRECV
128 const struct optdesc opt_so_cksumrecv= { "so-cksumrecv","cksumrecv",OPT_SO_CKSUMRECV,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_CKSUMRECV };
129 #endif /* SO_CKSUMRECV */
130 #ifdef SO_TIMESTAMP
131 const struct optdesc opt_so_timestamp= { "so-timestamp","timestamp",OPT_SO_TIMESTAMP,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_TIMESTAMP };
132 #endif
133 #ifdef SO_KERNACCEPT /* AIX 4.3.3 */
134 const struct optdesc opt_so_kernaccept={ "so-kernaccept","kernaccept",OPT_SO_KERNACCEPT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KERNACCEPT};
135 #endif /* SO_KERNACCEPT */
136 #ifdef SO_NO_CHECK
137 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 };
138 #endif
139 #ifdef SO_NOREUSEADDR /* AIX 4.3.3 */
140 const struct optdesc opt_so_noreuseaddr={"so-noreuseaddr","noreuseaddr",OPT_SO_NOREUSEADDR,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET, SO_NOREUSEADDR};
141 #endif /* SO_NOREUSEADDR */
142 #ifdef SO_PASSCRED
143 const struct optdesc opt_so_passcred = { "so-passcred", "passcred", OPT_SO_PASSCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PASSCRED};
144 #endif
145 #ifdef SO_PEERCRED
146 const struct optdesc opt_so_peercred = { "so-peercred", "peercred", OPT_SO_PEERCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT3,OFUNC_SOCKOPT, SOL_SOCKET, SO_PEERCRED};
147 #endif
148 #ifdef SO_PRIORITY
149 const struct optdesc opt_so_priority = { "so-priority", "priority", OPT_SO_PRIORITY, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PRIORITY};
150 #endif
151 #ifdef SO_REUSEPORT /* AIX 4.3.3, BSD, HP-UX, Linux >=3.9 */
152 const struct optdesc opt_so_reuseport = { "so-reuseport","reuseport",OPT_SO_REUSEPORT,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEPORT };
153 #endif /* defined(SO_REUSEPORT) */
154 #ifdef SO_SECURITY_AUTHENTICATION
155 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};
156 #endif
157 #ifdef SO_SECURITY_ENCRYPTION_NETWORK
158 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};
159 #endif
160 #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT
161 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};
162 #endif
163 #ifdef SO_USE_IFBUFS
164 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};
165 #endif /* SO_USE_IFBUFS */
166 #ifdef SO_USELOOPBACK /* AIX433, Solaris, HP-UX */
167 const struct optdesc opt_so_useloopback= { "so-useloopback","useloopback",OPT_SO_USELOOPBACK,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT, SOL_SOCKET, SO_USELOOPBACK};
168 #endif /* SO_USELOOPBACK */
169 #ifdef SO_DGRAM_ERRIND /* Solaris */
170 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};
171 #endif /* SO_DGRAM_ERRIND */
172 #ifdef SO_DONTLINGER /* Solaris */
173 const struct optdesc opt_so_dontlinger = { "so-dontlinger", "dontlinger", OPT_SO_DONTLINGER, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DONTLINGER };
174 #endif
175 /* the SO_PROTOTYPE is OS defined on Solaris, HP-UX; we lend this for a more
176 general purpose */
177 const struct optdesc opt_so_prototype = { "so-protocol", "protocol", OPT_SO_PROTOTYPE, GROUP_SOCKET,PH_SOCKET, TYPE_INT,OFUNC_SPEC, SOL_SOCKET,SO_PROTOCOL };
178 #ifdef FIOSETOWN
179 const struct optdesc opt_fiosetown = { "fiosetown", NULL, OPT_FIOSETOWN, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, FIOSETOWN };
180 #endif
181 #ifdef SIOCSPGRP
182 const struct optdesc opt_siocspgrp = { "siocspgrp", NULL, OPT_SIOCSPGRP, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, SIOCSPGRP };
183 #endif
184 const struct optdesc opt_bind = { "bind", NULL, OPT_BIND, GROUP_SOCKET, PH_BIND, TYPE_STRING,OFUNC_SPEC };
185 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) };
186 const struct optdesc opt_protocol_family = { "protocol-family", "pf", OPT_PROTOCOL_FAMILY, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC };
188 /* generic setsockopt() options */
189 const struct optdesc opt_setsockopt = { "setsockopt", "sockopt", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
190 const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int", OPT_SETSOCKOPT_INT, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_INT, OFUNC_SOCKOPT_GENERIC, 0, 0 };
191 const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
192 const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_STRING, OFUNC_SOCKOPT_GENERIC, 0, 0 };
193 const struct optdesc opt_setsockopt_listen = { "setsockopt-listen", "sockopt-listen", OPT_SETSOCKOPT_LISTEN, GROUP_SOCKET,PH_PREBIND, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 };
195 const struct optdesc opt_null_eof = { "null-eof", NULL, OPT_NULL_EOF, GROUP_SOCKET, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.null_eof) };
198 #if WITH_GENERICSOCKET
200 static int xioopen_socket_connect(
201 int argc,
202 const char *argv[],
203 struct opt *opts,
204 int xioflags,
205 xiofile_t *xxfd,
206 const struct addrdesc *addrdesc)
208 struct single *sfd = &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 xio_syntax(argv[0], 3, argc-1, addrdesc->syntax);
223 return STAT_NORETRY;
226 pf = strtoul(pfname, &garbage, 0);
227 if (*garbage != '\0') {
228 Warn1("garbage in parameter: \"%s\"", garbage);
231 proto = strtoul(protname, &garbage, 0);
232 if (*garbage != '\0') {
233 Warn1("garbage in parameter: \"%s\"", garbage);
236 retropt_socket_pf(opts, &pf);
237 retropt_int(opts, OPT_SO_TYPE, &socktype);
238 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
239 if (sfd->howtoend == END_UNSPEC)
240 sfd->howtoend = END_SHUTDOWN;
242 if (applyopts_single(sfd, opts, PH_INIT) < 0)
243 return -1;
244 applyopts(sfd, -1, opts, PH_INIT);
245 applyopts(sfd, -1, opts, PH_EARLY);
247 themsize = 0;
248 if ((result =
249 dalan(address, (uint8_t *)&them.soa.sa_data, &themsize, sizeof(them), 'i'))
250 < 0) {
251 Error1("data too long: \"%s\"", address);
252 } else if (result > 0) {
253 Error1("syntax error in \"%s\"", address);
255 them.soa.sa_family = pf;
256 themlen = themsize +
257 #if HAVE_STRUCT_SOCKADDR_SALEN
258 sizeof(them.soa.sa_len) +
259 #endif
260 sizeof(them.soa.sa_family);
262 sfd->dtype = XIOREAD_STREAM|XIOWRITE_STREAM;
264 socket_init(0, &us);
265 if (retropt_bind(opts, 0 /*pf*/, socktype, proto, (struct sockaddr *)&us, &uslen, 3,
266 sfd->para.socket.ip.ai_flags)
267 != STAT_NOACTION) {
268 needbind = true;
269 us.soa.sa_family = pf;
272 if ((result =
273 xioopen_connect(sfd,
274 needbind?&us:NULL, uslen,
275 (struct sockaddr *)&them, themlen,
276 opts, pf, socktype, proto, false)) != 0) {
277 return result;
279 if ((result = _xio_openlate(sfd, opts)) < 0) {
280 return result;
282 return STAT_OK;
285 #if WITH_LISTEN
286 static int xioopen_socket_listen(
287 int argc,
288 const char *argv[],
289 struct opt *opts,
290 int xioflags,
291 xiofile_t *xxfd,
292 const struct addrdesc *addrdesc)
294 struct single *sfd = &xxfd->stream;
295 const char *pfname = argv[1];
296 const char *protname = argv[2];
297 const char *usname = argv[3];
298 char *garbage;
299 int pf;
300 int proto;
301 int socktype = SOCK_STREAM;
302 union sockaddr_union us; socklen_t uslen; size_t ussize;
303 struct opt *opts0;
304 int result;
306 if (argc != 4) {
307 xio_syntax(argv[0], 3, argc-1, addrdesc->syntax);
308 return STAT_NORETRY;
311 pf = strtoul(pfname, &garbage, 0);
312 if (*garbage != '\0') {
313 Warn1("garbage in parameter: \"%s\"", garbage);
316 proto = strtoul(protname, &garbage, 0);
317 if (*garbage != '\0') {
318 Warn1("garbage in parameter: \"%s\"", garbage);
321 retropt_socket_pf(opts, &pf);
322 retropt_int(opts, OPT_SO_TYPE, &socktype);
323 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
324 if (sfd->howtoend == END_UNSPEC)
325 sfd->howtoend = END_SHUTDOWN;
327 socket_init(0, &us);
328 ussize = 0;
329 if ((result =
330 dalan(usname, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i'))
331 < 0) {
332 Error1("data too long: \"%s\"", usname);
333 } else if (result > 0) {
334 Error1("syntax error in \"%s\"", usname);
336 uslen = ussize + sizeof(us.soa.sa_family)
337 #if HAVE_STRUCT_SOCKADDR_SALEN
338 + sizeof(us.soa.sa_len)
339 #endif
341 us.soa.sa_family = pf;
343 if (applyopts_single(sfd, opts, PH_INIT) < 0)
344 return -1;
345 applyopts(sfd, -1, opts, PH_INIT);
346 applyopts(sfd, -1, opts, PH_EARLY);
348 opts0 = copyopts(opts, GROUP_ALL);
350 if ((result =
351 xioopen_listen(sfd, xioflags,
352 &us.soa, uslen,
353 opts, opts0, 0/*instead of pf*/, socktype, proto))
354 != STAT_OK)
355 return result;
356 return STAT_OK;
358 #endif /* WITH_LISTEN */
360 /* we expect the form: ...:domain:type:protocol:remote-address */
361 static int xioopen_socket_sendto(
362 int argc,
363 const char *argv[],
364 struct opt *opts,
365 int xioflags,
366 xiofile_t *xxfd,
367 const struct addrdesc *addrdesc)
369 int result;
371 if (argc != 5) {
372 xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
373 return STAT_NORETRY;
375 if ((result =
376 _xioopen_socket_sendto(argv[1], argv[2], argv[3], argv[4],
377 opts, xioflags, xxfd, addrdesc->groups))
378 != STAT_OK) {
379 return result;
381 _xio_openlate(&xxfd->stream, opts);
382 return STAT_OK;
385 static
386 int _xioopen_socket_sendto(const char *pfname, const char *type,
387 const char *protname, const char *address,
388 struct opt *opts, int xioflags, xiofile_t *xxfd,
389 groups_t groups) {
390 xiosingle_t *sfd = &xxfd->stream;
391 char *garbage;
392 union sockaddr_union us = {{0}};
393 socklen_t uslen = 0; size_t ussize;
394 size_t themsize;
395 int pf;
396 int socktype = SOCK_RAW;
397 int proto;
398 bool needbind = false;
399 char *bindstring = NULL;
400 int result;
402 pf = strtoul(pfname, &garbage, 0);
403 if (*garbage != '\0') {
404 Warn1("garbage in parameter: \"%s\"", garbage);
407 socktype = strtoul(type, &garbage, 0);
408 if (*garbage != '\0') {
409 Warn1("garbage in parameter: \"%s\"", garbage);
412 proto = strtoul(protname, &garbage, 0);
413 if (*garbage != '\0') {
414 Warn1("garbage in parameter: \"%s\"", garbage);
417 retropt_socket_pf(opts, &pf);
418 retropt_int(opts, OPT_SO_TYPE, &socktype);
419 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
420 if (sfd->howtoend == END_UNSPEC)
421 sfd->howtoend = END_SHUTDOWN;
423 sfd->peersa.soa.sa_family = pf;
424 themsize = 0;
425 if ((result =
426 dalan(address, (uint8_t *)&sfd->peersa.soa.sa_data, &themsize,
427 sizeof(sfd->peersa), 'i'))
428 < 0) {
429 Error1("data too long: \"%s\"", address);
430 } else if (result > 0) {
431 Error1("syntax error in \"%s\"", address);
433 sfd->salen = themsize + sizeof(sa_family_t)
434 #if HAVE_STRUCT_SOCKADDR_SALEN
435 + sizeof(sfd->peersa.soa.sa_len)
436 #endif
438 #if HAVE_STRUCT_SOCKADDR_SALEN
439 sfd->peersa.soa.sa_len =
440 sizeof(sfd->peersa.soa.sa_len) + sizeof(sfd->peersa.soa.sa_family) +
441 themsize;
442 #endif
444 if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
445 applyopts(sfd, -1, opts, PH_INIT);
447 if (pf == PF_UNSPEC) {
448 pf = sfd->peersa.soa.sa_family;
451 sfd->dtype = XIODATA_RECVFROM;
453 if (retropt_string(opts, OPT_BIND, &bindstring) == 0) {
454 ussize = 0;
455 if ((result =
456 dalan(bindstring, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i'))
457 < 0) {
458 Error1("data too long: \"%s\"", bindstring);
459 } else if (result > 0) {
460 Error1("syntax error in \"%s\"", bindstring);
462 us.soa.sa_family = pf;
463 uslen = ussize + sizeof(sa_family_t)
464 #if HAVE_STRUCT_SOCKADDR_SALEN
465 + sizeof(us.soa.sa_len)
466 #endif
468 needbind = true;
471 return
472 _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
473 opts, xioflags, sfd, groups, pf, socktype, proto, 0);
477 /* we expect the form: ...:domain:socktype:protocol:local-address */
478 static
479 int xioopen_socket_recvfrom(
480 int argc,
481 const char *argv[],
482 struct opt *opts,
483 int xioflags,
484 xiofile_t *xxfd,
485 const struct addrdesc *addrdesc)
487 struct single *sfd = &xxfd->stream;
488 const char *pfname = argv[1];
489 const char *typename = argv[2];
490 const char *protname = argv[3];
491 const char *address = argv[4];
492 char *garbage;
493 union sockaddr_union *us = &sfd->para.socket.la;
494 socklen_t uslen; size_t ussize;
495 int pf, socktype, proto;
496 char *rangename;
497 int result;
499 if (argc != 5) {
500 xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
501 return STAT_NORETRY;
504 pf = strtoul(pfname, &garbage, 0);
505 if (*garbage != '\0') {
506 Warn1("garbage in parameter: \"%s\"", garbage);
509 socktype = strtoul(typename, &garbage, 0);
510 if (*garbage != '\0') {
511 Warn1("garbage in parameter: \"%s\"", garbage);
514 proto = strtoul(protname, &garbage, 0);
515 if (*garbage != '\0') {
516 Warn1("garbage in parameter: \"%s\"", garbage);
519 retropt_socket_pf(opts, &pf);
520 retropt_int(opts, OPT_SO_TYPE, &socktype);
521 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
522 if (sfd->howtoend == END_UNSPEC)
523 sfd->howtoend = END_NONE;
525 ussize = 0;
526 if ((result =
527 dalan(address, (uint8_t *)&us->soa.sa_data, &ussize, sizeof(*us), 'i'))
528 < 0) {
529 Error1("data too long: \"%s\"", address);
530 } else if (result > 0) {
531 Error1("syntax error in \"%s\"", address);
533 us->soa.sa_family = pf;
534 uslen = ussize + sizeof(us->soa.sa_family)
535 #if HAVE_STRUCT_SOCKADDR_SALEN
536 + sizeof(us->soa.sa_len);
537 #endif
539 sfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
541 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
542 if (xioparserange(rangename, 0, &sfd->para.socket.range,
543 sfd->para.socket.ip.ai_flags)
544 < 0) {
545 free(rangename);
546 return STAT_NORETRY;
548 sfd->para.socket.dorange = true;
549 free(rangename);
552 if ((result =
553 _xioopen_dgram_recvfrom(sfd, xioflags, &us->soa, uslen,
554 opts, pf, socktype, proto, E_ERROR))
555 != STAT_OK) {
556 return result;
558 _xio_openlate(sfd, opts);
559 return STAT_OK;
562 /* we expect the form: ...:domain:type:protocol:local-address */
563 static
564 int xioopen_socket_recv(
565 int argc,
566 const char *argv[],
567 struct opt *opts,
568 int xioflags,
569 xiofile_t *xxfd,
570 const struct addrdesc *addrdesc)
572 struct single *sfd = &xxfd->stream;
573 const char *pfname = argv[1];
574 const char *typename = argv[2];
575 const char *protname = argv[3];
576 const char *address = argv[4];
577 char *garbage;
578 union sockaddr_union us;
579 socklen_t uslen; size_t ussize;
580 int pf, socktype, proto;
581 char *rangename;
582 int result;
584 if (argc != 5) {
585 xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
586 return STAT_NORETRY;
589 pf = strtoul(pfname, &garbage, 0);
590 if (*garbage != '\0') {
591 Warn1("garbage in parameter: \"%s\"", garbage);
594 socktype = strtoul(typename, &garbage, 0);
595 if (*garbage != '\0') {
596 Warn1("garbage in parameter: \"%s\"", garbage);
599 proto = strtoul(protname, &garbage, 0);
600 if (*garbage != '\0') {
601 Warn1("garbage in parameter: \"%s\"", garbage);
604 retropt_socket_pf(opts, &pf);
605 retropt_int(opts, OPT_SO_TYPE, &socktype);
606 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
607 if (sfd->howtoend == END_UNSPEC)
608 sfd->howtoend = END_NONE;
610 ussize = 0;
611 if ((result =
612 dalan(address, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i'))
613 < 0) {
614 Error1("data too long: \"%s\"", address);
615 } else if (result > 0) {
616 Error1("syntax error in \"%s\"", address);
618 us.soa.sa_family = pf;
619 uslen = ussize + sizeof(sa_family_t)
620 #if HAVE_STRUCT_SOCKADDR_SALEN
621 +sizeof(us.soa.sa_len)
622 #endif
624 sfd->dtype = XIOREAD_RECV;
625 sfd->para.socket.la.soa.sa_family = pf;
627 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
628 if (xioparserange(rangename, 0, &sfd->para.socket.range,
629 sfd->para.socket.ip.ai_flags)
630 < 0) {
631 free(rangename);
632 return STAT_NORETRY;
634 sfd->para.socket.dorange = true;
635 free(rangename);
638 if ((result =
639 _xioopen_dgram_recv(sfd, xioflags, &us.soa,
640 uslen, opts, pf, socktype, proto, E_ERROR))
641 != STAT_OK) {
642 return result;
644 _xio_openlate(sfd, opts);
645 return STAT_OK;
649 /* we expect the form: ...:domain:type:protocol:remote-address */
650 static int xioopen_socket_datagram(
651 int argc,
652 const char *argv[],
653 struct opt *opts,
654 int xioflags,
655 xiofile_t *xxfd,
656 const struct addrdesc *addrdesc)
658 xiosingle_t *sfd = &xxfd->stream;
659 const char *pfname = argv[1];
660 const char *typename = argv[2];
661 const char *protname = argv[3];
662 const char *address = argv[4];
663 char *garbage;
664 char *rangename;
665 size_t themsize;
666 int pf;
667 int result;
669 if (argc != 5) {
670 xio_syntax(argv[0], 4, argc-1, addrdesc->syntax);
671 return STAT_NORETRY;
674 pf = strtoul(pfname, &garbage, 0);
675 if (*garbage != '\0') {
676 Warn1("garbage in parameter: \"%s\"", garbage);
679 retropt_socket_pf(opts, &pf);
680 /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/
681 if (sfd->howtoend == END_UNSPEC)
682 sfd->howtoend = END_SHUTDOWN;
684 sfd->peersa.soa.sa_family = pf;
685 themsize = 0;
686 if ((result =
687 dalan(address, (uint8_t *)&sfd->peersa.soa.sa_data, &themsize,
688 sizeof(sfd->peersa), 'i'))
689 < 0) {
690 Error1("data too long: \"%s\"", address);
691 } else if (result > 0) {
692 Error1("syntax error in \"%s\"", address);
694 sfd->salen = themsize + sizeof(sa_family_t);
695 #if HAVE_STRUCT_SOCKADDR_SALEN
696 sfd->peersa.soa.sa_len =
697 sizeof(sfd->peersa.soa.sa_len) + sizeof(sfd->peersa.soa.sa_family) +
698 themsize;
699 #endif
701 if ((result =
702 _xioopen_socket_sendto(pfname, typename, protname, address,
703 opts, xioflags, xxfd, addrdesc->groups))
704 != STAT_OK) {
705 return result;
708 sfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
710 sfd->para.socket.la.soa.sa_family = sfd->peersa.soa.sa_family;
712 /* which reply sockets will accept - determine by range option */
713 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
714 if (xioparserange(rangename, 0, &sfd->para.socket.range,
715 sfd->para.socket.ip.ai_flags)
716 < 0) {
717 free(rangename);
718 return STAT_NORETRY;
720 sfd->para.socket.dorange = true;
721 sfd->dtype |= XIOREAD_RECV_CHECKRANGE;
722 free(rangename);
725 _xio_openlate(sfd, opts);
726 return STAT_OK;
729 #endif /* WITH_GENERICSOCKET */
731 /* EINTR not handled specially */
732 int xiogetpacketinfo(struct single *sfd, int fd)
734 #if defined(MSG_ERRQUEUE)
735 int _errno = errno;
736 char peername[256];
737 union sockaddr_union _peername;
738 /* union sockaddr_union _sockname; */
739 union sockaddr_union *pa = &_peername; /* peer address */
740 /* union sockaddr_union *la = &_sockname; */ /* local address */
741 socklen_t palen = sizeof(_peername); /* peer address size */
742 char ctrlbuff[1024]; /* ancillary messages */
743 struct msghdr msgh = {0};
745 msgh.msg_name = pa;
746 msgh.msg_namelen = palen;
747 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
748 msgh.msg_control = ctrlbuff;
749 #endif
750 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
751 msgh.msg_controllen = sizeof(ctrlbuff);
752 #endif
753 if (xiogetancillary(fd,
754 &msgh,
755 MSG_ERRQUEUE
756 #ifdef MSG_TRUNC
757 |MSG_TRUNC
758 #endif
759 ) >= 0
761 palen = msgh.msg_namelen;
763 Notice1("receiving packet from %s"/*"src"*/,
764 sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*,
765 sockaddr_info(&la->soa, sockname, sizeof(sockname))*/);
767 xiodopacketinfo(sfd, &msgh, true, true);
769 errno = _errno;
770 #endif /* defined(MSG_ERRQUEUE) */
771 return 0;
776 /* A subroutine that is common to all socket addresses that want to connect()
777 a socket to a peer.
778 Applies and consumes the following options:
779 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
780 PH_CONNECTED, PH_LATE,
781 OFUNC_OFFSET,
782 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
783 Does not fork, does not retry.
784 Alternate (alt) bind semantics are:
785 with IP sockets: lowport (selects randomly a free port from 640 to 1023)
786 with UNIX and abstract sockets: uses tmpname() to find a free file system
787 entry.
788 returns 0 on success.
790 int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
791 struct sockaddr *them, size_t themlen,
792 struct opt *opts, int pf, int socktype, int protocol,
793 bool alt, int level) {
794 int fcntl_flags = 0;
795 char infobuff[256];
796 union sockaddr_union la;
797 socklen_t lalen = themlen;
798 int _errno;
799 int result;
801 #if WITH_UNIX
802 if (pf == PF_UNIX && us != NULL) {
803 applyopts_named(us->un.sun_path, opts, PH_EARLY);
805 #endif
807 if ((sfd->fd = xiosocket(opts, pf, socktype, protocol, level)) < 0) {
808 return STAT_RETRYLATER;
811 applyopts_offset(sfd, opts);
812 applyopts(sfd, -1, opts, PH_PASTSOCKET);
813 applyopts(sfd, -1, opts, PH_FD);
815 applyopts_cloexec(sfd->fd, opts);
817 if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) {
818 return -1;
821 applyopts(sfd, -1, opts, PH_CONNECT);
823 if (sfd->para.socket.connect_timeout.tv_sec != 0 ||
824 sfd->para.socket.connect_timeout.tv_usec != 0) {
825 fcntl_flags = Fcntl(sfd->fd, F_GETFL);
826 Fcntl_l(sfd->fd, F_SETFL, fcntl_flags|O_NONBLOCK);
829 result = Connect(sfd->fd, them, themlen);
830 _errno = errno;
831 la.soa.sa_family = them->sa_family; lalen = sizeof(la);
832 if (Getsockname(sfd->fd, &la.soa, &lalen) < 0) {
833 Msg4(level-1, "getsockname(%d, %p, {%d}): %s",
834 sfd->fd, &la.soa, lalen, strerror(errno));
836 errno = _errno;
837 if (result < 0) {
838 if (errno == EINPROGRESS) {
839 if (sfd->para.socket.connect_timeout.tv_sec != 0 ||
840 sfd->para.socket.connect_timeout.tv_usec != 0) {
841 struct timeval timeout;
842 struct pollfd writefd;
843 int err;
844 socklen_t errlen = sizeof(err);
845 int result;
847 Info4("connect(%d, %s, "F_Zd"): %s",
848 sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
849 themlen, strerror(errno));
850 timeout = sfd->para.socket.connect_timeout;
851 writefd.fd = sfd->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 sfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno));
857 Close(sfd->fd);
858 return STAT_RETRYLATER;
860 if (result == 0) {
861 Msg2(level, "connecting to %s: %s",
862 sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
863 strerror(ETIMEDOUT));
864 Close(sfd->fd);
865 return STAT_RETRYLATER;
867 if (writefd.revents & POLLERR) {
868 #if 0
869 unsigned char dummy[1];
870 Read(sfd->fd, &dummy, 1); /* get error message */
871 Msg2(level, "connecting to %s: %s",
872 sockaddr_info(them, infobuff, sizeof(infobuff)),
873 strerror(errno));
874 #else
875 Connect(sfd->fd, them, themlen); /* get error message */
876 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
877 sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
878 themlen, strerror(errno));
879 #endif
880 Close(sfd->fd);
881 return STAT_RETRYLATER;
883 /* otherwise OK or network error */
884 result = Getsockopt(sfd->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
885 if (result != 0) {
886 Msg2(level, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s",
887 sfd->fd, strerror(err));
888 Close(sfd->fd);
889 return STAT_RETRYLATER;
891 Debug2("getsockopt(%d, SOL_SOCKET, SO_ERROR, { %d }) -> 0",
892 sfd->fd, err);
893 if (err != 0) {
894 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
895 sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
896 themlen, strerror(err));
897 Close(sfd->fd);
898 return STAT_RETRYLATER;
900 Fcntl_l(sfd->fd, F_SETFL, fcntl_flags);
901 } else {
902 Warn4("connect(%d, %s, "F_Zd"): %s",
903 sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
904 themlen, strerror(errno));
906 } else if (pf == PF_UNIX) {
907 /* this is for UNIX domain sockets: a connect attempt seems to be
908 the only way to distinguish stream and datagram sockets.
909 And no ancillary message expected
911 int _errno = errno;
912 Info4("connect(%d, %s, "F_Zd"): %s",
913 sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
914 themlen, strerror(errno));
915 /* caller must handle this condition */
916 Close(sfd->fd); sfd->fd = -1;
917 errno = _errno;
918 return STAT_RETRYLATER;
919 } else {
920 /* try to find details about error, especially from ICMP */
921 xiogetpacketinfo(sfd, sfd->fd);
923 /* continue mainstream */
924 Msg4(level, "connect(%d, %s, "F_Zd"): %s",
925 sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)),
926 themlen, strerror(errno));
927 Close(sfd->fd);
928 return STAT_RETRYLATER;
930 } else { /* result >= 0 */
931 Notice1("successfully connected from local address %s",
932 sockaddr_info(&la.soa, themlen, infobuff, sizeof(infobuff)));
935 applyopts_fchown(sfd->fd, opts); /* OPT_USER, OPT_GROUP */
936 applyopts(sfd, -1, opts, PH_CONNECTED);
937 #if WITH_UNIX
938 if (pf == PF_UNIX && us != NULL) {
939 applyopts_named(us->un.sun_path, opts, PH_LATE);
941 #endif
942 applyopts(sfd, -1, opts, PH_LATE);
944 return STAT_OK;
948 /* a subroutine that is common to all socket addresses that want to connect
949 to a peer address.
950 might fork.
951 applies and consumes the following option:
952 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT,
953 PH_CONNECTED, PH_LATE,
954 OFUNC_OFFSET,
955 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
956 returns 0 on success.
958 int xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen,
959 struct sockaddr *them, size_t themlen,
960 struct opt *opts, int pf, int socktype, int protocol,
961 bool alt) {
962 bool dofork = false;
963 struct opt *opts0;
964 char infobuff[256];
965 int level;
966 int result;
968 retropt_bool(opts, OPT_FORK, &dofork);
970 opts0 = copyopts(opts, GROUP_ALL);
972 Notice1("opening connection to %s",
973 sockaddr_info(them, themlen, infobuff, sizeof(infobuff)));
975 do { /* loop over retries and forks */
977 #if WITH_RETRY
978 if (sfd->forever || sfd->retry) {
979 level = E_INFO;
980 } else
981 #endif /* WITH_RETRY */
982 level = E_ERROR;
983 result =
984 _xioopen_connect(sfd, us, uslen, them, themlen, opts,
985 pf, socktype, protocol, alt, level);
986 switch (result) {
987 case STAT_OK: break;
988 #if WITH_RETRY
989 case STAT_RETRYLATER:
990 if (sfd->forever || sfd->retry) {
991 --sfd->retry;
992 if (result == STAT_RETRYLATER) {
993 Nanosleep(&sfd->intervall, NULL);
995 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
996 continue;
998 return STAT_NORETRY;
999 #endif /* WITH_RETRY */
1000 default:
1001 return result;
1004 if (dofork) {
1005 xiosetchilddied(); /* set SIGCHLD handler */
1008 #if WITH_RETRY
1009 if (dofork) {
1010 pid_t pid;
1011 int level = E_ERROR;
1012 if (sfd->forever || sfd->retry) {
1013 level = E_WARN; /* most users won't expect a problem here,
1014 so Notice is too weak */
1017 while ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
1018 --sfd->retry;
1019 if (sfd->forever || sfd->retry) {
1020 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
1021 Nanosleep(&sfd->intervall, NULL); continue;
1023 return STAT_RETRYLATER;
1026 if (pid == 0) { /* child process */
1027 break;
1030 /* parent process */
1031 Close(sfd->fd);
1032 /* with and without retry */
1033 Nanosleep(&sfd->intervall, NULL);
1034 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
1035 continue; /* with next socket() bind() connect() */
1036 } else
1037 #endif /* WITH_RETRY */
1039 break;
1041 #if 0
1042 if ((result = _xio_openlate(fd, opts)) < 0)
1043 return result;
1044 #endif
1045 } while (true);
1047 return 0;
1051 /* common to xioopen_udp_sendto, ..unix_sendto, ..rawip
1052 applies and consumes the following option:
1053 PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
1054 OFUNC_OFFSET
1055 OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
1057 int _xioopen_dgram_sendto(/* them is already in xfd->peersa */
1058 union sockaddr_union *us, socklen_t uslen,
1059 struct opt *opts,
1060 int xioflags, xiosingle_t *sfd, groups_t groups,
1061 int pf, int socktype, int ipproto, bool alt) {
1062 int level = E_ERROR;
1063 union sockaddr_union la; socklen_t lalen = sizeof(la);
1064 char infobuff[256];
1066 #if WITH_UNIX
1067 if (pf == PF_UNIX && us != NULL) {
1068 applyopts_named(us->un.sun_path, opts, PH_EARLY);
1070 #endif
1072 if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, level)) < 0) {
1073 return STAT_RETRYLATER;
1076 applyopts_offset(sfd, opts);
1077 applyopts_single(sfd, opts, PH_PASTSOCKET);
1078 applyopts(sfd, -1, opts, PH_PASTSOCKET);
1079 applyopts_single(sfd, opts, PH_FD);
1080 applyopts(sfd, -1, opts, PH_FD);
1082 applyopts_cloexec(sfd->fd, opts);
1084 if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) {
1085 return -1;
1088 /*applyopts(sfd, -1, opts, PH_CONNECT);*/
1090 if (Getsockname(sfd->fd, &la.soa, &lalen) < 0) {
1091 Warn4("getsockname(%d, %p, {%d}): %s",
1092 sfd->fd, &la.soa, lalen, strerror(errno));
1095 applyopts_fchown(sfd->fd, opts);
1096 applyopts(sfd, -1, opts, PH_CONNECTED);
1097 #if WITH_UNIX
1098 if (pf == PF_UNIX && us != NULL) {
1099 applyopts_named(us->un.sun_path, opts, PH_LATE);
1101 #endif
1102 /*0 applyopts(sfd, -1, opts, PH_LATE); */
1104 /* sfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */
1105 Notice1("successfully prepared local socket %s",
1106 sockaddr_info(&la.soa, lalen, infobuff, sizeof(infobuff)));
1108 return STAT_OK;
1112 /* when the recvfrom address (with option fork) receives a packet it keeps this
1113 packet in the IP stacks input queue and forks a sub process. The sub process
1114 then reads this packet for processing its data.
1115 There is a problem because the parent process would find the same packet
1116 again if it calls select()/poll() before the child process has read the
1117 packet.
1118 To solve this problem we implement the following mechanism:
1119 Before forking an unnamed pipe (fifo) is created. The sub process closes the
1120 write side when it has read the packet. The parent process waits until the
1121 read side of the pipe gives EOF and only then continues to listen.
1124 /* waits for incoming packet, checks its source address and port. Depending
1125 on fork option, it may fork a subprocess.
1126 Returns STAT_OK if a packet was accepted; with fork option, this is already in
1127 a new subprocess!
1128 Other return values indicate a problem; this can happen in the master
1129 process or in a subprocess.
1130 This function does not retry. If you need retries, handle this is a
1131 loop in the calling function.
1132 after fork, we set the forever/retry of the child process to 0
1133 applies and consumes the following options:
1134 PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD,
1135 PH_CONNECTED, PH_LATE, PH_LATE2
1136 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap
1137 EINTR is not handled specially.
1139 int _xioopen_dgram_recvfrom(struct single *sfd, int xioflags,
1140 struct sockaddr *us, socklen_t uslen,
1141 struct opt *opts,
1142 int pf, int socktype, int proto, int level) {
1143 char *rangename;
1144 bool dofork = false;
1145 pid_t pid; /* mostly int; only used with fork */
1146 char infobuff[256];
1147 char lisname[256];
1148 bool drop = false; /* true if current packet must be dropped */
1149 int result;
1151 retropt_bool(opts, OPT_FORK, &dofork);
1153 if (dofork) {
1154 if (!(xioflags & XIO_MAYFORK)) {
1155 Error("option fork not allowed here");
1156 return STAT_NORETRY;
1158 sfd->flags |= XIO_DOESFORK;
1161 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1163 if ((sfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) {
1164 return STAT_RETRYLATER;
1167 applyopts(sfd, -1, opts, PH_PASTSOCKET);
1168 /*! applyopts(sfd, -1, opts, PH_FD); */
1170 applyopts_cloexec(sfd->fd, opts);
1172 if (xiobind(sfd, (union sockaddr_union *)us, uslen,
1173 opts, pf, 0, level) < 0) {
1174 return -1;
1177 applyopts(sfd, -1, opts, PH_PASTBIND);
1179 #if WITH_UNIX
1180 if (pf == AF_UNIX && us != NULL) {
1181 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
1182 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
1183 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
1185 #endif /* WITH_UNIX */
1187 #if WITH_IP4 /*|| WITH_IP6*/
1188 switch (proto) {
1189 case IPPROTO_UDP:
1190 #ifdef IPPROTO_UDPLITE
1191 case IPPROTO_UDPLITE:
1192 #endif
1193 if (pf == PF_INET && ((struct sockaddr_in *)us)->sin_port == 0 ||
1194 pf == PF_INET6 && ((struct sockaddr_in6 *)us)->sin6_port == 0) {
1195 struct sockaddr_storage bound;
1196 socklen_t bndlen = sizeof(bound);
1197 char sockbuff[256];
1198 Getsockname(sfd->fd, (struct sockaddr *)&bound, &bndlen);
1199 sockaddr_info((struct sockaddr *)&bound, sizeof(struct sockaddr_storage), sockbuff, sizeof(sockbuff));
1200 Notice1("_xioopen_dgram_recvfrom(): bound to %s", sockbuff);
1203 #endif
1205 /* for generic sockets, this has already been retrieved */
1206 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
1207 if (xioparserange(rangename, pf, &sfd->para.socket.range,
1208 sfd->para.socket.ip.ai_flags)
1209 < 0) {
1210 free(rangename);
1211 return STAT_NORETRY;
1213 free(rangename);
1214 sfd->para.socket.dorange = true;
1217 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1218 xio_retropt_tcpwrap(sfd, opts);
1219 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1221 if (xioparms.logopt == 'm') {
1222 Info("starting recvfrom loop, switching to syslog");
1223 diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y';
1224 } else {
1225 Info("starting recvfrom loop");
1228 if (dofork) {
1229 xiosetchilddied();
1232 while (true) { /* but we only loop if fork option is set */
1233 char peername[256];
1234 union sockaddr_union _peername;
1235 union sockaddr_union _sockname;
1236 union sockaddr_union *pa = &_peername; /* peer address */
1237 union sockaddr_union *la = &_sockname; /* local address */
1238 socklen_t palen = sizeof(_peername); /* peer address size */
1239 char ctrlbuff[1024]; /* ancillary messages */
1240 struct msghdr msgh = {0};
1241 int trigger[2]; /* for socketpair that indicates consumption of packet */
1242 int rc;
1244 socket_init(pf, pa);
1246 if (drop) {
1247 char *dummy[2];
1249 Recv(sfd->fd, dummy, sizeof(dummy), 0);
1250 drop = true;
1253 Info("Recvfrom: Checking/waiting for next packet");
1254 /* loop until select()/poll() returns valid */
1255 do {
1256 struct pollfd readfd;
1257 /*? int level = E_ERROR;*/
1258 if (us != NULL) {
1259 Notice1("receiving on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
1260 } else {
1261 Notice1("receiving IP protocol %u", proto);
1263 readfd.fd = sfd->fd;
1264 readfd.events = POLLIN;
1265 if (xiopoll(&readfd, 1, NULL) > 0) {
1266 break;
1269 if (errno == EINTR) {
1270 continue;
1273 Msg2(level, "poll({%d,,},,-1): %s", sfd->fd, strerror(errno));
1274 Close(sfd->fd);
1275 return STAT_RETRYLATER;
1276 } while (true);
1278 msgh.msg_name = pa;
1279 msgh.msg_namelen = palen;
1280 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
1281 msgh.msg_control = ctrlbuff;
1282 #endif
1283 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
1284 msgh.msg_controllen = sizeof(ctrlbuff);
1285 #endif
1286 while ((rc = xiogetancillary(sfd->fd,
1287 &msgh,
1288 MSG_PEEK
1289 #ifdef MSG_TRUNC
1290 |MSG_TRUNC
1291 #endif
1292 )) < 0 &&
1293 errno == EINTR) ;
1294 if (rc < 0) return STAT_RETRYLATER;
1295 palen = msgh.msg_namelen;
1297 Notice1("receiving packet from %s"/*"src"*/,
1298 sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*,
1299 sockaddr_info(&la->soa, sockname, sizeof(sockname))*/);
1301 xiodopacketinfo(sfd, &msgh, true, true);
1303 if (xiocheckpeer(sfd, pa, la) < 0) {
1304 /* drop packet */
1305 char buff[512];
1306 Recv(sfd->fd, buff, sizeof(buff), 0);
1307 continue;
1309 Info1("permitting packet from %s",
1310 sockaddr_info(&pa->soa, palen,
1311 infobuff, sizeof(infobuff)));
1313 /* set the env vars describing the local and remote sockets */
1314 /*xiosetsockaddrenv("SOCK", la, lalen, proto);*/
1315 xiosetsockaddrenv("PEER", pa, palen, proto);
1317 applyopts(sfd, -1, opts, PH_FD);
1319 applyopts(sfd, -1, opts, PH_CONNECTED);
1321 sfd->peersa = *(union sockaddr_union *)pa;
1322 sfd->salen = palen;
1324 if (dofork) {
1325 Info("Generating socketpair that triggers parent when packet has been consumed");
1326 if (Socketpair(PF_UNIX, SOCK_STREAM, 0, trigger) < 0) {
1327 Error1("socketpair(PF_UNIX, SOCK_STREAM, 0, ...): %s", strerror(errno));
1330 if ((pid = xio_fork(false, level, sfd->shutup)) < 0) {
1331 Close(trigger[0]);
1332 Close(trigger[1]);
1333 Close(sfd->fd);
1334 return STAT_RETRYLATER;
1337 if (pid == 0) { /* child */
1338 Close(trigger[0]);
1339 sfd->triggerfd = trigger[1];
1340 Fcntl_l(sfd->triggerfd, F_SETFD, FD_CLOEXEC);
1342 #if WITH_RETRY
1343 /* !? */
1344 sfd->retry = 0;
1345 sfd->forever = 0;
1346 level = E_ERROR;
1347 #endif /* WITH_RETRY */
1349 #if WITH_UNIX
1350 /* with UNIX sockets: only listening parent is allowed to remove
1351 the socket file */
1352 sfd->opt_unlink_close = false;
1353 #endif /* WITH_UNIX */
1355 break;
1358 /* Parent */
1359 Close(trigger[1]);
1362 char buf[1];
1363 while (Read(trigger[0], buf, 1) < 0 && errno == EINTR) ;
1366 Info("continue listening");
1367 } else {
1368 break;
1371 if ((result = _xio_openlate(sfd, opts)) != 0)
1372 return STAT_NORETRY;
1374 return STAT_OK;
1378 /* returns STAT_* */
1379 int _xioopen_dgram_recv(struct single *sfd, int xioflags,
1380 struct sockaddr *us, socklen_t uslen,
1381 struct opt *opts, int pf, int socktype, int proto,
1382 int level) {
1383 char *rangename;
1385 if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1387 if ((sfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) {
1388 return STAT_RETRYLATER;
1391 applyopts(sfd, -1, opts, PH_PASTSOCKET);
1392 /*! applyopts(sfd, -1, opts, PH_FD); */
1394 applyopts_cloexec(sfd->fd, opts);
1396 if (xiobind(sfd, (union sockaddr_union *)us, uslen, opts, pf, 0, level) < 0) {
1397 return -1;
1400 #if WITH_UNIX
1401 if (pf == AF_UNIX && us != NULL) {
1402 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
1403 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
1404 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
1406 #endif /* WITH_UNIX */
1408 #if WITH_IP4 /*|| WITH_IP6*/
1409 switch (proto) {
1410 case IPPROTO_UDP:
1411 #ifdef IPPROTO_UDPLITE
1412 case IPPROTO_UDPLITE:
1413 #endif
1414 if (pf == PF_INET && ((struct sockaddr_in *)us)->sin_port == 0 ||
1415 pf == PF_INET6 && ((struct sockaddr_in6 *)us)->sin6_port == 0) {
1416 struct sockaddr_storage bound;
1417 socklen_t bndlen = sizeof(bound);
1418 char sockbuff[256];
1419 Getsockname(sfd->fd, (struct sockaddr *)&bound, &bndlen);
1420 sockaddr_info((struct sockaddr *)&bound, sizeof(struct sockaddr_storage), sockbuff, sizeof(sockbuff));
1421 Notice1("_xioopen_dgram_recv(): bound to %s", sockbuff);
1424 #endif
1426 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
1427 if (xioparserange(rangename, pf, &sfd->para.socket.range,
1428 sfd->para.socket.ip.ai_flags)
1429 < 0) {
1430 free(rangename);
1431 return STAT_NORETRY;
1433 free(rangename);
1434 sfd->para.socket.dorange = true;
1437 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1438 xio_retropt_tcpwrap(sfd, opts);
1439 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1441 if (xioparms.logopt == 'm') {
1442 Info("starting recv loop, switching to syslog");
1443 diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y';
1444 } else {
1445 Info("starting recv loop");
1448 return STAT_OK;
1452 int retropt_socket_pf(struct opt *opts, int *pf) {
1453 char *pfname;
1455 if (retropt_string(opts, OPT_PROTOCOL_FAMILY, &pfname) >= 0) {
1456 if (isdigit((unsigned char)pfname[0])) {
1457 *pf = strtoul(pfname, NULL /*!*/, 0);
1458 #if WITH_IP4
1459 } else if (!strcasecmp("inet", pfname) ||
1460 !strcasecmp("inet4", pfname) ||
1461 !strcasecmp("ip4", pfname) ||
1462 !strcasecmp("ipv4", pfname) ||
1463 !strcasecmp("2", pfname)) {
1464 *pf = PF_INET;
1465 #endif /* WITH_IP4 */
1466 #if WITH_IP6
1467 } else if (!strcasecmp("inet6", pfname) ||
1468 !strcasecmp("ip6", pfname) ||
1469 !strcasecmp("ipv6", pfname) ||
1470 !strcasecmp("10", pfname)) {
1471 *pf = PF_INET6;
1472 #endif /* WITH_IP6 */
1473 } else {
1474 Error1("unknown protocol family \"%s\"", pfname);
1475 /*! Warn("falling back to INET");*/
1477 free(pfname);
1478 return 0;
1480 return -1;
1484 /* This function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
1485 the arriving packet, thus it does not "consume" the packet.
1486 In msgh the msg_name pointer must refer to an (empty) sockaddr storage.
1487 Returns STAT_OK on success, or STAT_RETRYLATER when an error occurred,
1488 including EINTR.
1489 (recvmsg() retrieves just meta info, not the data)
1491 int xiogetancillary(int fd, struct msghdr *msgh, int flags) {
1492 char peekbuff[1];
1493 #if HAVE_STRUCT_IOVEC
1494 struct iovec iovec;
1495 #endif
1497 #if HAVE_STRUCT_IOVEC
1498 iovec.iov_base = peekbuff;
1499 iovec.iov_len = sizeof(peekbuff);
1500 msgh->msg_iov = &iovec;
1501 msgh->msg_iovlen = 1;
1502 #endif
1503 #if HAVE_STRUCT_MSGHDR_MSGFLAGS
1504 msgh->msg_flags = 0;
1505 #endif
1506 if (Recvmsg(fd, msgh, flags) < 0) {
1507 Info1("recvmsg(): %s", strerror(errno));
1508 return STAT_RETRYLATER;
1510 return STAT_OK;
1514 /* works through the ancillary messages found in the given socket header record
1515 and logs the relevant information (E_DEBUG, E_INFO).
1516 calls protocol/layer specific functions for handling the messages
1517 creates appropriate environment vars if withenv is set */
1518 int xiodopacketinfo(
1519 struct single *sfd,
1520 struct msghdr *msgh,
1521 bool withlog,
1522 bool withenv)
1524 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
1525 struct cmsghdr *cmsg;
1527 /* parse ancillary messages */
1528 cmsg = CMSG_FIRSTHDR(msgh);
1529 while (cmsg != NULL) {
1530 int num = 0; /* number of data components of a ancill.msg */
1531 int i;
1532 char typbuff[16], *typp;
1533 char nambuff[128], *namp;
1534 char valbuff[256], *valp;
1535 char envbuff[256], *envp;
1537 Info3("ancillary message in xiodopacketinfo(): len="F_Zu", level=%d, type=%d",
1538 cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
1539 if (withlog) {
1540 xiodump(CMSG_DATA(cmsg),
1541 cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
1542 valbuff, sizeof(valbuff)-1, 0);
1543 Debug4("ancillary message: len="F_cmsg_len", level=%d, type=%d, data=%s",
1544 cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type,
1545 valbuff);
1548 /* try to get the anc.msg. contents in handy components, protocol/level
1549 dependent */
1550 switch (cmsg->cmsg_level) {
1551 case SOL_SOCKET:
1552 xiolog_ancillary_socket(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1,
1553 nambuff, sizeof(nambuff)-1,
1554 envbuff, sizeof(envbuff)-1,
1555 valbuff, sizeof(valbuff)-1);
1556 break;
1557 #if WITH_IP4 || WITH_IP6
1558 case SOL_IP:
1559 xiolog_ancillary_ip(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1,
1560 nambuff, sizeof(nambuff)-1,
1561 envbuff, sizeof(envbuff)-1,
1562 valbuff, sizeof(valbuff)-1);
1563 break;
1564 #endif /* WITH_IP4 || WITH_IP6 */
1565 #if WITH_IP6
1566 case SOL_IPV6:
1567 xiolog_ancillary_ip6(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1,
1568 nambuff, sizeof(nambuff)-1,
1569 envbuff, sizeof(envbuff)-1,
1570 valbuff, sizeof(valbuff)-1);
1571 break;
1572 #endif /* WITH_IP6 */
1573 #if _WITH_INTERFACE && HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA
1574 case SOL_PACKET:
1575 xiolog_ancillary_packet(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1,
1576 nambuff, sizeof(nambuff)-1,
1577 envbuff, sizeof(envbuff)-1,
1578 valbuff, sizeof(valbuff)-1);
1579 break;
1580 #endif /* HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA */
1581 default:
1582 num = 1;
1583 snprintf(typbuff, sizeof(typbuff)-1, "LEVEL%u", cmsg->cmsg_level);
1584 snprintf(nambuff, sizeof(nambuff)-1, "type%u", cmsg->cmsg_type);
1585 xiodump(CMSG_DATA(cmsg),
1586 cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg),
1587 valbuff, sizeof(valbuff)-1, 0);
1589 /* here the info is in typbuff (one string), nambuff (num consecutive
1590 strings), and valbuff (num consecutive strings) */
1591 i = 0;
1592 typp = typbuff; namp = nambuff; envp = envbuff; valp = valbuff;
1593 while (i < num) {
1594 if (withlog) {
1595 Info3("ancillary message: %s: %s=%s", typp, namp, valp);
1597 if (withenv) {
1598 if (*envp) {
1599 xiosetenv(envp, valp, 1, NULL);
1600 } else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) {
1601 xiosetenv(typp, valp, 1, NULL);
1602 } else {
1603 xiosetenv2(typp, namp, valp, 1, NULL);
1606 if (++i == num) break;
1607 namp = strchr(namp, '\0')+1;
1608 envp = strchr(envp, '\0')+1;
1609 valp = strchr(valp, '\0')+1;
1611 cmsg = CMSG_NXTHDR(msgh, cmsg);
1613 return 0;
1614 #else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
1615 return -1;
1616 #endif /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
1620 /* check if peer address is within permitted range.
1621 return >= 0 if so. */
1622 int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) {
1623 switch (sa->soa.sa_family) {
1624 #if WITH_IP4
1625 case PF_INET:
1626 return
1627 xiocheckrange_ip4(&sa->ip4, range);
1628 #endif /* WITH_IP4 */
1629 #if WITH_IP6
1630 case PF_INET6:
1631 return
1632 xiocheckrange_ip6(&sa->ip6, range);
1633 #endif /* WITH_IP6 */
1634 #if 0
1635 case PF_UNSPEC:
1637 socklen_t i;
1638 for (i = 0; i < sizeof(sa->soa.sa_data); ++i) {
1639 if ((range->netmask.soa.sa_data[i] & sa->soa.sa_data[i]) != range->netaddr.soa.sa_data[i]) {
1640 return -1;
1643 return 0;
1645 #endif
1647 return -1;
1650 int xiocheckpeer(xiosingle_t *sfd,
1651 union sockaddr_union *pa, union sockaddr_union *la) {
1652 char infobuff[256];
1653 int result;
1655 #if WITH_IP4
1656 if (sfd->para.socket.dorange) {
1657 if (pa == NULL) { return -1; }
1658 if (xiocheckrange(pa, &sfd->para.socket.range) < 0) {
1659 char infobuff[256];
1660 Warn1("refusing connection from %s due to range option",
1661 sockaddr_info(&pa->soa, 0,
1662 infobuff, sizeof(infobuff)));
1663 return -1;
1665 Info1("permitting connection from %s due to range option",
1666 sockaddr_info(&pa->soa, 0,
1667 infobuff, sizeof(infobuff)));
1669 #endif /* WITH_IP4 */
1671 #if WITH_TCP || WITH_UDP
1672 if (sfd->para.socket.ip.dosourceport) {
1673 if (pa == NULL) { return -1; }
1674 #if WITH_IP4
1675 if (pa->soa.sa_family == AF_INET &&
1676 ntohs(((struct sockaddr_in *)pa)->sin_port) != sfd->para.socket.ip.sourceport) {
1677 Warn1("refusing connection from %s due to wrong sourceport",
1678 sockaddr_info(&pa->soa, 0,
1679 infobuff, sizeof(infobuff)));
1680 return -1;
1682 #endif /* WITH_IP4 */
1683 #if WITH_IP6
1684 if (pa->soa.sa_family == AF_INET6 &&
1685 ntohs(((struct sockaddr_in6 *)pa)->sin6_port) != sfd->para.socket.ip.sourceport) {
1686 Warn1("refusing connection from %s due to wrong sourceport",
1687 sockaddr_info(&pa->soa, 0,
1688 infobuff, sizeof(infobuff)));
1689 return -1;
1691 #endif /* WITH_IP6 */
1692 Info1("permitting connection from %s due to sourceport option",
1693 sockaddr_info(&pa->soa, 0,
1694 infobuff, sizeof(infobuff)));
1695 } else if (sfd->para.socket.ip.lowport) {
1696 if (pa == NULL) { return -1; }
1697 if (pa->soa.sa_family == AF_INET &&
1698 ntohs(((struct sockaddr_in *)pa)->sin_port) >= IPPORT_RESERVED) {
1699 Warn1("refusing connection from %s due to lowport option",
1700 sockaddr_info(&pa->soa, 0,
1701 infobuff, sizeof(infobuff)));
1702 return -1;
1704 #if WITH_IP6
1705 else if (pa->soa.sa_family == AF_INET6 &&
1706 ntohs(((struct sockaddr_in6 *)pa)->sin6_port) >=
1707 IPPORT_RESERVED) {
1708 Warn1("refusing connection from %s due to lowport option",
1709 sockaddr_info(&pa->soa, 0,
1710 infobuff, sizeof(infobuff)));
1711 return -1;
1713 #endif /* WITH_IP6 */
1714 Info1("permitting connection from %s due to lowport option",
1715 sockaddr_info(&pa->soa, 0,
1716 infobuff, sizeof(infobuff)));
1718 #endif /* WITH_TCP || WITH_UDP */
1720 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
1721 result = xio_tcpwrap_check(sfd, la, pa);
1722 if (result < 0) {
1723 char infobuff[256];
1724 Warn1("refusing connection from %s due to tcpwrapper option",
1725 sockaddr_info(&pa->soa, 0,
1726 infobuff, sizeof(infobuff)));
1727 return -1;
1728 } else if (result > 0) {
1729 Info1("permitting connection from %s due to tcpwrapper option",
1730 sockaddr_info(&pa->soa, 0,
1731 infobuff, sizeof(infobuff)));
1733 #endif /* (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
1735 return 0; /* permitted */
1739 #if HAVE_STRUCT_CMSGHDR
1740 /* converts the ancillary message in *cmsg into a form useable for further
1741 processing. knows the specifics of common message types.
1742 returns the number of resulting syntax elements in *num
1743 returns a sequence of \0 terminated type strings in *typbuff
1744 returns a sequence of \0 terminated name strings in *nambuff
1745 returns a sequence of \0 terminated value strings in *valbuff
1746 the respective len parameters specify the available space in the buffers
1747 returns STAT_OK or other STAT_*
1749 static int
1750 xiolog_ancillary_socket(
1751 struct single *sfd,
1752 struct cmsghdr *cmsg,
1753 int *num,
1754 char *typbuff, int typlen,
1755 char *nambuff, int namlen,
1756 char *envbuff, int envlen,
1757 char *valbuff, int vallen)
1759 const char *cmsgtype, *cmsgname, *cmsgenvn;
1760 size_t msglen;
1761 struct timeval *tv;
1762 int rc = STAT_OK;
1764 #if defined(CMSG_DATA)
1766 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
1767 switch (cmsg->cmsg_type) {
1768 #ifdef SO_PASSCRED
1769 case SO_PASSCRED: /* this is really a UNIX/LOCAL message */
1770 /*! needs implementation */
1771 #endif /* SO_PASSCRED */
1772 #ifdef SO_RIGHTS
1773 case SO_RIGHTS: /* this is really a UNIX/LOCAL message */
1774 /*! needs implementation */
1775 #endif
1776 default: /* binary data */
1777 snprintf(typbuff, typlen, "SOCKET.%u", cmsg->cmsg_type);
1778 nambuff[0] = '\0'; strncat(nambuff, "data", namlen-1);
1779 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
1780 return STAT_OK;
1781 #ifdef SO_TIMESTAMP
1782 # ifdef SCM_TIMESTAMP
1783 case SCM_TIMESTAMP:
1784 # else
1785 case SO_TIMESTAMP:
1786 # endif
1787 tv = (struct timeval *)CMSG_DATA(cmsg);
1788 cmsgtype =
1789 #ifdef SCM_TIMESTAMP
1790 "SCM_TIMESTAMP" /* FreeBSD */
1791 #else
1792 "SO_TIMESTAMP" /* Linux */
1793 #endif
1795 cmsgname = "timestamp";
1796 cmsgenvn = "TIMESTAMP";
1797 { time_t t = tv->tv_sec; ctime_r(&t, valbuff); }
1798 snprintf(strchr(valbuff, '\0')-1/*del \n*/, vallen-strlen(valbuff)+1, ", %06ld usecs", (long)tv->tv_usec);
1799 break;
1800 #endif /* defined(SO_TIMESTAMP) */
1803 /* when we come here we provide a single parameter
1804 with type in cmsgtype, name in cmsgname,
1805 and value already in valbuff */
1806 *num = 1;
1807 if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING;
1808 typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1);
1809 if (strlen(cmsgname) >= namlen) rc = STAT_WARNING;
1810 nambuff[0] = '\0'; strncat(nambuff, cmsgname, namlen-1);
1811 if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING;
1812 envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1);
1813 return rc;
1815 #else /* !defined(CMSG_DATA) */
1817 return STAT_NORETRY;
1819 #endif /* !defined(CMSG_DATA) */
1821 #endif /* HAVE_STRUCT_CMSGHDR */
1824 /* return the name of the interface with given index
1825 or NULL if is fails
1826 The system call requires an arbitrary socket; the calling program may
1827 provide one in parameter ins to avoid creation of a dummy socket. ins must
1828 be <0 if it does not specify a socket fd. */
1829 char *xiogetifname(int ind, char *val, int ins) {
1830 #if !HAVE_PROTOTYPE_LIB_if_indextoname
1831 int s;
1832 struct ifreq ifr;
1834 if (ins >= 0) {
1835 s = ins;
1836 } else {
1837 if ((s = Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
1838 Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno));
1839 return NULL;
1843 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1844 ifr.ifr_index = ind;
1845 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1846 ifr.ifr_ifindex = ind;
1847 #endif
1848 #ifdef SIOCGIFNAME
1849 if(Ioctl(s, SIOCGIFNAME, &ifr) < 0) {
1850 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1851 Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_index=%d, ...}: %s",
1852 s, ifr.ifr_index, strerror(errno));
1853 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1854 Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_ifindex=%d, ...}: %s",
1855 s, ifr.ifr_ifindex, strerror(errno));
1856 #endif
1857 if (ins < 0) Close(s);
1858 return NULL;
1860 #endif /* SIOCGIFNAME */
1861 if (ins < 0) Close(s);
1862 strcpy(val, ifr.ifr_name);
1863 return val;
1864 #else /* HAVE_PROTOTYPE_LIB_if_indextoname */
1865 return if_indextoname(ind, val);
1866 #endif /* HAVE_PROTOTYPE_LIB_if_indextoname */
1870 /* parses a network specification consisting of an address and a mask. */
1871 int xioparsenetwork(
1872 const char *rangename,
1873 int pf,
1874 struct xiorange *range,
1875 const int ai_flags[2])
1877 size_t addrlen = 0, masklen = 0;
1878 int result;
1880 switch (pf) {
1881 #if WITH_IP4
1882 case PF_INET:
1883 return xioparsenetwork_ip4(rangename, range, ai_flags);
1884 break;
1885 #endif /* WITH_IP4 */
1886 #if WITH_IP6
1887 case PF_INET6:
1888 return xioparsenetwork_ip6(rangename, range, ai_flags);
1889 break;
1890 #endif /* WITH_IP6 */
1891 case PF_UNSPEC:
1893 char *addrname;
1894 const char *maskname;
1895 if ((maskname = strchr(rangename, ':')) == NULL) {
1896 Error1("syntax error in range \"%s\" of unspecified address family: use <addr>:<mask>", rangename);
1897 return STAT_NORETRY;
1899 ++maskname; /* skip ':' */
1900 if ((addrname = Malloc(maskname-rangename)) == NULL) {
1901 return STAT_NORETRY;
1903 strncpy(addrname, rangename, maskname-rangename-1); /* ok */
1904 addrname[maskname-rangename-1] = '\0';
1905 result =
1906 dalan(addrname, (uint8_t *)&range->netaddr.soa.sa_data, &addrlen,
1907 sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
1908 /* data length */, 'i');
1909 if (result < 0) {
1910 Error1("data too long: \"%s\"", addrname);
1911 free(addrname); return STAT_NORETRY;
1912 } else if (result > 0) {
1913 Error1("syntax error in \"%s\"", addrname);
1914 free(addrname); return STAT_NORETRY;
1916 free(addrname);
1917 result =
1918 dalan(maskname, (uint8_t *)&range->netmask.soa.sa_data, &masklen,
1919 sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data)
1920 /* data length */, 'i');
1921 if (result < 0) {
1922 Error1("data too long: \"%s\"", maskname);
1923 return STAT_NORETRY;
1924 } else if (result > 0) {
1925 Error1("syntax error in \"%s\"", maskname);
1926 return STAT_NORETRY;
1928 if (addrlen != masklen) {
1929 Error2("network address is "F_Zu" bytes long, mask is "F_Zu" bytes long",
1930 addrlen, masklen);
1931 /* recover by padding the shorter component with 0 */
1932 memset((char *)&range->netaddr.soa.sa_data+addrlen, 0,
1933 MAX(0, addrlen-masklen));
1934 memset((char *)&range->netmask.soa.sa_data+masklen, 0,
1935 MAX(0, masklen-addrlen));
1938 break;
1939 default:
1940 Error1("range option not supported with address family %d", pf);
1941 return STAT_NORETRY;
1943 return STAT_OK;
1947 /* parses a string of form address/bits or address:mask, and fills the fields
1948 of the range union. The addr component is masked with mask. */
1949 int xioparserange(
1950 const char *rangename,
1951 int pf,
1952 struct xiorange *range,
1953 const int ai_flags[2])
1955 int i;
1956 if (xioparsenetwork(rangename, pf, range, ai_flags) < 0) {
1957 Error2("failed to parse or resolve range \"%s\" (pf=%d)", rangename, pf);
1958 return -1;
1960 /* we have parsed the address and mask; now we make sure that the stored
1961 address has 0 where mask is 0, to simplify comparisions */
1962 switch (pf) {
1963 #if WITH_IP4
1964 case PF_INET:
1965 range->netaddr.ip4.sin_addr.s_addr &= range->netmask.ip4.sin_addr.s_addr;
1966 break;
1967 #endif /* WITH_IP4 */
1968 #if WITH_IP6
1969 case PF_INET6:
1970 return xiorange_ip6andmask(range);
1971 break;
1972 #endif /* WITH_IP6 */
1973 case PF_UNSPEC:
1974 for (i = 0; i < sizeof(range->netaddr); ++i) {
1975 ((char *)&range->netaddr)[i] &= ((char *)&range->netmask)[i];
1977 break;
1978 default:
1979 Error1("range option not supported with address family %d", pf);
1980 return STAT_NORETRY;
1982 return 0;
1986 /* set environment variables describing (part of) a socket address, e.g.
1987 SOCAT_SOCKADDR. lr (local/remote) specifies a string like "SOCK" or "PEER".
1988 proto should correspond to the third parameter of socket(2) and is used to
1989 determine the presence of port information. */
1990 int xiosetsockaddrenv(const char *lr,
1991 union sockaddr_union *sau, socklen_t salen,
1992 int proto) {
1993 # define XIOSOCKADDRENVLEN 256
1994 char namebuff[XIOSOCKADDRENVLEN];
1995 char valuebuff[XIOSOCKADDRENVLEN];
1996 int idx = 0, result;
1998 strcpy(namebuff, lr);
1999 switch (sau->soa.sa_family) {
2000 #if WITH_UNIX
2001 case PF_UNIX:
2002 result =
2003 xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2004 valuebuff, XIOSOCKADDRENVLEN,
2005 &sau->un, salen, proto);
2006 xiosetenv(namebuff, valuebuff, 1, NULL);
2007 break;
2008 #endif /* WITH_UNIX */
2009 #if WITH_IP4
2010 case PF_INET:
2011 do {
2012 result =
2013 xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2014 valuebuff, XIOSOCKADDRENVLEN,
2015 &sau->ip4, proto);
2016 xiosetenv(namebuff, valuebuff, 1, NULL);
2017 namebuff[strlen(lr)] = '\0'; ++idx;
2018 } while (result > 0);
2019 break;
2020 #endif /* WITH_IP4 */
2021 #if WITH_IP6
2022 case PF_INET6:
2023 strcpy(namebuff, lr);
2024 do {
2025 result =
2026 xiosetsockaddrenv_ip6(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2027 valuebuff, XIOSOCKADDRENVLEN,
2028 &sau->ip6, proto);
2029 xiosetenv(namebuff, valuebuff, 1, NULL);
2030 namebuff[strlen(lr)] = '\0'; ++idx;
2031 } while (result > 0);
2032 break;
2033 #endif /* WITH_IP6 */
2034 #if WITH_VSOCK
2035 case PF_VSOCK:
2036 strcpy(namebuff, lr);
2037 do {
2038 result =
2039 xiosetsockaddrenv_vsock(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
2040 valuebuff, XIOSOCKADDRENVLEN,
2041 &sau->vm, proto);
2042 xiosetenv(namebuff, valuebuff, 1, NULL);
2043 namebuff[strlen(lr)] = '\0'; ++idx;
2044 } while (result > 0);
2045 break;
2046 #endif /* WITH_VSOCK */
2047 #if LATER
2048 case PF_PACKET:
2049 result = xiosetsockaddrenv_packet(lr, (void *)sau, proto); break;
2050 #endif
2051 default:
2052 result = -1;
2053 break;
2055 return result;
2056 # undef XIOSOCKADDRENVLEN
2059 #endif /* _WITH_SOCKET */
2061 /* these do sockets internally */
2063 /* retrieves options so-type and so-prototype from opts, calls socket, and
2064 ev. generates an appropriate error message.
2065 returns 0 on success or -1 if an error occurred. */
2067 xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
2068 int result;
2070 retropt_int(opts, OPT_SO_TYPE, &socktype);
2071 retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
2072 applyopts(NULL, -1, opts, PH_PRESOCKET);
2073 result = Socket(pf, socktype, proto);
2074 if (result < 0) {
2075 int _errno = errno;
2076 Msg4(msglevel, "socket(%d, %d, %d): %s",
2077 pf, socktype, proto, strerror(errno));
2078 errno = _errno;
2079 return -1;
2081 return result;
2084 /* retrieves options so-type and so-prototype from opts, calls socketpair, and
2085 ev. generates an appropriate error message.
2086 returns 0 on success or -1 if an error occurred. */
2088 xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]) {
2089 int result;
2091 retropt_int(opts, OPT_SO_TYPE, &socktype);
2092 retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
2093 result = Socketpair(pf, socktype, proto, sv);
2094 if (result < 0) {
2095 Error5("socketpair(%d, %d, %d, %p): %s",
2096 pf, socktype, proto, sv, strerror(errno));
2097 return -1;
2099 return result;
2102 /* Binds a socket to a socket address. Handles IP (internet protocol), UNIX
2103 domain, Linux abstract UNIX domain.
2104 The bind address us may be NULL in which case no bind() happens, except with
2105 alt (on option unix-bind-tempname (bind-tempname)).
2106 Alternate (atl) bind semantics are:
2107 with IP sockets: lowport (selects randomly a free port from 640 to 1023)
2108 with UNIX and abstract sockets: uses a method similar to tmpname() to
2109 find a free file system entry.
2111 int xiobind(
2112 struct single *sfd,
2113 union sockaddr_union *us,
2114 size_t uslen,
2115 struct opt *opts,
2116 int pf,
2117 bool alt,
2118 int level)
2120 char infobuff[256];
2121 int result;
2123 if (false /* for canonical reasons */) {
2125 #if WITH_UNIX
2126 } else if (pf == PF_UNIX) {
2127 if (alt && us != NULL) {
2128 bool abstract = false;
2129 char *usrname = NULL, *sockname;
2131 #if WITH_ABSTRACT_UNIXSOCKET
2132 abstract = (us->un.sun_path[0] == '\0');
2133 #endif
2135 if (uslen == ((char *)&us->un.sun_path-(char *)us)) {
2136 usrname = NULL;
2137 } else {
2138 #if WITH_ABSTRACT_UNIXSOCKET
2139 if (abstract)
2140 usrname = strndup(us->un.sun_path+1, sizeof(us->un.sun_path)-1);
2141 else
2142 #endif
2143 usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path));
2144 if (usrname == NULL) {
2145 int _errno = errno;
2146 Error2("strndup(\"%s\", "F_Zu"): out of memory",
2147 us->un.sun_path, sizeof(us->un.sun_path));
2148 errno = _errno;
2149 return -1;
2153 do { /* loop over tempnam bind() attempts */
2154 sockname = xio_tempnam(usrname, abstract);
2155 if (sockname == NULL) {
2156 Error2("tempnam(\"%s\"): %s", usrname, strerror(errno));
2157 free(usrname);
2158 return -1;
2160 strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path));
2161 uslen = sizeof(&((struct sockaddr_un *)0)->sun_path) +
2162 Min(strlen(sockname), sizeof(us->un.sun_path)); /*?*/
2163 free(sockname);
2165 if (Bind(sfd->fd, (struct sockaddr *)us, uslen) < 0) {
2166 Msg4(errno==EADDRINUSE?E_INFO:level, "bind(%d, {%s}, "F_Zd"): %s",
2167 sfd->fd, sockaddr_info((struct sockaddr *)us, uslen,
2168 infobuff, sizeof(infobuff)),
2169 uslen, strerror(errno));
2170 if (errno != EADDRINUSE) {
2171 free(usrname);
2172 Close(sfd->fd);
2173 return STAT_RETRYLATER;
2175 } else {
2176 break; /* could bind to path, good, continue past loop */
2178 } while (true);
2179 free(usrname);
2180 applyopts_named(us->un.sun_path, opts, PH_PREOPEN);
2181 } else
2183 if (us != NULL) {
2184 if (Bind(sfd->fd, &us->soa, uslen) < 0) {
2185 Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
2186 sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
2187 uslen, strerror(errno));
2188 Close(sfd->fd);
2189 return STAT_RETRYLATER;
2191 applyopts_named(us->un.sun_path, opts, PH_PREOPEN);
2194 applyopts(sfd, sfd->fd, opts, PH_PREBIND);
2195 applyopts(sfd, sfd->fd, opts, PH_BIND);
2196 #endif /* WITH_UNIX */
2198 #if WITH_TCP || WITH_UDP
2199 } else if (alt) {
2200 union sockaddr_union sin, *sinp;
2201 unsigned short *port, i, N;
2202 div_t dv;
2204 applyopts(sfd, sfd->fd, opts, PH_PREBIND);
2205 applyopts(sfd, sfd->fd, opts, PH_BIND);
2206 /* prepare sockaddr for bind probing */
2207 if (us) {
2208 sinp = us;
2209 } else {
2210 if (pf == AF_INET) {
2211 socket_in_init(&sin.ip4);
2212 #if WITH_IP6
2213 } else {
2214 socket_in6_init(&sin.ip6);
2215 #endif
2217 sinp = &sin;
2219 if (pf == AF_INET) {
2220 port = &sin.ip4.sin_port;
2221 #if WITH_IP6
2222 } else if (pf == AF_INET6) {
2223 port = &sin.ip6.sin6_port;
2224 #endif
2225 } else {
2226 port = 0; /* just to make compiler happy */
2228 /* combine random+step variant to quickly find a free port when only
2229 few are in use, and certainly find a free port in defined time even
2230 if there are almost all in use */
2231 /* dirt 1: having tcp/udp code in socket function */
2232 /* dirt 2: using a time related system call for init of random */
2234 /* generate a random port, with millisecond random init */
2235 #if 0
2236 struct timeb tb;
2237 ftime(&tb);
2238 srandom(tb.time*1000+tb.millitm);
2239 #else
2240 struct timeval tv;
2241 struct timezone tz;
2242 tz.tz_minuteswest = 0;
2243 tz.tz_dsttime = 0;
2244 if ((result = Gettimeofday(&tv, &tz)) < 0) {
2245 Warn2("gettimeofday(%p, {0,0}): %s", &tv, strerror(errno));
2247 srandom(tv.tv_sec*1000000+tv.tv_usec);
2248 #endif
2250 /* Note: IPPORT_RESERVED is from includes, 1024 */
2251 dv = div(random(), IPPORT_RESERVED-XIO_IPPORT_LOWER);
2252 i = N = XIO_IPPORT_LOWER + dv.rem;
2253 do { /* loop over lowport bind() attempts */
2254 *port = htons(i);
2255 if (Bind(sfd->fd, &sinp->soa, sizeof(*sinp)) < 0) {
2256 Msg4(errno==EADDRINUSE?E_INFO:level,
2257 "bind(%d, {%s}, "F_Zd"): %s", sfd->fd,
2258 sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)),
2259 sizeof(*sinp), strerror(errno));
2260 if (errno != EADDRINUSE) {
2261 Close(sfd->fd);
2262 return STAT_RETRYLATER;
2264 } else {
2265 break; /* could bind to port, good, continue past loop */
2267 --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1;
2268 if (i == N) {
2269 Msg(level, "no low port available");
2270 /*errno = EADDRINUSE; still assigned */
2271 Close(sfd->fd);
2272 return STAT_RETRYLATER;
2274 } while (i != N);
2275 #endif /* WITH_TCP || WITH_UDP */
2277 } else {
2278 applyopts(sfd, sfd->fd, opts, PH_PREBIND);
2279 if (us) {
2280 applyopts(sfd, sfd->fd, opts, PH_BIND);
2281 if (Bind(sfd->fd, &us->soa, uslen) < 0) {
2282 Msg4(level, "bind(%d, {%s}, "F_Zd"): %s",
2283 sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
2284 uslen, strerror(errno));
2285 Close(sfd->fd);
2286 return STAT_RETRYLATER;
2291 applyopts(sfd, -1, opts, PH_PASTBIND);
2292 return 0;
2295 /* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on
2296 Socat option so-reuseaddr:
2297 Option not applied: set it to 1
2298 Option applied with a value: set it to the value
2299 Option applied eith empty value "so-reuseaddr=": do not call setsockopt() for
2300 SO_REUSEADDR
2301 Return 0 on success, or -1 with errno when an error occurred.
2303 int xiosock_reuseaddr(int fd, int ipproto, struct opt *opts)
2305 union integral val;
2306 union integral notnull;
2307 int _errno;
2309 val.u_int = 0;
2310 notnull.u_bool = false;
2311 if (ipproto == IPPROTO_TCP) {
2312 val.u_int = 1;
2313 notnull.u_bool = true;
2315 retropt_2integrals(opts, OPT_SO_REUSEADDR, &val, &notnull);
2316 if (notnull.u_bool) {
2317 if (Setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val.u_int, sizeof(int))
2318 != 0) {
2319 _errno = errno;
2320 Error4("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, { %d }, "F_Zu"): %s",
2321 fd, val.u_int, sizeof(val.u_int), strerror(errno));
2322 errno = _errno;
2323 return -1;
2326 return 0;