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 if (hints
.ai_protocol
== 0) {
242 Error7("getaddrinfo\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s",
243 node
?node
:"NULL", service
?service
:"NULL",
244 hints
.ai_flags
, hints
.ai_family
,
245 hints
.ai_socktype
, hints
.ai_protocol
,
246 gai_strerror(error_num
));
247 if (res
!= NULL
) freeaddrinfo(res
);
248 if (numnode
) free(numnode
);
251 hints
.ai_protocol
= 0;
254 if (error_num
!= 0) {
255 Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s",
256 node
?node
:"NULL", service
?service
:"NULL",
257 hints
.ai_flags
, hints
.ai_family
,
258 hints
.ai_socktype
, hints
.ai_protocol
,
259 (error_num
== EAI_SYSTEM
)?
260 strerror(errno
):gai_strerror(error_num
));
261 if (res
!= NULL
) freeaddrinfo(res
);
262 if (numnode
) free(numnode
);
265 if (res_opts0
| res_opts1
) {
266 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
267 save_res_opts
& ( res_opts0
| res_opts1
));
270 return STAT_RETRYLATER
;
273 service
= NULL
; /* do not resolve later again */
276 if (family
== PF_UNSPEC
&& xioopts
.preferred_ip
== '0') {
277 /* we just take the first result */
278 family
= res
[0].ai_addr
->sa_family
;
280 if (family
== PF_UNSPEC
) {
282 trypf
= (xioopts
.preferred_ip
=='6'?PF_INET6
:PF_INET
);
283 /* we must look for a matching entry */
284 while (record
!= NULL
) {
285 if (record
->ai_family
== trypf
) {
287 break; /* family and record set accordingly */
289 record
= record
->ai_next
;
291 if (record
== NULL
) {
292 /* we did not find a "preferred" entry, take the first */
294 family
= res
[0].ai_addr
->sa_family
;
301 if (*socklen
> record
->ai_addrlen
) {
302 *socklen
= record
->ai_addrlen
;
304 memcpy(&sau
->ip4
, record
->ai_addr
, *socklen
);
306 #endif /* WITH_IP4 */
310 /* older AIX versions pass wrong length, so we correct it */
311 record
->ai_addr
->sa_len
= sizeof(struct sockaddr_in6
);
313 if (*socklen
> record
->ai_addrlen
) {
314 *socklen
= record
->ai_addrlen
;
316 memcpy(&sau
->ip6
, record
->ai_addr
, *socklen
);
318 #endif /* WITH_IP6 */
320 Error1("address resolved to unknown protocol family %d",
321 record
->ai_addr
->sa_family
);
328 case PF_INET
: *socklen
= sizeof(sau
->ip4
); break;
329 #endif /* WITH_IP4 */
331 case PF_INET6
: *socklen
= sizeof(sau
->ip6
); break;
332 #endif /* WITH_IP6 */
336 #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */
339 /* first fallback is getipnodebyname() */
340 if (family
== PF_UNSPEC
) {
341 #if WITH_IP4 && WITH_IP6
342 family
= xioopts
.default_ip
=='6'?PF_INET6
:PF_INET
;
349 host
= Getipnodebyname(node
, family
, AI_V4MAPPED
, &error_num
);
351 const static char ai_host_not_found
[] = "Host not found";
352 const static char ai_no_address
[] = "No address";
353 const static char ai_no_recovery
[] = "No recovery";
354 const static char ai_try_again
[] = "Try again";
355 const char *error_msg
= "Unknown error";
357 case HOST_NOT_FOUND
: error_msg
= ai_host_not_found
; break;
358 case NO_ADDRESS
: error_msg
= ai_no_address
;
359 case NO_RECOVERY
: error_msg
= ai_no_recovery
;
360 case TRY_AGAIN
: error_msg
= ai_try_again
;
362 Error2("getipnodebyname(\"%s\", ...): %s", node
, error_msg
);
367 *socklen
= sizeof(sau
->ip4
);
368 sau
->soa
.sa_family
= PF_INET
;
369 memcpy(&sau
->ip4
.sin_addr
, host
->h_addr_list
[0], 4);
374 *socklen
= sizeof(sau
->ip6
);
375 sau
->soa
.sa_family
= PF_INET6
;
376 memcpy(&sau
->ip6
.sin6_addr
, host
->h_addr_list
[0], 16);
384 #else /* !HAVE_PROTOTYPE_LIB_getipnodebyname */
387 /* this is not a typical IP6 resolver function - but Linux
388 "man gethostbyname" says that the only supported address type with
389 this function is AF_INET _at present_, so maybe this fallback will
390 be useful somewhere sometimes in a future even for IP6 */
391 if (family
== PF_UNSPEC
) {
392 #if WITH_IP4 && WITH_IP6
393 family
= xioopts
.default_ip
=='6'?PF_INET6
:PF_INET
;
400 /*!!! try gethostbyname2 for IP6 */
401 if ((host
= Gethostbyname(node
)) == NULL
) {
402 Error2("gethostbyname(\"%s\"): %s", node
,
403 h_errno
== NETDB_INTERNAL
? strerror(errno
) :
406 if (res_opts0
| res_opts1
) {
407 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
408 save_res_opts
& ( res_opts0
| res_opts1
));
411 return STAT_RETRYLATER
;
413 if (host
->h_addrtype
!= family
) {
414 Error2("xioaddrinfo(): \"%s\" does not resolve to %s",
415 node
, family
==PF_INET
?"IP4":"IP6");
420 *socklen
= sizeof(sau
->ip4
);
421 sau
->soa
.sa_family
= PF_INET
;
422 memcpy(&sau
->ip4
.sin_addr
, host
->h_addr_list
[0], 4);
424 #endif /* WITH_IP4 */
427 *socklen
= sizeof(sau
->ip6
);
428 sau
->soa
.sa_family
= PF_INET6
;
429 memcpy(&sau
->ip6
.sin6_addr
, host
->h_addr_list
[0], 16);
431 #endif /* WITH_IP6 */
438 #if WITH_TCP || WITH_UDP
440 port
= parseport(service
, protocol
);
445 case PF_INET
: sau
->ip4
.sin_port
= port
; break;
446 #endif /* WITH_IP4 */
448 case PF_INET6
: sau
->ip6
.sin6_port
= port
; break;
449 #endif /* WITH_IP6 */
452 #endif /* WITH_TCP || WITH_UDP */
454 if (numnode
) free(numnode
);
457 if (res_opts0
| res_opts1
) {
458 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
459 save_res_opts
& ( res_opts0
| res_opts1
));
461 #endif /* HAVE_RESOLV_H */
466 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
467 /* Converts the ancillary message in *cmsg into a form useable for further
468 processing. knows the specifics of common message types.
469 These are valid for IPv4 and IPv6
470 Returns the number of resulting syntax elements in *num
471 Returns a sequence of \0 terminated type strings in *typbuff
472 Returns a sequence of \0 terminated name strings in *nambuff
473 Returns a sequence of \0 terminated value strings in *valbuff
474 The respective len parameters specify the available space in the buffers
475 Returns STAT_OK on success
476 Returns STAT_WARNING if a buffer was too short and data truncated.
478 int xiolog_ancillary_ip(struct cmsghdr
*cmsg
, int *num
,
479 char *typbuff
, int typlen
,
480 char *nambuff
, int namlen
,
481 char *envbuff
, int envlen
,
482 char *valbuff
, int vallen
) {
484 const char *cmsgtype
, *cmsgname
= NULL
, *cmsgenvn
= NULL
;
486 char scratch1
[16]; /* can hold an IPv4 address in ASCII */
487 #if WITH_IP4 && defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
493 msglen
= cmsg
->cmsg_len
-((char *)CMSG_DATA(cmsg
)-(char *)cmsg
);
495 switch (cmsg
->cmsg_type
) {
498 typbuff
[0] = '\0'; strncat(typbuff
, "IP", typlen
-1);
499 snprintf(nambuff
, namlen
, "type_%u", cmsg
->cmsg_type
);
500 xiodump(CMSG_DATA(cmsg
), msglen
, valbuff
, vallen
, 0);
503 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
505 struct in_pktinfo
*pktinfo
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
507 typbuff
[0] = '\0'; strncat(typbuff
, "IP_PKTINFO", typlen
-1);
508 snprintf(nambuff
, namlen
, "%s%c%s%c%s", "if", '\0', "locaddr", '\0', "dstaddr");
509 snprintf(envbuff
, envlen
, "%s%c%s%c%s", "IP_IF", '\0',
510 "IP_LOCADDR", '\0', "IP_DSTADDR");
511 snprintf(valbuff
, vallen
, "%s%c%s%c%s",
512 xiogetifname(pktinfo
->ipi_ifindex
, scratch1
, -1), '\0',
513 #if HAVE_PKTINFO_IPI_SPEC_DST
514 inet4addr_info(ntohl(pktinfo
->ipi_spec_dst
.s_addr
),
515 scratch2
, sizeof(scratch2
)),
520 inet4addr_info(ntohl(pktinfo
->ipi_addr
.s_addr
),
521 scratch3
, sizeof(scratch3
)));
522 Notice3("Ancillary message: interface \"%s\", locaddr=%s, dstaddr=%s",
523 xiogetifname(pktinfo
->ipi_ifindex
, scratch1
, -1),
524 #if HAVE_PKTINFO_IPI_SPEC_DST
525 inet4addr_info(ntohl(pktinfo
->ipi_spec_dst
.s_addr
),
526 scratch2
, sizeof(scratch2
)),
530 inet4addr_info(ntohl(pktinfo
->ipi_addr
.s_addr
),
531 scratch3
, sizeof(scratch3
)));
534 #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
535 #endif /* WITH_IP4 */
536 #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR
538 struct xio_extended_err
{
539 struct sock_extended_err see
;
545 struct xio_extended_err
*err
=
546 (struct xio_extended_err
*)CMSG_DATA(cmsg
);
548 typbuff
[0] = '\0'; strncat(typbuff
, "IP_RECVERR", typlen
-1);
549 snprintf(nambuff
, namlen
, "%s%c%s%c%s%c%s%c%s%c%s",
550 "errno", '\0', "origin", '\0', "type", '\0',
551 "code", '\0', "info", '\0', "data");
552 snprintf(envbuff
, envlen
, "%s%c%s%c%s%c%s%c%s%c%s",
553 "IP_RECVERR_ERRNO", '\0', "IP_RECVERR_ORIGIN", '\0',
554 "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0',
555 "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA");
556 snprintf(valbuff
, vallen
, "%u%c%u%c%u%c%u%c%u%c%u",
557 err
->see
.ee_errno
, '\0', err
->see
.ee_origin
, '\0', err
->see
.ee_type
, '\0',
558 err
->see
.ee_code
, '\0', err
->see
.ee_info
, '\0', err
->see
.ee_data
);
560 switch (err
->see
.ee_origin
) {
563 case SO_EE_ORIGIN_ICMP
:
565 inet4addr_info(ntohl(err
->data1
), addrbuff
, sizeof(addrbuff
));
566 Notice6("received ICMP from %s, type %d, code %d, info %d, data %d, resulting in errno %d",
567 addrbuff
, err
->see
.ee_type
, err
->see
.ee_code
, err
->see
.ee_info
, err
->see
.ee_data
, err
->see
.ee_errno
);
570 #endif /* WITH_IP4 */
572 case SO_EE_ORIGIN_ICMP6
:
574 Notice5("received ICMP type %d, code %d, info %d, data %d, resulting in errno %d",
575 err
->see
.ee_type
, err
->see
.ee_code
, err
->see
.ee_info
, err
->see
.ee_data
, err
->see
.ee_errno
);
578 #endif /* WITH_IP6 */
580 Notice6("received error message origin %d, type %d, code %d, info %d, data %d, generating errno %d",
581 err
->see
.ee_origin
, err
->see
.ee_type
, err
->see
.ee_code
, err
->see
.ee_info
, err
->see
.ee_data
, err
->see
.ee_errno
);
586 #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */
589 /* spec in FreeBSD: /usr/include/net/if_dl.h */
590 struct sockaddr_dl
*sadl
= (struct sockaddr_dl
*)CMSG_DATA(cmsg
);
592 typbuff
[0] = '\0'; strncat(typbuff
, "IP_RECVIF", typlen
-1);
593 nambuff
[0] = '\0'; strncat(nambuff
, "if", namlen
-1);
594 envbuff
[0] = '\0'; strncat(envbuff
, "IP_IF", envlen
-1);
597 xiosubstr(scratch1
, sadl
->sdl_data
, 0, sadl
->sdl_nlen
), vallen
-1);
598 Notice1("IP_RECVIF: %s", valbuff
);
601 #endif /* defined(IP_RECVIF) */
603 #ifdef IP_RECVDSTADDR
606 typbuff
[0] = '\0'; strncat(typbuff
, "IP_RECVDSTADDR", typlen
-1);
607 nambuff
[0] = '\0'; strncat(nambuff
, "dstaddr", namlen
-1);
608 envbuff
[0] = '\0'; strncat(envbuff
, "IP_DSTADDR", envlen
-1);
609 inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg
)), valbuff
, vallen
);
610 Notice1("IP_RECVDSTADDR: %s", valbuff
);
613 #endif /* WITH_IP4 */
618 cmsgtype
= "IP_OPTIONS"; cmsgname
= "options"; cmsgctr
= -1;
622 cmsgtype
= "IP_TOS"; cmsgname
= "tos"; cmsgctr
= msglen
;
624 case IP_TTL
: /* Linux */
626 case IP_RECVTTL
: /* FreeBSD */
628 cmsgtype
= "IP_TTL"; cmsgname
= "ttl"; cmsgctr
= msglen
; break;
630 /* when we come here we provide a single parameter
631 with name in cmsgname, value length in msglen */
633 if (strlen(cmsgtype
) >= typlen
) rc
= STAT_WARNING
;
634 typbuff
[0] = '\0'; strncat(typbuff
, cmsgtype
, typlen
-1);
635 if (strlen(cmsgname
) >= namlen
) rc
= STAT_WARNING
;
636 nambuff
[0] = '\0'; strncat(nambuff
, cmsgname
, namlen
-1);
638 if (strlen(cmsgenvn
) >= envlen
) rc
= STAT_WARNING
;
639 envbuff
[0] = '\0'; strncat(envbuff
, cmsgenvn
, envlen
-1);
645 snprintf(valbuff
, vallen
, "%u", *(unsigned char *)CMSG_DATA(cmsg
));
646 Notice2("Ancillary message: %s=%u", cmsgname
, *(unsigned char *)CMSG_DATA(cmsg
));
649 snprintf(valbuff
, vallen
, "%u", (*(unsigned int *)CMSG_DATA(cmsg
)));
650 Notice2("Ancillary message: %s=%u", cmsgname
, *(unsigned int *)CMSG_DATA(cmsg
));
653 xiodump(CMSG_DATA(cmsg
), msglen
, valbuff
, vallen
, 0); break;
658 #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */
661 #if HAVE_STRUCT_IP_MREQ_SOURCE
662 int xiotype_ip_add_source_membership(char *token
, const struct optname
*ent
, struct opt
*opt
) {
663 /* we do not resolve the addresses here because we do not yet know
664 if we are coping with an IPv4 or IPv6 socat address */
665 const char *ends
[] = { ":", NULL
};
666 const char *nests
[] = { "[","]", NULL
};
667 char buff
[512], *buffp
=buff
; size_t bufspc
= sizeof(buff
)-1;
671 /* parse first IP address, expect ':' */
673 nestlex((const char **)&tokp
, &buffp
, &bufspc
,
674 ends
, NULL
, NULL
, nests
,
677 Error1("option too long: \"%s\"", token
);
679 } else if (parsres
> 0) {
680 Error1("syntax error in \"%s\"", token
);
684 Error1("syntax in option %s: missing ':'", token
);
687 opt
->value
.u_ip_mreq_source
.mcaddr
= strdup(buff
); /*!!! NULL */
690 /* parse second IP address, expect ':' or '\0'' */
694 nestlex((const char **)&tokp
, &buffp
, &bufspc
,
695 ends
, NULL
, NULL
, nests
,
698 Error1("option too long: \"%s\"", token
);
700 } else if (parsres
> 0) {
701 Error1("syntax error in \"%s\"", token
);
705 Error1("syntax in option %s: missing ':'", token
);
708 opt
->value
.u_ip_mreq_source
.ifaddr
= strdup(buff
); /*!!! NULL */
711 /* parse third IP address, expect ':' or '\0'' */
715 nestlex((const char **)&tokp
, &buffp
, &bufspc
,
716 ends
, NULL
, NULL
, nests
,
719 Error1("option too long: \"%s\"", token
);
721 } else if (parsres
> 0) {
722 Error1("syntax error in \"%s\"", token
);
726 Error1("syntax in option %s: trailing cruft", token
);
729 opt
->value
.u_ip_mreq_source
.srcaddr
= strdup(buff
); /*!!! NULL */
731 Info4("setting option \"%s\" to {0x%08x,0x%08x,0x%08x}",
733 ntohl(*(unsigned int *)opt
->value
.u_ip_mreq_source
.mcaddr
),
734 ntohl(*(unsigned int *)opt
->value
.u_ip_mreq_source
.ifaddr
),
735 ntohl(*(unsigned int *)opt
->value
.u_ip_mreq_source
.srcaddr
));
739 int xioapply_ip_add_source_membership(struct single
*xfd
, struct opt
*opt
) {
740 struct ip_mreq_source ip4_mreq_src
= {{0}};
741 /* IPv6 not supported - seems to have different handling */
742 union sockaddr_union sockaddr1
;
743 socklen_t socklen1
= sizeof(sockaddr1
.ip4
);
744 union sockaddr_union sockaddr2
;
745 socklen_t socklen2
= sizeof(sockaddr2
.ip4
);
746 union sockaddr_union sockaddr3
;
747 socklen_t socklen3
= sizeof(sockaddr3
.ip4
);
749 /* first parameter is always multicast address */
751 xiogetaddrinfo(opt
->value
.u_ip_mreq_source
.mcaddr
, NULL
,
752 xfd
->para
.socket
.la
.soa
.sa_family
,
753 SOCK_DGRAM
, IPPROTO_IP
,
754 &sockaddr1
, &socklen1
, 0, 0);
755 ip4_mreq_src
.imr_multiaddr
= sockaddr1
.ip4
.sin_addr
;
756 /* second parameter is interface address */
757 xiogetaddrinfo(opt
->value
.u_ip_mreq_source
.ifaddr
, NULL
,
758 xfd
->para
.socket
.la
.soa
.sa_family
,
759 SOCK_DGRAM
, IPPROTO_IP
,
760 &sockaddr2
, &socklen2
, 0, 0);
761 ip4_mreq_src
.imr_interface
= sockaddr2
.ip4
.sin_addr
;
762 /* third parameter is source address */
763 xiogetaddrinfo(opt
->value
.u_ip_mreq_source
.srcaddr
, NULL
,
764 xfd
->para
.socket
.la
.soa
.sa_family
,
765 SOCK_DGRAM
, IPPROTO_IP
,
766 &sockaddr3
, &socklen3
, 0, 0);
767 ip4_mreq_src
.imr_sourceaddr
= sockaddr3
.ip4
.sin_addr
;
768 if (Setsockopt(xfd
->fd
, opt
->desc
->major
, opt
->desc
->minor
,
769 &ip4_mreq_src
, sizeof(ip4_mreq_src
)) < 0) {
770 Error8("setsockopt(%d, %d, %d, {0x%08x,0x%08x,0x%08x}, "F_Zu
"): %s",
771 xfd
->fd
, opt
->desc
->major
, opt
->desc
->minor
,
772 htonl((uint32_t)ip4_mreq_src
.imr_multiaddr
.s_addr
),
773 ip4_mreq_src
.imr_interface
.s_addr
,
774 ip4_mreq_src
.imr_sourceaddr
.s_addr
,
775 sizeof(struct ip_mreq_source
),
777 opt
->desc
= ODESC_ERROR
;
782 #endif /* HAVE_STRUCT_IP_MREQ_SOURCE */
784 #endif /* _WITH_IP4 || _WITH_IP6 */