2 /* Copyright Gerhard Rieger 2001-2008 */
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"
19 #if WITH_IP4 || WITH_IP6
22 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
};
25 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
};
28 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
};
30 #ifdef IP_RECVTTL /* -Cygwin */
31 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
};
34 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
};
37 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
};
39 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
};
40 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
};
42 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
};
45 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
};
47 #ifdef IP_MTU_DISCOVER
48 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
};
51 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
};
54 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
};
56 #ifdef IP_ROUTER_ALERT
57 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
};
59 /* following: Linux allows int but OpenBSD reqs char/byte */
60 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
};
61 /* following: Linux allows int but OpenBSD reqs char/byte */
62 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
};
63 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
};
65 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
};
67 #ifdef IP_ADD_MEMBERSHIP
68 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
};
71 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
};
74 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
};
78 const struct optdesc opt_res_debug
= { "res-debug", NULL
, OPT_RES_DEBUG
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_DEBUG
};
79 const struct optdesc opt_res_aaonly
= { "res-aaonly", "aaonly", OPT_RES_AAONLY
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_AAONLY
};
80 const struct optdesc opt_res_usevc
= { "res-usevc", "usevc", OPT_RES_USEVC
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_USEVC
};
81 const struct optdesc opt_res_primary
= { "res-primary", "primary", OPT_RES_PRIMARY
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_PRIMARY
};
82 const struct optdesc opt_res_igntc
= { "res-igntc", "igntc", OPT_RES_IGNTC
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_IGNTC
};
83 const struct optdesc opt_res_recurse
= { "res-recurse", "recurse", OPT_RES_RECURSE
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_RECURSE
};
84 const struct optdesc opt_res_defnames
= { "res-defnames", "defnames", OPT_RES_DEFNAMES
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_DEFNAMES
};
85 const struct optdesc opt_res_stayopen
= { "res-stayopen", "stayopen", OPT_RES_STAYOPEN
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_STAYOPEN
};
86 const struct optdesc opt_res_dnsrch
= { "res-dnsrch", "dnsrch", OPT_RES_DNSRCH
, GROUP_SOCK_IP
, PH_INIT
, TYPE_BOOL
, OFUNC_OFFSET_MASKS
, (size_t)&((xiosingle_t
*)0)->para
.socket
.ip
.res_opts
, sizeof(unsigned long), RES_DNSRCH
};
87 #endif /* HAVE_RESOLV_H */
89 #endif /* WITH_IP4 || WITH_IP6 */
97 Debug1("res_init() -> %d", result
);
100 #endif /* HAVE_RESOLV_H */
103 unsigned long res_opts() {
106 #endif /* HAVE_RESOLV_H */
108 /* the ultimate(?) socat resolver function
109 node: the address to be resolved; supported forms:
110 1.2.3.4 (IPv4 address)
112 hostname (hostname resolving to IPv4 or IPv6 address)
113 hostname.domain (fq hostname resolving to IPv4 or IPv6 address)
114 service: the port specification; may be numeric or symbolic
115 family: PF_INET, PF_INET6, or PF_UNSPEC permitting both
116 socktype: SOCK_STREAM, SOCK_DGRAM
117 protocol: IPPROTO_UDP, IPPROTO_TCP
118 sau: an uninitialized storage for the resulting socket address
119 returns: STAT_OK, STAT_RETRYLATER
121 int xiogetaddrinfo(const char *node
, const char *service
,
122 int family
, int socktype
, int protocol
,
123 union sockaddr_union
*sau
, socklen_t
*socklen
,
124 unsigned long res_opts0
, unsigned long res_opts1
) {
125 int port
= -1; /* port number in network byte order */
126 char *numnode
= NULL
;
128 unsigned long save_res_opts
= 0;
130 struct addrinfo hints
= {0};
131 struct addrinfo
*res
= NULL
;
132 #else /* HAVE_GETIPNODEBYNAME || nothing */
133 struct hostent
*host
;
138 if (res_opts0
| res_opts1
) {
139 if (!(_res
.options
& RES_INIT
)) {
140 Res_init(); /*!!! returns -1 on error */
142 save_res_opts
= _res
.options
;
143 _res
.options
&= ~res_opts0
;
144 _res
.options
|= res_opts1
;
145 Debug2("changed _res.options from 0x%lx to 0x%lx",
146 save_res_opts
, _res
.options
);
148 #endif /* HAVE_RESOLV_H */
149 memset(sau
, 0, *socklen
);
150 sau
->soa
.sa_family
= family
;
152 /* if service is numeric we don't want to have a lookup (might take long
153 with NIS), so we handle this specially */
154 if (service
&& isdigit(service
[0]&0xff)) {
156 port
= htons(strtoul(service
, &extra
, 0));
157 if (*extra
!= '\0') {
158 Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"",
164 /* the resolver functions might handle numeric forms of node names by
165 reverse lookup, that's not what we want.
166 So we detect these and handle them specially */
167 if (node
&& isdigit(node
[0]&0xff)) {
169 hints
.ai_flags
|= AI_NUMERICHOST
;
170 #endif /* HAVE_GETADDRINFO */
171 if (family
== PF_UNSPEC
) {
174 } else if (family
== PF_INET6
) {
175 /* map "explicitely" into IPv6 address space; getipnodebyname() does
176 this with AI_V4MAPPED, but not getaddrinfo() */
177 if ((numnode
= Malloc(strlen(node
)+7+1)) == NULL
) {
179 if (res_opts0
| res_opts1
) {
180 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
181 save_res_opts
& ( res_opts0
| res_opts1
));
186 sprintf(numnode
, "::ffff:%s", node
);
188 hints
.ai_flags
|= AI_NUMERICHOST
;
189 #endif /* HAVE_GETADDRINFO */
192 } else if (node
&& node
[0] == '[' && node
[(nodelen
=strlen(node
))-1]==']') {
193 if ((numnode
= Malloc(nodelen
-1)) == NULL
) {
195 if (res_opts0
| res_opts1
) {
196 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
197 save_res_opts
& ( res_opts0
| res_opts1
));
202 strncpy(numnode
, node
+1, nodelen
-2);
203 numnode
[nodelen
-2] = '\0';
206 hints
.ai_flags
|= AI_NUMERICHOST
;
207 #endif /* HAVE_GETADDRINFO */
208 if (family
== PF_UNSPEC
) family
= PF_INET6
;
209 #endif /* WITH_IP6 */
213 if (node
!= NULL
|| service
!= NULL
) {
214 struct addrinfo
*record
;
216 if (socktype
!= SOCK_STREAM
&& socktype
!= SOCK_DGRAM
) {
217 /* actual socket type value is not supported - fallback to a good one */
218 socktype
= SOCK_DGRAM
;
220 if (protocol
!= IPPROTO_TCP
&& protocol
!= IPPROTO_UDP
) {
221 /* actual protocol value is not supported - fallback to a good one */
222 if (socktype
== SOCK_DGRAM
) {
223 protocol
= IPPROTO_UDP
;
225 protocol
= IPPROTO_TCP
;
228 hints
.ai_flags
|= AI_PASSIVE
;
229 hints
.ai_family
= family
;
230 hints
.ai_socktype
= socktype
;
231 hints
.ai_protocol
= protocol
;
232 hints
.ai_addrlen
= 0;
233 hints
.ai_addr
= NULL
;
234 hints
.ai_canonname
= NULL
;
235 hints
.ai_next
= NULL
;
237 if ((error_num
= Getaddrinfo(node
, service
, &hints
, &res
)) != 0) {
238 Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s",
239 node
, service
, hints
.ai_flags
, hints
.ai_family
,
240 hints
.ai_socktype
, hints
.ai_protocol
,
241 (error_num
== EAI_SYSTEM
)?
242 strerror(errno
):gai_strerror(error_num
));
243 if (res
!= NULL
) freeaddrinfo(res
);
244 if (numnode
) free(numnode
);
247 if (res_opts0
| res_opts1
) {
248 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
249 save_res_opts
& ( res_opts0
| res_opts1
));
252 return STAT_RETRYLATER
;
254 service
= NULL
; /* do not resolve later again */
257 if (family
== PF_UNSPEC
&& xioopts
.preferred_ip
== '0') {
258 /* we just take the first result */
259 family
= res
[0].ai_addr
->sa_family
;
261 if (family
== PF_UNSPEC
) {
263 trypf
= (xioopts
.preferred_ip
=='6'?PF_INET6
:PF_INET
);
264 /* we must look for a matching entry */
265 while (record
!= NULL
) {
266 if (record
->ai_family
== trypf
) {
268 break; /* family and record set accordingly */
270 record
= record
->ai_next
;
272 if (record
== NULL
) {
273 /* we did not find a "preferred" entry, take the first */
275 family
= res
[0].ai_addr
->sa_family
;
282 if (*socklen
> record
->ai_addrlen
) {
283 *socklen
= record
->ai_addrlen
;
285 memcpy(&sau
->ip4
, record
->ai_addr
, *socklen
);
287 #endif /* WITH_IP4 */
291 /* older AIX versions pass wrong length, so we correct it */
292 record
->ai_addr
->sa_len
= sizeof(struct sockaddr_in6
);
294 if (*socklen
> record
->ai_addrlen
) {
295 *socklen
= record
->ai_addrlen
;
297 memcpy(&sau
->ip6
, record
->ai_addr
, *socklen
);
299 #endif /* WITH_IP6 */
301 Error1("address resolved to unknown protocol family %d",
302 record
->ai_addr
->sa_family
);
309 case PF_INET
: *socklen
= sizeof(sau
->ip4
); break;
310 #endif /* WITH_IP4 */
312 case PF_INET6
: *socklen
= sizeof(sau
->ip6
); break;
313 #endif /* WITH_IP6 */
317 #elif HAVE_GETIPNODEBYNAME /* !HAVE_GETADDRINFO */
320 /* first fallback is getipnodebyname() */
321 if (family
== PF_UNSPEC
) {
322 #if WITH_IP4 && WITH_IP6
323 family
= xioopts
.default_ip
=='6'?PF_INET6
:PF_INET
;
330 host
= Getipnodebyname(node
, family
, AI_V4MAPPED
, &error_num
);
332 const static char ai_host_not_found
[] = "Host not found";
333 const static char ai_no_address
[] = "No address";
334 const static char ai_no_recovery
[] = "No recovery";
335 const static char ai_try_again
[] = "Try again";
336 const char *error_msg
= "Unknown error";
338 case HOST_NOT_FOUND
: error_msg
= ai_host_not_found
; break;
339 case NO_ADDRESS
: error_msg
= ai_no_address
;
340 case NO_RECOVERY
: error_msg
= ai_no_recovery
;
341 case TRY_AGAIN
: error_msg
= ai_try_again
;
343 Error2("getipnodebyname(\"%s\", ...): %s", node
, error_msg
);
348 *socklen
= sizeof(sau
->ip4
);
349 sau
->soa
.sa_family
= PF_INET
;
350 memcpy(&sau
->ip4
.sin_addr
, host
->h_addr_list
[0], 4);
355 *socklen
= sizeof(sau
->ip6
);
356 sau
->soa
.sa_family
= PF_INET6
;
357 memcpy(&sau
->ip6
.sin6_addr
, host
->h_addr_list
[0], 16);
365 #else /* !HAVE_GETIPNODEBYNAME */
368 /* this is not a typical IP6 resolver function - but Linux
369 "man gethostbyname" says that the only supported address type with
370 this function is AF_INET _at present_, so maybe this fallback will
371 be useful somewhere sometimesin a future even for IP6 */
372 if (family
== PF_UNSPEC
) {
373 #if WITH_IP4 && WITH_IP6
374 family
= xioopts
.default_ip
=='6'?PF_INET6
:PF_INET
;
381 /*!!! try gethostbyname2 for IP6 */
382 if ((host
= Gethostbyname(node
)) == NULL
) {
383 Error2("gethostbyname(\"%s\"): %s", node
,
384 h_errno
== NETDB_INTERNAL
? strerror(errno
) :
387 if (res_opts0
| res_opts1
) {
388 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
389 save_res_opts
& ( res_opts0
| res_opts1
));
392 return STAT_RETRYLATER
;
394 if (host
->h_addrtype
!= family
) {
395 Error2("xioaddrinfo(): \"%s\" does not resolve to %s",
396 node
, family
==PF_INET
?"IP4":"IP6");
401 *socklen
= sizeof(sau
->ip4
);
402 sau
->soa
.sa_family
= PF_INET
;
403 memcpy(&sau
->ip4
.sin_addr
, host
->h_addr_list
[0], 4);
405 #endif /* WITH_IP4 */
408 *socklen
= sizeof(sau
->ip6
);
409 sau
->soa
.sa_family
= PF_INET6
;
410 memcpy(&sau
->ip6
.sin6_addr
, host
->h_addr_list
[0], 16);
412 #endif /* WITH_IP6 */
419 #if WITH_TCP || WITH_UDP
421 port
= parseport(service
, protocol
);
426 case PF_INET
: sau
->ip4
.sin_port
= port
; break;
427 #endif /* WITH_IP4 */
429 case PF_INET6
: sau
->ip6
.sin6_port
= port
; break;
430 #endif /* WITH_IP6 */
433 #endif /* WITH_TCP || WITH_UDP */
435 if (numnode
) free(numnode
);
438 if (res_opts0
| res_opts1
) {
439 _res
.options
= (_res
.options
& (~res_opts0
&~res_opts1
) |
440 save_res_opts
& ( res_opts0
| res_opts1
));
442 #endif /* HAVE_RESOLV_H */
447 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
448 /* these are valid for IPv4 and IPv6 */
449 int xiolog_ancillary_ip(struct cmsghdr
*cmsg
, int *num
,
450 char *typbuff
, int typlen
,
451 char *nambuff
, int namlen
,
452 char *envbuff
, int envlen
,
453 char *valbuff
, int vallen
) {
454 const char *cmsgtype
, *cmsgname
= NULL
, *cmsgenvn
= NULL
, *cmsgfmt
= NULL
;
456 char scratch1
[16]; /* can hold an IPv4 address in ASCII */
460 msglen
= cmsg
->cmsg_len
-((char *)CMSG_DATA(cmsg
)-(char *)cmsg
);
462 switch (cmsg
->cmsg_type
) {
465 strncpy(typbuff
, "IP", typlen
);
466 snprintf(nambuff
, namlen
, "type_%u", cmsg
->cmsg_type
);
467 xiodump(CMSG_DATA(cmsg
), msglen
, valbuff
, vallen
, 0);
470 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
472 struct in_pktinfo
*pktinfo
= (struct in_pktinfo
*)CMSG_DATA(cmsg
);
474 strncpy(typbuff
, "IP_PKTINFO", typlen
);
475 snprintf(nambuff
, namlen
, "%s%c%s%c%s", "if", '\0', "locaddr", '\0', "dstaddr");
476 snprintf(envbuff
, envlen
, "%s%c%s%c%s", "IP_IF", '\0',
477 "IP_LOCADDR", '\0', "IP_DSTADDR");
478 snprintf(valbuff
, vallen
, "%s%c%s%c%s",
479 xiogetifname(pktinfo
->ipi_ifindex
, scratch1
, -1), '\0',
480 inet4addr_info(ntohl(pktinfo
->ipi_spec_dst
.s_addr
), scratch2
, sizeof(scratch2
)), '\0',
481 inet4addr_info(ntohl(pktinfo
->ipi_addr
.s_addr
), scratch3
, sizeof(scratch3
)));
484 #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
485 #endif /* WITH_IP4 */
488 struct sock_extended_err
*err
=
489 (struct sock_extended_err
*)CMSG_DATA(cmsg
);
491 strncpy(typbuff
, "IP_RECVERR", typlen
);
492 snprintf(nambuff
, namlen
, "%s%c%s%c%s%c%s%c%s%c%s",
493 "errno", '\0', "origin", '\0', "type", '\0',
494 "code", '\0', "info", '\0', "data");
495 snprintf(envbuff
, envlen
, "%s%c%s%c%s%c%s%c%s%c%s",
496 "IP_RECVERR_ERRNO", '\0', "IP_RECVERR_ORIGIN", '\0',
497 "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0',
498 "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA");
499 snprintf(valbuff
, vallen
, "%u%c%u%c%u%c%u%c%u%c%u",
500 err
->ee_errno
, '\0', err
->ee_origin
, '\0', err
->ee_type
, '\0',
501 err
->ee_code
, '\0', err
->ee_info
, '\0', err
->ee_data
);
504 #endif /* IP_RECVERR */
507 /* spec in FreeBSD: /usr/include/net/if_dl.h */
508 struct sockaddr_dl
*sadl
= (struct sockaddr_dl
*)CMSG_DATA(cmsg
);
510 strncpy(typbuff
, "IP_RECVIF", typlen
);
511 strncpy(nambuff
, "if", namlen
);
512 strncpy(envbuff
, "IP_IF", envlen
);
514 xiosubstr(scratch1
, sadl
->sdl_data
, 0, sadl
->sdl_nlen
), vallen
);
517 #endif /* defined(IP_RECVIF) */
519 #ifdef IP_RECVDSTADDR
522 strncpy(typbuff
, "IP_RECVDSTADDR", typlen
);
523 strncpy(nambuff
, "dstaddr", namlen
);
524 strncpy(envbuff
, "IP_DSTADDR", envlen
);
525 inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg
)), valbuff
, vallen
);
528 #endif /* WITH_IP4 */
533 cmsgtype
= "IP_OPTIONS"; cmsgname
= "options"; cmsgfmt
= NULL
; break;
535 cmsgtype
= "IP_TOS"; cmsgname
= "tos"; cmsgfmt
= "%u"; break;
536 case IP_TTL
: /* Linux */
538 case IP_RECVTTL
: /* FreeBSD */
540 cmsgtype
= "IP_TTL"; cmsgname
= "ttl"; cmsgfmt
= "%u"; break;
542 /* when we come here we provide a single parameter
543 with type in cmsgtype, name in cmsgname, printf format in cmsgfmt */
545 if (strlen(cmsgtype
) >= typlen
) Fatal("buff too short");
546 strncpy(typbuff
, cmsgtype
, typlen
);
547 if (strlen(cmsgname
) >= namlen
) Fatal("buff too short");
548 strncpy(nambuff
, cmsgname
, namlen
);
550 if (strlen(cmsgenvn
) >= envlen
) Fatal("buff too short");
551 strncpy(envbuff
, cmsgenvn
, envlen
);
555 if (cmsgfmt
!= NULL
) {
556 snprintf(valbuff
, vallen
, cmsgfmt
, *(unsigned char *)CMSG_DATA(cmsg
));
558 xiodump(CMSG_DATA(cmsg
), msglen
, valbuff
, vallen
, 0);
562 #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */
564 #endif /* _WITH_IP4 || _WITH_IP6 */