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 IP related functions */
7 #include "xiosysincludes.h"
9 #if _WITH_IP4 || _WITH_IP6
13 #include "xio-ascii.h"
14 #include "xio-socket.h"
20 #if WITH_IP4 || WITH_IP6
23 const struct optdesc opt_ip_options
= { "ip-options", "ipoptions", OPT_IP_OPTIONS
, GROUP_SOCK_IP
, PH_PASTSOCKET
,TYPE_BIN
, OFUNC_SOCKOPT_APPEND
, SOL_IP
, IP_OPTIONS
};
26 const struct optdesc opt_ip_pktinfo
= { "ip-pktinfo", "pktinfo", OPT_IP_PKTINFO
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_PKTINFO
};
29 const struct optdesc opt_ip_recvtos
= { "ip-recvtos", "recvtos", OPT_IP_RECVTOS
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RECVTOS
};
31 #ifdef IP_RECVTTL /* -Cygwin */
32 const struct optdesc opt_ip_recvttl
= { "ip-recvttl", "recvttl", OPT_IP_RECVTTL
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RECVTTL
};
35 const struct optdesc opt_ip_recvopts
= { "ip-recvopts","recvopts", OPT_IP_RECVOPTS
,GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RECVOPTS
};
38 const struct optdesc opt_ip_retopts
= { "ip-retopts", "retopts", OPT_IP_RETOPTS
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RETOPTS
};
40 const struct optdesc opt_ip_tos
= { "ip-tos", "tos", OPT_IP_TOS
, GROUP_SOCK_IP
, PH_PASTSOCKET
,TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_TOS
};
41 const struct optdesc opt_ip_ttl
= { "ip-ttl", "ttl", OPT_IP_TTL
, GROUP_SOCK_IP
, PH_PASTSOCKET
,TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_TTL
};
43 const struct optdesc opt_ip_hdrincl
= { "ip-hdrincl", "hdrincl", OPT_IP_HDRINCL
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_HDRINCL
};
46 const struct optdesc opt_ip_recverr
= { "ip-recverr", "recverr", OPT_IP_RECVERR
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RECVERR
};
48 #ifdef IP_MTU_DISCOVER
49 const struct optdesc opt_ip_mtu_discover
={"ip-mtu-discover","mtudiscover",OPT_IP_MTU_DISCOVER
,GROUP_SOCK_IP
,PH_PASTSOCKET
,TYPE_INT
,OFUNC_SOCKOPT
,SOL_IP
,IP_MTU_DISCOVER
};
52 const struct optdesc opt_ip_mtu
= { "ip-mtu", "mtu", OPT_IP_MTU
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_MTU
};
55 const struct optdesc opt_ip_transparent
= {"ip-transparent", "transparent", OPT_IP_TRANSPARENT
, GROUP_SOCK_IP
, PH_PREBIND
, TYPE_BOOL
, OFUNC_SOCKOPT
, SOL_IP
, IP_TRANSPARENT
};
58 const struct optdesc opt_ip_freebind
= { "ip-freebind","freebind", OPT_IP_FREEBIND
,GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_FREEBIND
};
60 #ifdef IP_ROUTER_ALERT
61 const struct optdesc opt_ip_router_alert
={"ip-router-alert","routeralert",OPT_IP_ROUTER_ALERT
,GROUP_SOCK_IP
,PH_PASTSOCKET
,TYPE_INT
,OFUNC_SOCKOPT
,SOL_IP
,IP_ROUTER_ALERT
};
63 /* following: Linux allows int but OpenBSD reqs char/byte */
64 const struct optdesc opt_ip_multicast_ttl
={"ip-multicast-ttl","multicastttl",OPT_IP_MULTICAST_TTL
,GROUP_SOCK_IP
,PH_PASTSOCKET
,TYPE_BYTE
,OFUNC_SOCKOPT
,SOL_IP
,IP_MULTICAST_TTL
};
65 /* following: Linux allows int but OpenBSD reqs char/byte */
66 const struct optdesc opt_ip_multicast_loop
={"ip-multicast-loop","multicastloop",OPT_IP_MULTICAST_LOOP
,GROUP_SOCK_IP
,PH_PASTSOCKET
,TYPE_BYTE
,OFUNC_SOCKOPT
,SOL_IP
,IP_MULTICAST_LOOP
};
67 const struct optdesc opt_ip_multicast_if
={"ip-multicast-if", "multicast-if", OPT_IP_MULTICAST_IF
, GROUP_SOCK_IP
,PH_PASTSOCKET
,TYPE_IP4NAME
,OFUNC_SOCKOPT
,SOL_IP
,IP_MULTICAST_IF
};
69 const struct optdesc opt_ip_pktoptions
= { "ip-pktoptions", "pktopts", OPT_IP_PKTOPTIONS
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_PKTOPTIONS
};
71 #ifdef IP_ADD_MEMBERSHIP
72 const struct optdesc opt_ip_add_membership
= { "ip-add-membership", "membership",OPT_IP_ADD_MEMBERSHIP
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_IP_MREQN
, OFUNC_SOCKOPT
, SOL_IP
, IP_ADD_MEMBERSHIP
};
74 #ifdef IP_ADD_SOURCE_MEMBERSHIP
75 const struct optdesc opt_ip_add_source_membership
= { "ip-add-source-membership", "source-membership",OPT_IP_ADD_SOURCE_MEMBERSHIP
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_IP_MREQ_SOURCE
, OFUNC_SOCKOPT
, SOL_IP
, IP_ADD_SOURCE_MEMBERSHIP
};
78 const struct optdesc opt_ip_recvdstaddr
= { "ip-recvdstaddr", "recvdstaddr",OPT_IP_RECVDSTADDR
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RECVDSTADDR
};
81 const struct optdesc opt_ip_recvif
= { "ip-recvif", "recvdstaddrif",OPT_IP_RECVIF
, GROUP_SOCK_IP
, PH_PASTSOCKET
, TYPE_INT
, OFUNC_SOCKOPT
, SOL_IP
, IP_RECVIF
};
84 #if WITH_RES_DEPRECATED
85 # define WITH_RES_AAONLY 1
86 # define WITH_RES_PRIMARY 1
87 #endif /* WITH_RES_DEPRECATED */
89 const struct optdesc opt_res_debug
= { "res-debug", NULL
, OPT_RES_DEBUG
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_DEBUG
};
91 const struct optdesc opt_res_aaonly
= { "res-aaonly", "aaonly", OPT_RES_AAONLY
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_AAONLY
};
93 const struct optdesc opt_res_usevc
= { "res-usevc", "usevc", OPT_RES_USEVC
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_USEVC
};
95 const struct optdesc opt_res_primary
= { "res-primary", "primary", OPT_RES_PRIMARY
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_PRIMARY
};
97 const struct optdesc opt_res_igntc
= { "res-igntc", "igntc", OPT_RES_IGNTC
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_IGNTC
};
98 const struct optdesc opt_res_recurse
= { "res-recurse", "recurse", OPT_RES_RECURSE
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_RECURSE
};
99 const struct optdesc opt_res_defnames
= { "res-defnames", "defnames", OPT_RES_DEFNAMES
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_DEFNAMES
};
100 const struct optdesc opt_res_stayopen
= { "res-stayopen", "stayopen", OPT_RES_STAYOPEN
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_STAYOPEN
};
101 const struct optdesc opt_res_dnsrch
= { "res-dnsrch", "dnsrch", OPT_RES_DNSRCH
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, XIO_OFFSETOF(para
.socket
.ip
.res_opts
), XIO_SIZEOF(para
.socket
.ip
.res_opts
), RES_DNSRCH
};
102 #endif /* HAVE_RESOLV_H */
104 #endif /* WITH_IP4 || WITH_IP6 */
112 Debug1("res_init() -> %d", result
);
115 #endif /* HAVE_RESOLV_H */
118 unsigned long res_opts() {
121 #endif /* HAVE_RESOLV_H */
123 /* the ultimate(?) socat resolver function
124 node: the address to be resolved; supported forms:
125 1.2.3.4 (IPv4 address)
127 hostname (hostname resolving to IPv4 or IPv6 address)
128 hostname.domain (fq hostname resolving to IPv4 or IPv6 address)
129 service: the port specification; may be numeric or symbolic
130 family: PF_INET, PF_INET6, or PF_UNSPEC permitting both
131 socktype: SOCK_STREAM, SOCK_DGRAM, ...
132 protocol: IPPROTO_UDP, IPPROTO_TCP
133 sau: an uninitialized storage for the resulting socket address
134 returns: STAT_OK, STAT_RETRYLATER
136 int xiogetaddrinfo(const char *node
, const char *service
,
137 int family
, int socktype
, int protocol
,
138 union sockaddr_union
*sau
, socklen_t
*socklen
,
139 unsigned long res_opts0
, unsigned long res_opts1
) {
140 int port
= -1; /* port number in network byte order */
141 char *numnode
= NULL
;
143 unsigned long save_res_opts
= 0;
145 struct addrinfo hints
= {0};
146 struct addrinfo
*res
= NULL
;
147 #else /* HAVE_PROTOTYPE_LIB_getipnodebyname || nothing */
148 struct hostent
*host
;
153 if (res_opts0
| res_opts1
) {
154 if (!(_res
.options
& RES_INIT
)) {
155 Res_init(); /*!!! returns -1 on error */
157 save_res_opts
= _res
.options
;
158 _res
.options
&= ~res_opts0
;
159 _res
.options
|= res_opts1
;
160 Debug2("changed _res.options from 0x%lx to 0x%lx",
161 save_res_opts
, _res
.options
);
163 #endif /* HAVE_RESOLV_H */
164 memset(sau
, 0, *socklen
);
165 sau
->soa
.sa_family
= family
;
167 if (service
&& service
[0]=='\0') {
168 Error("empty port/service");
172 if (family
== AF_VSOCK
) {
173 error_num
= sockaddr_vm_parse(&sau
->vm
, node
, service
);
179 #endif /* WITH_VSOCK */
181 /* if service is numeric we don't want to have a lookup (might take long
182 with NIS), so we handle this specially */
183 if (service
&& isdigit(service
[0]&0xff)) {
185 port
= htons(strtoul(service
, &extra
, 0));
186 if (*extra
!= '\0') {
187 Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"",
193 /* the resolver functions might handle numeric forms of node names by
194 reverse lookup, that's not what we want.
195 So we detect these and handle them specially */
196 if (0) { /* for canonical reasons */
199 } else if (node
&& node
[0] == '[' && node
[(nodelen
=strlen(node
))-1]==']') {
200 if ((numnode
= Malloc(nodelen
-1)) == NULL
) {
202 if (res_opts0
| res_opts1
) {
203 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
204 save_res_opts
& ( res_opts0
| res_opts1
));
209 strncpy(numnode
, node
+1, nodelen
-2); /* ok */
210 numnode
[nodelen
-2] = '\0';
213 hints
.ai_flags
|= AI_NUMERICHOST
;
214 #endif /* HAVE_GETADDRINFO */
215 if (family
== PF_UNSPEC
) family
= PF_INET6
;
216 #endif /* WITH_IP6 */
220 if (node
!= NULL
|| service
!= NULL
) {
221 struct addrinfo
*record
;
223 hints
.ai_flags
|= AI_PASSIVE
;
224 hints
.ai_family
= family
;
225 hints
.ai_socktype
= socktype
;
226 hints
.ai_protocol
= protocol
;
227 hints
.ai_addrlen
= 0;
228 hints
.ai_addr
= NULL
;
229 hints
.ai_canonname
= NULL
;
230 hints
.ai_next
= NULL
;
233 error_num
= Getaddrinfo(node
, service
, &hints
, &res
);
234 if (error_num
== 0) break;
235 if (error_num
== EAI_SOCKTYPE
&& socktype
!= 0) {
236 /* there are systems where kernel goes SCTP but not getaddrinfo() */
237 hints
.ai_socktype
= 0;
240 if (error_num
== EAI_SERVICE
&& protocol
!= 0) {
241 hints
.ai_protocol
= 0;
244 if (error_num
!= 0) {
245 Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s",
246 node
?node
:"NULL", service
?service
:"NULL",
247 hints
.ai_flags
, hints
.ai_family
,
248 hints
.ai_socktype
, hints
.ai_protocol
,
249 (error_num
== EAI_SYSTEM
)?
250 strerror(errno
):gai_strerror(error_num
));
251 if (res
!= NULL
) freeaddrinfo(res
);
252 if (numnode
) free(numnode
);
255 if (res_opts0
| res_opts1
) {
256 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
257 save_res_opts
& ( res_opts0
| res_opts1
));
260 return STAT_RETRYLATER
;
263 service
= NULL
; /* do not resolve later again */
266 if (family
== PF_UNSPEC
&& xioopts
.preferred_ip
== '0') {
267 /* we just take the first result */
268 family
= res
[0].ai_addr
->sa_family
;
270 if (family
== PF_UNSPEC
) {
272 trypf
= (xioopts
.preferred_ip
=='6'?PF_INET6
:PF_INET
);
273 /* we must look for a matching entry */
274 while (record
!= NULL
) {
275 if (record
->ai_family
== trypf
) {
277 break; /* family and record set accordingly */
279 record
= record
->ai_next
;
281 if (record
== NULL
) {
282 /* we did not find a "preferred" entry, take the first */
284 family
= res
[0].ai_addr
->sa_family
;
291 if (*socklen
> record
->ai_addrlen
) {
292 *socklen
= record
->ai_addrlen
;
294 memcpy(&sau
->ip4
, record
->ai_addr
, *socklen
);
296 #endif /* WITH_IP4 */
300 /* older AIX versions pass wrong length, so we correct it */
301 record
->ai_addr
->sa_len
= sizeof(struct sockaddr_in6
);
303 if (*socklen
> record
->ai_addrlen
) {
304 *socklen
= record
->ai_addrlen
;
306 memcpy(&sau
->ip6
, record
->ai_addr
, *socklen
);
308 #endif /* WITH_IP6 */
310 Error1("address resolved to unknown protocol family %d",
311 record
->ai_addr
->sa_family
);
318 case PF_INET
: *socklen
= sizeof(sau
->ip4
); break;
319 #endif /* WITH_IP4 */
321 case PF_INET6
: *socklen
= sizeof(sau
->ip6
); break;
322 #endif /* WITH_IP6 */
326 #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */
329 /* first fallback is getipnodebyname() */
330 if (family
== PF_UNSPEC
) {
331 #if WITH_IP4 && WITH_IP6
332 family
= xioopts
.default_ip
=='6'?PF_INET6
:PF_INET
;
339 host
= Getipnodebyname(node
, family
, AI_V4MAPPED
, &error_num
);
341 const static char ai_host_not_found
[] = "Host not found";
342 const static char ai_no_address
[] = "No address";
343 const static char ai_no_recovery
[] = "No recovery";
344 const static char ai_try_again
[] = "Try again";
345 const char *error_msg
= "Unknown error";
347 case HOST_NOT_FOUND
: error_msg
= ai_host_not_found
; break;
348 case NO_ADDRESS
: error_msg
= ai_no_address
;
349 case NO_RECOVERY
: error_msg
= ai_no_recovery
;
350 case TRY_AGAIN
: error_msg
= ai_try_again
;
352 Error2("getipnodebyname(\"%s\", ...): %s", node
, error_msg
);
357 *socklen
= sizeof(sau
->ip4
);
358 sau
->soa
.sa_family
= PF_INET
;
359 memcpy(&sau
->ip4
.sin_addr
, host
->h_addr_list
[0], 4);
364 *socklen
= sizeof(sau
->ip6
);
365 sau
->soa
.sa_family
= PF_INET6
;
366 memcpy(&sau
->ip6
.sin6_addr
, host
->h_addr_list
[0], 16);
374 #else /* !HAVE_PROTOTYPE_LIB_getipnodebyname */
377 /* this is not a typical IP6 resolver function - but Linux
378 "man gethostbyname" says that the only supported address type with
379 this function is AF_INET _at present_, so maybe this fallback will
380 be useful somewhere sometimes in a future even for IP6 */
381 if (family
== PF_UNSPEC
) {
382 #if WITH_IP4 && WITH_IP6
383 family
= xioopts
.default_ip
=='6'?PF_INET6
:PF_INET
;
390 /*!!! try gethostbyname2 for IP6 */
391 if ((host
= Gethostbyname(node
)) == NULL
) {
392 Error2("gethostbyname(\"%s\"): %s", node
,
393 h_errno
== NETDB_INTERNAL
? strerror(errno
) :
396 if (res_opts0
| res_opts1
) {
397 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
398 save_res_opts
& ( res_opts0
| res_opts1
));
401 return STAT_RETRYLATER
;
403 if (host
->h_addrtype
!= family
) {
404 Error2("xioaddrinfo(): \"%s\" does not resolve to %s",
405 node
, family
==PF_INET
?"IP4":"IP6");
410 *socklen
= sizeof(sau
->ip4
);
411 sau
->soa
.sa_family
= PF_INET
;
412 memcpy(&sau
->ip4
.sin_addr
, host
->h_addr_list
[0], 4);
414 #endif /* WITH_IP4 */
417 *socklen
= sizeof(sau
->ip6
);
418 sau
->soa
.sa_family
= PF_INET6
;
419 memcpy(&sau
->ip6
.sin6_addr
, host
->h_addr_list
[0], 16);
421 #endif /* WITH_IP6 */
428 #if WITH_TCP || WITH_UDP
430 port
= parseport(service
, protocol
);
435 case PF_INET
: sau
->ip4
.sin_port
= port
; break;
436 #endif /* WITH_IP4 */
438 case PF_INET6
: sau
->ip6
.sin6_port
= port
; break;
439 #endif /* WITH_IP6 */
442 #endif /* WITH_TCP || WITH_UDP */
444 if (numnode
) free(numnode
);
447 if (res_opts0
| res_opts1
) {
448 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
449 save_res_opts
& ( res_opts0
| res_opts1
));
451 #endif /* HAVE_RESOLV_H */
456 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
457 /* converts the ancillary message in *cmsg into a form useable for further
458 processing. knows the specifics of common message types.
459 these are valid for IPv4 and IPv6
460 returns the number of resulting syntax elements in *num
461 returns a sequence of \0 terminated type strings in *typbuff
462 returns a sequence of \0 terminated name strings in *nambuff
463 returns a sequence of \0 terminated value strings in *valbuff
464 the respective len parameters specify the available space in the buffers
465 returns STAT_OK on success
466 returns STAT_WARNING if a buffer was too short and data truncated.
468 int xiolog_ancillary_ip(struct cmsghdr
*cmsg
, int *num
,
469 char *typbuff
, int typlen
,
470 char *nambuff
, int namlen
,
471 char *envbuff
, int envlen
,
472 char *valbuff
, int vallen
) {
474 const char *cmsgtype
, *cmsgname
= NULL
, *cmsgenvn
= NULL
;
476 char scratch1
[16]; /* can hold an IPv4 address in ASCII */
477 #if WITH_IP4 && defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
483 msglen
= cmsg
->cmsg_len
-((char *)CMSG_DATA(cmsg
)-(char *)cmsg
);
485 switch (cmsg
->cmsg_type
) {
488 typbuff
[0] = '\0'; strncat(typbuff
, "IP", typlen
-1);
489 snprintf(nambuff
, namlen
, "type_%u", cmsg
->cmsg_type
);
490 xiodump(CMSG_DATA(cmsg
), msglen
, valbuff
, vallen
, 0);
493 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
495 struct in_pktinfo
*pktinfo
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
497 typbuff
[0] = '\0'; strncat(typbuff
, "IP_PKTINFO", typlen
-1);
498 snprintf(nambuff
, namlen
, "%s%c%s%c%s", "if", '\0', "locaddr", '\0', "dstaddr");
499 snprintf(envbuff
, envlen
, "%s%c%s%c%s", "IP_IF", '\0',
500 "IP_LOCADDR", '\0', "IP_DSTADDR");
501 snprintf(valbuff
, vallen
, "%s%c%s%c%s",
502 xiogetifname(pktinfo
->ipi_ifindex
, scratch1
, -1), '\0',
503 #if HAVE_PKTINFO_IPI_SPEC_DST
504 inet4addr_info(ntohl(pktinfo
->ipi_spec_dst
.s_addr
),
505 scratch2
, sizeof(scratch2
)),
510 inet4addr_info(ntohl(pktinfo
->ipi_addr
.s_addr
),
511 scratch3
, sizeof(scratch3
)));
514 #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
515 #endif /* WITH_IP4 */
516 #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR
518 struct sock_extended_err
*err
=
519 (struct sock_extended_err
*)CMSG_DATA(cmsg
);
521 typbuff
[0] = '\0'; strncat(typbuff
, "IP_RECVERR", typlen
-1);
522 snprintf(nambuff
, namlen
, "%s%c%s%c%s%c%s%c%s%c%s",
523 "errno", '\0', "origin", '\0', "type", '\0',
524 "code", '\0', "info", '\0', "data");
525 snprintf(envbuff
, envlen
, "%s%c%s%c%s%c%s%c%s%c%s",
526 "IP_RECVERR_ERRNO", '\0', "IP_RECVERR_ORIGIN", '\0',
527 "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0',
528 "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA");
529 snprintf(valbuff
, vallen
, "%u%c%u%c%u%c%u%c%u%c%u",
530 err
->ee_errno
, '\0', err
->ee_origin
, '\0', err
->ee_type
, '\0',
531 err
->ee_code
, '\0', err
->ee_info
, '\0', err
->ee_data
);
534 #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */
537 /* spec in FreeBSD: /usr/include/net/if_dl.h */
538 struct sockaddr_dl
*sadl
= (struct sockaddr_dl
*)CMSG_DATA(cmsg
);
540 typbuff
[0] = '\0'; strncat(typbuff
, "IP_RECVIF", typlen
-1);
541 nambuff
[0] = '\0'; strncat(nambuff
, "if", namlen
-1);
542 envbuff
[0] = '\0'; strncat(envbuff
, "IP_IF", envlen
-1);
545 xiosubstr(scratch1
, sadl
->sdl_data
, 0, sadl
->sdl_nlen
), vallen
-1);
548 #endif /* defined(IP_RECVIF) */
550 #ifdef IP_RECVDSTADDR
553 typbuff
[0] = '\0'; strncat(typbuff
, "IP_RECVDSTADDR", typlen
-1);
554 nambuff
[0] = '\0'; strncat(nambuff
, "dstaddr", namlen
-1);
555 envbuff
[0] = '\0'; strncat(envbuff
, "IP_DSTADDR", envlen
-1);
556 inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg
)), valbuff
, vallen
);
559 #endif /* WITH_IP4 */
564 cmsgtype
= "IP_OPTIONS"; cmsgname
= "options"; cmsgctr
= -1; break;
566 cmsgtype
= "IP_TOS"; cmsgname
= "tos"; cmsgctr
= msglen
; break;
567 case IP_TTL
: /* Linux */
569 case IP_RECVTTL
: /* FreeBSD */
571 cmsgtype
= "IP_TTL"; cmsgname
= "ttl"; cmsgctr
= msglen
; break;
573 /* when we come here we provide a single parameter
574 with type in cmsgtype, name in cmsgname, value length in msglen */
576 if (strlen(cmsgtype
) >= typlen
) rc
= STAT_WARNING
;
577 typbuff
[0] = '\0'; strncat(typbuff
, cmsgtype
, typlen
-1);
578 if (strlen(cmsgname
) >= namlen
) rc
= STAT_WARNING
;
579 nambuff
[0] = '\0'; strncat(nambuff
, cmsgname
, namlen
-1);
581 if (strlen(cmsgenvn
) >= envlen
) rc
= STAT_WARNING
;
582 envbuff
[0] = '\0'; strncat(envbuff
, cmsgenvn
, envlen
-1);
588 snprintf(valbuff
, vallen
, "%u", *(unsigned char *)CMSG_DATA(cmsg
)); break;
590 snprintf(valbuff
, vallen
, "%u", (*(unsigned int *)CMSG_DATA(cmsg
))); break;
592 xiodump(CMSG_DATA(cmsg
), msglen
, valbuff
, vallen
, 0); break;
597 #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */
600 #if HAVE_STRUCT_IP_MREQ_SOURCE
601 int xiotype_ip_add_source_membership(char *token
, const struct optname
*ent
, struct opt
*opt
) {
602 /* we do not resolve the addresses here because we do not yet know
603 if we are coping with an IPv4 or IPv6 socat address */
604 const char *ends
[] = { ":", NULL
};
605 const char *nests
[] = { "[","]", NULL
};
606 char buff
[512], *buffp
=buff
; size_t bufspc
= sizeof(buff
)-1;
610 /* parse first IP address, expect ':' */
612 nestlex((const char **)&tokp
, &buffp
, &bufspc
,
613 ends
, NULL
, NULL
, nests
,
616 Error1("option too long: \"%s\"", token
);
618 } else if (parsres
> 0) {
619 Error1("syntax error in \"%s\"", token
);
623 Error1("syntax in option %s: missing ':'", token
);
626 opt
->value
.u_ip_mreq_source
.mcaddr
= strdup(buff
); /*!!! NULL */
629 /* parse second IP address, expect ':' or '\0'' */
633 nestlex((const char **)&tokp
, &buffp
, &bufspc
,
634 ends
, NULL
, NULL
, nests
,
637 Error1("option too long: \"%s\"", token
);
639 } else if (parsres
> 0) {
640 Error1("syntax error in \"%s\"", token
);
644 Error1("syntax in option %s: missing ':'", token
);
647 opt
->value
.u_ip_mreq_source
.ifaddr
= strdup(buff
); /*!!! NULL */
650 /* parse third IP address, expect ':' or '\0'' */
654 nestlex((const char **)&tokp
, &buffp
, &bufspc
,
655 ends
, NULL
, NULL
, nests
,
658 Error1("option too long: \"%s\"", token
);
660 } else if (parsres
> 0) {
661 Error1("syntax error in \"%s\"", token
);
665 Error1("syntax in option %s: trailing cruft", token
);
668 opt
->value
.u_ip_mreq_source
.srcaddr
= strdup(buff
); /*!!! NULL */
670 Info4("setting option \"%s\" to {0x%08x,0x%08x,0x%08x}",
672 ntohl(*(unsigned int *)opt
->value
.u_ip_mreq_source
.mcaddr
),
673 ntohl(*(unsigned int *)opt
->value
.u_ip_mreq_source
.ifaddr
),
674 ntohl(*(unsigned int *)opt
->value
.u_ip_mreq_source
.srcaddr
));
678 int xioapply_ip_add_source_membership(struct single
*xfd
, struct opt
*opt
) {
679 struct ip_mreq_source ip4_mreq_src
= {{0}};
680 /* IPv6 not supported - seems to have different handling */
681 union sockaddr_union sockaddr1
;
682 socklen_t socklen1
= sizeof(sockaddr1
.ip4
);
683 union sockaddr_union sockaddr2
;
684 socklen_t socklen2
= sizeof(sockaddr2
.ip4
);
685 union sockaddr_union sockaddr3
;
686 socklen_t socklen3
= sizeof(sockaddr3
.ip4
);
688 /* first parameter is always multicast address */
690 xiogetaddrinfo(opt
->value
.u_ip_mreq_source
.mcaddr
, NULL
,
691 xfd
->para
.socket
.la
.soa
.sa_family
,
692 SOCK_DGRAM
, IPPROTO_IP
,
693 &sockaddr1
, &socklen1
, 0, 0);
694 ip4_mreq_src
.imr_multiaddr
= sockaddr1
.ip4
.sin_addr
;
695 /* second parameter is interface address */
696 xiogetaddrinfo(opt
->value
.u_ip_mreq_source
.ifaddr
, NULL
,
697 xfd
->para
.socket
.la
.soa
.sa_family
,
698 SOCK_DGRAM
, IPPROTO_IP
,
699 &sockaddr2
, &socklen2
, 0, 0);
700 ip4_mreq_src
.imr_interface
= sockaddr2
.ip4
.sin_addr
;
701 /* third parameter is source address */
702 xiogetaddrinfo(opt
->value
.u_ip_mreq_source
.srcaddr
, NULL
,
703 xfd
->para
.socket
.la
.soa
.sa_family
,
704 SOCK_DGRAM
, IPPROTO_IP
,
705 &sockaddr3
, &socklen3
, 0, 0);
706 ip4_mreq_src
.imr_sourceaddr
= sockaddr3
.ip4
.sin_addr
;
707 if (Setsockopt(xfd
->fd
, opt
->desc
->major
, opt
->desc
->minor
,
708 &ip4_mreq_src
, sizeof(ip4_mreq_src
)) < 0) {
709 Error8("setsockopt(%d, %d, %d, {0x%08x,0x%08x,0x%08x}, "F_Zu
"): %s",
710 xfd
->fd
, opt
->desc
->major
, opt
->desc
->minor
,
711 htonl((uint32_t)ip4_mreq_src
.imr_multiaddr
.s_addr
),
712 ip4_mreq_src
.imr_interface
.s_addr
,
713 ip4_mreq_src
.imr_sourceaddr
.s_addr
,
714 sizeof(struct ip_mreq_source
),
716 opt
->desc
= ODESC_ERROR
;
721 #endif /* HAVE_STRUCT_IP_MREQ_SOURCE */
723 #endif /* _WITH_IP4 || _WITH_IP6 */