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"
13 #include "xio-ascii.h"
14 #include "xio-socket.h"
15 #include "xio-named.h"
18 #include "xio-vsock.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
);
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
,
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
);
54 union sockaddr_union
*us
,
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>") };
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
};
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
};
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
};
103 /* end of setsockopt options of UNIX98 standard */
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
};
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
};
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
};
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
};
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
};
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
};
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 */
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
};
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 */
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
};
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 */
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
};
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
};
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
};
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
};
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
};
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
};
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
};
175 /* the SO_PROTOTYPE is OS defined on Solaris, HP-UX; we lend this for a more
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
};
179 const struct optdesc opt_fiosetown
= { "fiosetown", NULL
, OPT_FIOSETOWN
, GROUP_SOCKET
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_IOCTL
, FIOSETOWN
};
182 const struct optdesc opt_siocspgrp
= { "siocspgrp", NULL
, OPT_SIOCSPGRP
, GROUP_SOCKET
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_IOCTL
, SIOCSPGRP
};
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(
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];
215 int socktype
= SOCK_STREAM
;
217 union sockaddr_union them
; socklen_t themlen
; size_t themsize
;
218 union sockaddr_union us
; socklen_t uslen
= sizeof(us
);
222 xio_syntax(argv
[0], 3, argc
-1, addrdesc
->syntax
);
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)
244 applyopts(sfd
, -1, opts
, PH_INIT
);
245 applyopts(sfd
, -1, opts
, PH_EARLY
);
249 dalan(address
, (uint8_t *)&them
.soa
.sa_data
, &themsize
, sizeof(them
), 'i'))
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
;
257 #if HAVE_STRUCT_SOCKADDR_SALEN
258 sizeof(them
.soa
.sa_len
) +
260 sizeof(them
.soa
.sa_family
);
262 sfd
->dtype
= XIOREAD_STREAM
|XIOWRITE_STREAM
;
265 if (retropt_bind(opts
, 0 /*pf*/, socktype
, proto
, (struct sockaddr
*)&us
, &uslen
, 3,
266 sfd
->para
.socket
.ip
.ai_flags
)
269 us
.soa
.sa_family
= pf
;
274 needbind
?&us
:NULL
, uslen
,
275 (struct sockaddr
*)&them
, themlen
,
276 opts
, pf
, socktype
, proto
, false)) != 0) {
279 if ((result
= _xio_openlate(sfd
, opts
)) < 0) {
286 static int xioopen_socket_listen(
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];
301 int socktype
= SOCK_STREAM
;
302 union sockaddr_union us
; socklen_t uslen
; size_t ussize
;
307 xio_syntax(argv
[0], 3, argc
-1, addrdesc
->syntax
);
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
;
330 dalan(usname
, (uint8_t *)&us
.soa
.sa_data
, &ussize
, sizeof(us
), 'i'))
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
)
341 us
.soa
.sa_family
= pf
;
343 if (applyopts_single(sfd
, opts
, PH_INIT
) < 0)
345 applyopts(sfd
, -1, opts
, PH_INIT
);
346 applyopts(sfd
, -1, opts
, PH_EARLY
);
348 opts0
= copyopts(opts
, GROUP_ALL
);
351 xioopen_listen(sfd
, xioflags
,
353 opts
, opts0
, 0/*instead of pf*/, socktype
, proto
))
358 #endif /* WITH_LISTEN */
360 /* we expect the form: ...:domain:type:protocol:remote-address */
361 static int xioopen_socket_sendto(
367 const struct addrdesc
*addrdesc
)
372 xio_syntax(argv
[0], 4, argc
-1, addrdesc
->syntax
);
376 _xioopen_socket_sendto(argv
[1], argv
[2], argv
[3], argv
[4],
377 opts
, xioflags
, xxfd
, addrdesc
->groups
))
381 _xio_openlate(&xxfd
->stream
, opts
);
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
,
390 xiosingle_t
*sfd
= &xxfd
->stream
;
392 union sockaddr_union us
= {{0}};
393 socklen_t uslen
= 0; size_t ussize
;
396 int socktype
= SOCK_RAW
;
398 bool needbind
= false;
399 char *bindstring
= NULL
;
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
;
426 dalan(address
, (uint8_t *)&sfd
->peersa
.soa
.sa_data
, &themsize
,
427 sizeof(sfd
->peersa
), 'i'))
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
)
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
) +
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) {
456 dalan(bindstring
, (uint8_t *)&us
.soa
.sa_data
, &ussize
, sizeof(us
), 'i'))
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
)
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 */
479 int xioopen_socket_recvfrom(
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];
493 union sockaddr_union
*us
= &sfd
->para
.socket
.la
;
494 socklen_t uslen
; size_t ussize
;
495 int pf
, socktype
, proto
;
500 xio_syntax(argv
[0], 4, argc
-1, addrdesc
->syntax
);
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
;
527 dalan(address
, (uint8_t *)&us
->soa
.sa_data
, &ussize
, sizeof(*us
), 'i'))
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
);
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
)
548 sfd
->para
.socket
.dorange
= true;
553 _xioopen_dgram_recvfrom(sfd
, xioflags
, &us
->soa
, uslen
,
554 opts
, pf
, socktype
, proto
, E_ERROR
))
558 _xio_openlate(sfd
, opts
);
562 /* we expect the form: ...:domain:type:protocol:local-address */
564 int xioopen_socket_recv(
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];
578 union sockaddr_union us
;
579 socklen_t uslen
; size_t ussize
;
580 int pf
, socktype
, proto
;
585 xio_syntax(argv
[0], 4, argc
-1, addrdesc
->syntax
);
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
;
612 dalan(address
, (uint8_t *)&us
.soa
.sa_data
, &ussize
, sizeof(us
), 'i'))
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
)
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
)
634 sfd
->para
.socket
.dorange
= true;
639 _xioopen_dgram_recv(sfd
, xioflags
, &us
.soa
,
640 uslen
, opts
, pf
, socktype
, proto
, E_ERROR
))
644 _xio_openlate(sfd
, opts
);
649 /* we expect the form: ...:domain:type:protocol:remote-address */
650 static int xioopen_socket_datagram(
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];
670 xio_syntax(argv
[0], 4, argc
-1, addrdesc
->syntax
);
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
;
687 dalan(address
, (uint8_t *)&sfd
->peersa
.soa
.sa_data
, &themsize
,
688 sizeof(sfd
->peersa
), 'i'))
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
) +
702 _xioopen_socket_sendto(pfname
, typename
, protname
, address
,
703 opts
, xioflags
, xxfd
, addrdesc
->groups
))
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
)
720 sfd
->para
.socket
.dorange
= true;
721 sfd
->dtype
|= XIOREAD_RECV_CHECKRANGE
;
725 _xio_openlate(sfd
, opts
);
729 #endif /* WITH_GENERICSOCKET */
731 /* EINTR not handled specially */
732 int xiogetpacketinfo(struct single
*sfd
, int fd
)
734 #if defined(MSG_ERRQUEUE)
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};
746 msgh
.msg_namelen
= palen
;
747 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
748 msgh
.msg_control
= ctrlbuff
;
750 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
751 msgh
.msg_controllen
= sizeof(ctrlbuff
);
753 if (xiogetancillary(fd
,
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);
770 #endif /* defined(MSG_ERRQUEUE) */
776 /* A subroutine that is common to all socket addresses that want to connect()
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,
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
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
) {
796 union sockaddr_union la
;
797 socklen_t lalen
= themlen
;
802 if (pf
== PF_UNIX
&& us
!= NULL
) {
803 applyopts_named(us
->un
.sun_path
, opts
, PH_EARLY
);
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) {
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
);
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
));
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
;
844 socklen_t errlen
= sizeof(err
);
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
);
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
));
858 return STAT_RETRYLATER
;
861 Msg2(level
, "connecting to %s: %s",
862 sockaddr_info(them
, themlen
, infobuff
, sizeof(infobuff
)),
863 strerror(ETIMEDOUT
));
865 return STAT_RETRYLATER
;
867 if (writefd
.revents
& POLLERR
) {
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
)),
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
));
881 return STAT_RETRYLATER
;
883 /* otherwise OK or network error */
884 result
= Getsockopt(sfd
->fd
, SOL_SOCKET
, SO_ERROR
, &err
, &errlen
);
886 Msg2(level
, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s",
887 sfd
->fd
, strerror(err
));
889 return STAT_RETRYLATER
;
891 Debug2("getsockopt(%d, SOL_SOCKET, SO_ERROR, { %d }) -> 0",
894 Msg4(level
, "connect(%d, %s, "F_Zd
"): %s",
895 sfd
->fd
, sockaddr_info(them
, themlen
, infobuff
, sizeof(infobuff
)),
896 themlen
, strerror(err
));
898 return STAT_RETRYLATER
;
900 Fcntl_l(sfd
->fd
, F_SETFL
, fcntl_flags
);
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
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;
918 return STAT_RETRYLATER
;
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
));
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
);
938 if (pf
== PF_UNIX
&& us
!= NULL
) {
939 applyopts_named(us
->un
.sun_path
, opts
, PH_LATE
);
942 applyopts(sfd
, -1, opts
, PH_LATE
);
948 /* a subroutine that is common to all socket addresses that want to connect
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,
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
,
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 */
978 if (sfd
->forever
|| sfd
->retry
) {
981 #endif /* WITH_RETRY */
984 _xioopen_connect(sfd
, us
, uslen
, them
, themlen
, opts
,
985 pf
, socktype
, protocol
, alt
, level
);
989 case STAT_RETRYLATER
:
990 if (sfd
->forever
|| sfd
->retry
) {
992 if (result
== STAT_RETRYLATER
) {
993 Nanosleep(&sfd
->intervall
, NULL
);
995 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
999 #endif /* WITH_RETRY */
1005 xiosetchilddied(); /* set SIGCHLD handler */
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) {
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 */
1030 /* parent process */
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() */
1037 #endif /* WITH_RETRY */
1042 if ((result
= _xio_openlate(fd
, opts
)) < 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
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
,
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
);
1067 if (pf
== PF_UNIX
&& us
!= NULL
) {
1068 applyopts_named(us
->un
.sun_path
, opts
, PH_EARLY
);
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) {
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
);
1098 if (pf
== PF_UNIX
&& us
!= NULL
) {
1099 applyopts_named(us
->un
.sun_path
, opts
, PH_LATE
);
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
)));
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
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
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
,
1142 int pf
, int socktype
, int proto
, int level
) {
1144 bool dofork
= false;
1145 pid_t pid
; /* mostly int; only used with fork */
1148 bool drop
= false; /* true if current packet must be dropped */
1151 retropt_bool(opts
, OPT_FORK
, &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) {
1177 applyopts(sfd
, -1, opts
, PH_PASTBIND
);
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*/
1190 #ifdef IPPROTO_UDPLITE
1191 case IPPROTO_UDPLITE
:
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
);
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
);
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
)
1211 return STAT_NORETRY
;
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';
1225 Info("starting recvfrom loop");
1232 while (true) { /* but we only loop if fork option is set */
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 */
1244 socket_init(pf
, pa
);
1249 Recv(sfd
->fd
, dummy
, sizeof(dummy
), 0);
1253 Info("Recvfrom: Checking/waiting for next packet");
1254 /* loop until select()/poll() returns valid */
1256 struct pollfd readfd
;
1257 /*? int level = E_ERROR;*/
1259 Notice1("receiving on %s", sockaddr_info(us
, uslen
, lisname
, sizeof(lisname
)));
1261 Notice1("receiving IP protocol %u", proto
);
1263 readfd
.fd
= sfd
->fd
;
1264 readfd
.events
= POLLIN
;
1265 if (xiopoll(&readfd
, 1, NULL
) > 0) {
1269 if (errno
== EINTR
) {
1273 Msg2(level
, "poll({%d,,},,-1): %s", sfd
->fd
, strerror(errno
));
1275 return STAT_RETRYLATER
;
1279 msgh
.msg_namelen
= palen
;
1280 #if HAVE_STRUCT_MSGHDR_MSGCONTROL
1281 msgh
.msg_control
= ctrlbuff
;
1283 #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
1284 msgh
.msg_controllen
= sizeof(ctrlbuff
);
1286 while ((rc
= xiogetancillary(sfd
->fd
,
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) {
1306 Recv(sfd
->fd
, buff
, sizeof(buff
), 0);
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
;
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) {
1334 return STAT_RETRYLATER
;
1337 if (pid
== 0) { /* child */
1339 sfd
->triggerfd
= trigger
[1];
1340 Fcntl_l(sfd
->triggerfd
, F_SETFD
, FD_CLOEXEC
);
1347 #endif /* WITH_RETRY */
1350 /* with UNIX sockets: only listening parent is allowed to remove
1352 sfd
->opt_unlink_close
= false;
1353 #endif /* WITH_UNIX */
1363 while (Read(trigger
[0], buf
, 1) < 0 && errno
== EINTR
) ;
1366 Info("continue listening");
1371 if ((result
= _xio_openlate(sfd
, opts
)) != 0)
1372 return STAT_NORETRY
;
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
,
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) {
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*/
1411 #ifdef IPPROTO_UDPLITE
1412 case IPPROTO_UDPLITE
:
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
);
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
);
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
)
1431 return STAT_NORETRY
;
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';
1445 Info("starting recv loop");
1452 int retropt_socket_pf(struct opt
*opts
, int *pf
) {
1455 if (retropt_string(opts
, OPT_PROTOCOL_FAMILY
, &pfname
) >= 0) {
1456 if (isdigit((unsigned char)pfname
[0])) {
1457 *pf
= strtoul(pfname
, NULL
/*!*/, 0);
1459 } else if (!strcasecmp("inet", pfname
) ||
1460 !strcasecmp("inet4", pfname
) ||
1461 !strcasecmp("ip4", pfname
) ||
1462 !strcasecmp("ipv4", pfname
) ||
1463 !strcasecmp("2", pfname
)) {
1465 #endif /* WITH_IP4 */
1467 } else if (!strcasecmp("inet6", pfname
) ||
1468 !strcasecmp("ip6", pfname
) ||
1469 !strcasecmp("ipv6", pfname
) ||
1470 !strcasecmp("10", pfname
)) {
1472 #endif /* WITH_IP6 */
1474 Error1("unknown protocol family \"%s\"", pfname
);
1475 /*! Warn("falling back to INET");*/
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,
1489 (recvmsg() retrieves just meta info, not the data)
1491 int xiogetancillary(int fd
, struct msghdr
*msgh
, int flags
) {
1493 #if HAVE_STRUCT_IOVEC
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;
1503 #if HAVE_STRUCT_MSGHDR_MSGFLAGS
1504 msgh
->msg_flags
= 0;
1506 if (Recvmsg(fd
, msgh
, flags
) < 0) {
1507 Info1("recvmsg(): %s", strerror(errno
));
1508 return STAT_RETRYLATER
;
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(
1520 struct msghdr
*msgh
,
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 */
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
);
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
,
1548 /* try to get the anc.msg. contents in handy components, protocol/level
1550 switch (cmsg
->cmsg_level
) {
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);
1557 #if WITH_IP4 || WITH_IP6
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);
1564 #endif /* WITH_IP4 || WITH_IP6 */
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);
1572 #endif /* WITH_IP6 */
1573 #if _WITH_INTERFACE && HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA
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);
1580 #endif /* HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA */
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) */
1592 typp
= typbuff
; namp
= nambuff
; envp
= envbuff
; valp
= valbuff
;
1595 Info3("ancillary message: %s: %s=%s", typp
, namp
, valp
);
1599 xiosetenv(envp
, valp
, 1, NULL
);
1600 } else if (!strcasecmp(typp
+strlen(typp
)-strlen(namp
), namp
)) {
1601 xiosetenv(typp
, valp
, 1, NULL
);
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
);
1614 #else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */
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
) {
1627 xiocheckrange_ip4(&sa
->ip4
, range
);
1628 #endif /* WITH_IP4 */
1632 xiocheckrange_ip6(&sa
->ip6
, range
);
1633 #endif /* WITH_IP6 */
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
]) {
1650 int xiocheckpeer(xiosingle_t
*sfd
,
1651 union sockaddr_union
*pa
, union sockaddr_union
*la
) {
1656 if (sfd
->para
.socket
.dorange
) {
1657 if (pa
== NULL
) { return -1; }
1658 if (xiocheckrange(pa
, &sfd
->para
.socket
.range
) < 0) {
1660 Warn1("refusing connection from %s due to range option",
1661 sockaddr_info(&pa
->soa
, 0,
1662 infobuff
, sizeof(infobuff
)));
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; }
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
)));
1682 #endif /* WITH_IP4 */
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
)));
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
)));
1705 else if (pa
->soa
.sa_family
== AF_INET6
&&
1706 ntohs(((struct sockaddr_in6
*)pa
)->sin6_port
) >=
1708 Warn1("refusing connection from %s due to lowport option",
1709 sockaddr_info(&pa
->soa
, 0,
1710 infobuff
, sizeof(infobuff
)));
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
);
1724 Warn1("refusing connection from %s due to tcpwrapper option",
1725 sockaddr_info(&pa
->soa
, 0,
1726 infobuff
, sizeof(infobuff
)));
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_*
1750 xiolog_ancillary_socket(
1752 struct cmsghdr
*cmsg
,
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
;
1764 #if defined(CMSG_DATA)
1766 msglen
= cmsg
->cmsg_len
-((char *)CMSG_DATA(cmsg
)-(char *)cmsg
);
1767 switch (cmsg
->cmsg_type
) {
1769 case SO_PASSCRED
: /* this is really a UNIX/LOCAL message */
1770 /*! needs implementation */
1771 #endif /* SO_PASSCRED */
1773 case SO_RIGHTS
: /* this is really a UNIX/LOCAL message */
1774 /*! needs implementation */
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);
1782 # ifdef SCM_TIMESTAMP
1787 tv
= (struct timeval
*)CMSG_DATA(cmsg
);
1789 #ifdef SCM_TIMESTAMP
1790 "SCM_TIMESTAMP" /* FreeBSD */
1792 "SO_TIMESTAMP" /* Linux */
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
);
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 */
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);
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
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
1837 if ((s
= Socket(PF_INET
, SOCK_DGRAM
, IPPROTO_IP
)) < 0) {
1838 Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno
));
1843 #if HAVE_STRUCT_IFREQ_IFR_INDEX
1844 ifr
.ifr_index
= ind
;
1845 #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX
1846 ifr
.ifr_ifindex
= ind
;
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
));
1857 if (ins
< 0) Close(s
);
1860 #endif /* SIOCGIFNAME */
1861 if (ins
< 0) Close(s
);
1862 strcpy(val
, ifr
.ifr_name
);
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
,
1874 struct xiorange
*range
,
1875 const int ai_flags
[2])
1877 size_t addrlen
= 0, masklen
= 0;
1883 return xioparsenetwork_ip4(rangename
, range
, ai_flags
);
1885 #endif /* WITH_IP4 */
1888 return xioparsenetwork_ip6(rangename
, range
, ai_flags
);
1890 #endif /* WITH_IP6 */
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';
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');
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
;
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');
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",
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
));
1940 Error1("range option not supported with address family %d", pf
);
1941 return STAT_NORETRY
;
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. */
1950 const char *rangename
,
1952 struct xiorange
*range
,
1953 const int ai_flags
[2])
1956 if (xioparsenetwork(rangename
, pf
, range
, ai_flags
) < 0) {
1957 Error2("failed to parse or resolve range \"%s\" (pf=%d)", rangename
, pf
);
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 */
1965 range
->netaddr
.ip4
.sin_addr
.s_addr
&= range
->netmask
.ip4
.sin_addr
.s_addr
;
1967 #endif /* WITH_IP4 */
1970 return xiorange_ip6andmask(range
);
1972 #endif /* WITH_IP6 */
1974 for (i
= 0; i
< sizeof(range
->netaddr
); ++i
) {
1975 ((char *)&range
->netaddr
)[i
] &= ((char *)&range
->netmask
)[i
];
1979 Error1("range option not supported with address family %d", pf
);
1980 return STAT_NORETRY
;
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
,
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
) {
2003 xiosetsockaddrenv_unix(idx
, strchr(namebuff
, '\0'), XIOSOCKADDRENVLEN
-strlen(lr
),
2004 valuebuff
, XIOSOCKADDRENVLEN
,
2005 &sau
->un
, salen
, proto
);
2006 xiosetenv(namebuff
, valuebuff
, 1, NULL
);
2008 #endif /* WITH_UNIX */
2013 xiosetsockaddrenv_ip4(idx
, strchr(namebuff
, '\0'), XIOSOCKADDRENVLEN
-strlen(lr
),
2014 valuebuff
, XIOSOCKADDRENVLEN
,
2016 xiosetenv(namebuff
, valuebuff
, 1, NULL
);
2017 namebuff
[strlen(lr
)] = '\0'; ++idx
;
2018 } while (result
> 0);
2020 #endif /* WITH_IP4 */
2023 strcpy(namebuff
, lr
);
2026 xiosetsockaddrenv_ip6(idx
, strchr(namebuff
, '\0'), XIOSOCKADDRENVLEN
-strlen(lr
),
2027 valuebuff
, XIOSOCKADDRENVLEN
,
2029 xiosetenv(namebuff
, valuebuff
, 1, NULL
);
2030 namebuff
[strlen(lr
)] = '\0'; ++idx
;
2031 } while (result
> 0);
2033 #endif /* WITH_IP6 */
2036 strcpy(namebuff
, lr
);
2039 xiosetsockaddrenv_vsock(idx
, strchr(namebuff
, '\0'), XIOSOCKADDRENVLEN
-strlen(lr
),
2040 valuebuff
, XIOSOCKADDRENVLEN
,
2042 xiosetenv(namebuff
, valuebuff
, 1, NULL
);
2043 namebuff
[strlen(lr
)] = '\0'; ++idx
;
2044 } while (result
> 0);
2046 #endif /* WITH_VSOCK */
2049 result
= xiosetsockaddrenv_packet(lr
, (void *)sau
, proto
); break;
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
) {
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
);
2076 Msg4(msglevel
, "socket(%d, %d, %d): %s",
2077 pf
, socktype
, proto
, strerror(errno
));
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]) {
2091 retropt_int(opts
, OPT_SO_TYPE
, &socktype
);
2092 retropt_int(opts
, OPT_SO_PROTOTYPE
, &proto
);
2093 result
= Socketpair(pf
, socktype
, proto
, sv
);
2095 Error5("socketpair(%d, %d, %d, %p): %s",
2096 pf
, socktype
, proto
, sv
, strerror(errno
));
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.
2113 union sockaddr_union
*us
,
2123 if (false /* for canonical reasons */) {
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');
2135 if (uslen
== ((char *)&us
->un
.sun_path
-(char *)us
)) {
2138 #if WITH_ABSTRACT_UNIXSOCKET
2140 usrname
= strndup(us
->un
.sun_path
+1, sizeof(us
->un
.sun_path
)-1);
2143 usrname
= strndup(us
->un
.sun_path
, sizeof(us
->un
.sun_path
));
2144 if (usrname
== NULL
) {
2146 Error2("strndup(\"%s\", "F_Zu
"): out of memory",
2147 us
->un
.sun_path
, sizeof(us
->un
.sun_path
));
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
));
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
)); /*?*/
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
) {
2173 return STAT_RETRYLATER
;
2176 break; /* could bind to path, good, continue past loop */
2180 applyopts_named(us
->un
.sun_path
, opts
, PH_PREOPEN
);
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
));
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
2200 union sockaddr_union sin
, *sinp
;
2201 unsigned short *port
, i
, N
;
2204 applyopts(sfd
, sfd
->fd
, opts
, PH_PREBIND
);
2205 applyopts(sfd
, sfd
->fd
, opts
, PH_BIND
);
2206 /* prepare sockaddr for bind probing */
2210 if (pf
== AF_INET
) {
2211 socket_in_init(&sin
.ip4
);
2214 socket_in6_init(&sin
.ip6
);
2219 if (pf
== AF_INET
) {
2220 port
= &sin
.ip4
.sin_port
;
2222 } else if (pf
== AF_INET6
) {
2223 port
= &sin
.ip6
.sin6_port
;
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 */
2238 srandom(tb
.time
*1000+tb
.millitm
);
2242 tz
.tz_minuteswest
= 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
);
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 */
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
) {
2262 return STAT_RETRYLATER
;
2265 break; /* could bind to port, good, continue past loop */
2267 --i
; if (i
< XIO_IPPORT_LOWER
) i
= IPPORT_RESERVED
-1;
2269 Msg(level
, "no low port available");
2270 /*errno = EADDRINUSE; still assigned */
2272 return STAT_RETRYLATER
;
2275 #endif /* WITH_TCP || WITH_UDP */
2278 applyopts(sfd
, sfd
->fd
, opts
, PH_PREBIND
);
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
));
2286 return STAT_RETRYLATER
;
2291 applyopts(sfd
, -1, opts
, PH_PASTBIND
);
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
2301 Return 0 on success, or -1 with errno when an error occurred.
2303 int xiosock_reuseaddr(int fd
, int ipproto
, struct opt
*opts
)
2306 union integral notnull
;
2310 notnull
.u_bool
= false;
2311 if (ipproto
== IPPROTO_TCP
) {
2313 notnull
.u_bool
= true;
2315 retropt_2integrals(opts
, OPT_SO_REUSEADDR
, &val
, ¬null
);
2316 if (notnull
.u_bool
) {
2317 if (Setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &val
.u_int
, sizeof(int))
2320 Error4("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, { %d }, "F_Zu
"): %s",
2321 fd
, val
.u_int
, sizeof(val
.u_int
), strerror(errno
));