version 1.7.3.0
[socat.git] / xio-ip.c
blob0c9141dfe237e8e9c20091d171e2ed75a5078479
1 /* source: xio-ip.c */
2 /* Copyright Gerhard Rieger */
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
11 #include "xioopen.h"
13 #include "xio-ascii.h"
14 #include "xio-socket.h"
15 #include "xio-ip.h"
16 #include "xio-ip6.h"
19 #if WITH_IP4 || WITH_IP6
21 #ifdef IP_OPTIONS
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 };
23 #endif
24 #ifdef IP_PKTINFO
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 };
26 #endif
27 #ifdef IP_RECVTOS
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 };
29 #endif
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 };
32 #endif
33 #ifdef IP_RECVOPTS
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 };
35 #endif
36 #ifdef IP_RETOPTS
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 };
38 #endif
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 };
41 #ifdef IP_HDRINCL
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 };
43 #endif
44 #ifdef IP_RECVERR
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 };
46 #endif
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 };
49 #endif
50 #ifdef IP_MTU
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 };
52 #endif
53 #ifdef IP_FREEBIND
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 };
55 #endif
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};
58 #endif
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};
64 #ifdef IP_PKTOPTIONS
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 };
66 #endif
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 };
69 #endif
70 #ifdef IP_RECVDSTADDR
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 };
72 #endif
73 #ifdef IP_RECVIF
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 };
75 #endif
77 #if HAVE_RESOLV_H
78 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 };
79 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 };
80 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 };
81 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 };
82 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 };
83 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 };
84 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 };
85 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 };
86 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 };
87 #endif /* HAVE_RESOLV_H */
89 #endif /* WITH_IP4 || WITH_IP6 */
92 #if HAVE_RESOLV_H
93 int Res_init(void) {
94 int result;
95 Debug("res_init()");
96 result = res_init();
97 Debug1("res_init() -> %d", result);
98 return result;
100 #endif /* HAVE_RESOLV_H */
102 #if HAVE_RESOLV_H
103 unsigned long res_opts() {
104 return _res.options;
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)
111 [::2] (IPv6 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;
127 size_t nodelen;
128 unsigned long save_res_opts = 0;
129 #if HAVE_GETADDRINFO
130 struct addrinfo hints = {0};
131 struct addrinfo *res = NULL;
132 #else /* HAVE_PROTOTYPE_LIB_getipnodebyname || nothing */
133 struct hostent *host;
134 #endif
135 int error_num;
137 #if HAVE_RESOLV_H
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 && service[0]=='\0') {
153 Error("empty port/service");
155 /* if service is numeric we don't want to have a lookup (might take long
156 with NIS), so we handle this specially */
157 if (service && isdigit(service[0]&0xff)) {
158 char *extra;
159 port = htons(strtoul(service, &extra, 0));
160 if (*extra != '\0') {
161 Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"",
162 service, extra);
164 service = NULL;
167 /* the resolver functions might handle numeric forms of node names by
168 reverse lookup, that's not what we want.
169 So we detect these and handle them specially */
170 if (node && isdigit(node[0]&0xff)) {
171 #if HAVE_GETADDRINFO
172 hints.ai_flags |= AI_NUMERICHOST;
173 #endif /* HAVE_GETADDRINFO */
174 if (family == PF_UNSPEC) {
175 family = PF_INET;
176 #if HAVE_GETADDRINFO
177 } else if (family == PF_INET6) {
178 /* map "explicitely" into IPv6 address space; getipnodebyname() does
179 this with AI_V4MAPPED, but not getaddrinfo() */
180 if ((numnode = Malloc(strlen(node)+7+1)) == NULL) {
181 #if HAVE_RESOLV_H
182 if (res_opts0 | res_opts1) {
183 _res.options = (_res.options & (~res_opts0&~res_opts1) |
184 save_res_opts& ( res_opts0| res_opts1));
186 #endif
187 return STAT_NORETRY;
189 sprintf(numnode, "::ffff:%s", node);
190 node = numnode;
191 hints.ai_flags |= AI_NUMERICHOST;
192 #endif /* HAVE_GETADDRINFO */
194 #if WITH_IP6
195 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') {
196 if ((numnode = Malloc(nodelen-1)) == NULL) {
197 #if HAVE_RESOLV_H
198 if (res_opts0 | res_opts1) {
199 _res.options = (_res.options & (~res_opts0&~res_opts1) |
200 save_res_opts& ( res_opts0| res_opts1));
202 #endif
203 return STAT_NORETRY;
205 strncpy(numnode, node+1, nodelen-2); /* ok */
206 numnode[nodelen-2] = '\0';
207 node = numnode;
208 #if HAVE_GETADDRINFO
209 hints.ai_flags |= AI_NUMERICHOST;
210 #endif /* HAVE_GETADDRINFO */
211 if (family == PF_UNSPEC) family = PF_INET6;
212 #endif /* WITH_IP6 */
215 #if HAVE_GETADDRINFO
216 if (node != NULL || service != NULL) {
217 struct addrinfo *record;
219 if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) {
220 /* actual socket type value is not supported - fallback to a good one */
221 socktype = SOCK_DGRAM;
223 if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) {
224 /* actual protocol value is not supported - fallback to a good one */
225 if (socktype == SOCK_DGRAM) {
226 protocol = IPPROTO_UDP;
227 } else {
228 protocol = IPPROTO_TCP;
231 hints.ai_flags |= AI_PASSIVE;
232 hints.ai_family = family;
233 hints.ai_socktype = socktype;
234 hints.ai_protocol = protocol;
235 hints.ai_addrlen = 0;
236 hints.ai_addr = NULL;
237 hints.ai_canonname = NULL;
238 hints.ai_next = NULL;
240 if ((error_num = Getaddrinfo(node, service, &hints, &res)) != 0) {
241 Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s",
242 node, service, hints.ai_flags, hints.ai_family,
243 hints.ai_socktype, hints.ai_protocol,
244 (error_num == EAI_SYSTEM)?
245 strerror(errno):gai_strerror(error_num));
246 if (res != NULL) freeaddrinfo(res);
247 if (numnode) free(numnode);
249 #if HAVE_RESOLV_H
250 if (res_opts0 | res_opts1) {
251 _res.options = (_res.options & (~res_opts0&~res_opts1) |
252 save_res_opts& ( res_opts0| res_opts1));
254 #endif
255 return STAT_RETRYLATER;
257 service = NULL; /* do not resolve later again */
259 record = res;
260 if (family == PF_UNSPEC && xioopts.preferred_ip == '0') {
261 /* we just take the first result */
262 family = res[0].ai_addr->sa_family;
264 if (family == PF_UNSPEC) {
265 int trypf;
266 trypf = (xioopts.preferred_ip=='6'?PF_INET6:PF_INET);
267 /* we must look for a matching entry */
268 while (record != NULL) {
269 if (record->ai_family == trypf) {
270 family = trypf;
271 break; /* family and record set accordingly */
273 record = record->ai_next;
275 if (record == NULL) {
276 /* we did not find a "preferred" entry, take the first */
277 record = res;
278 family = res[0].ai_addr->sa_family;
282 switch (family) {
283 #if WITH_IP4
284 case PF_INET:
285 if (*socklen > record->ai_addrlen) {
286 *socklen = record->ai_addrlen;
288 memcpy(&sau->ip4, record->ai_addr, *socklen);
289 break;
290 #endif /* WITH_IP4 */
291 #if WITH_IP6
292 case PF_INET6:
293 #if _AIX
294 /* older AIX versions pass wrong length, so we correct it */
295 record->ai_addr->sa_len = sizeof(struct sockaddr_in6);
296 #endif
297 if (*socklen > record->ai_addrlen) {
298 *socklen = record->ai_addrlen;
300 memcpy(&sau->ip6, record->ai_addr, *socklen);
301 break;
302 #endif /* WITH_IP6 */
303 default:
304 Error1("address resolved to unknown protocol family %d",
305 record->ai_addr->sa_family);
306 break;
308 freeaddrinfo(res);
309 } else {
310 switch (family) {
311 #if WITH_IP4
312 case PF_INET: *socklen = sizeof(sau->ip4); break;
313 #endif /* WITH_IP4 */
314 #if WITH_IP6
315 case PF_INET6: *socklen = sizeof(sau->ip6); break;
316 #endif /* WITH_IP6 */
320 #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */
322 if (node != NULL) {
323 /* first fallback is getipnodebyname() */
324 if (family == PF_UNSPEC) {
325 #if WITH_IP4 && WITH_IP6
326 family = xioopts.default_ip=='6'?PF_INET6:PF_INET;
327 #elif WITH_IP6
328 family = PF_INET6;
329 #else
330 family = PF_INET;
331 #endif
333 host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num);
334 if (host == NULL) {
335 const static char ai_host_not_found[] = "Host not found";
336 const static char ai_no_address[] = "No address";
337 const static char ai_no_recovery[] = "No recovery";
338 const static char ai_try_again[] = "Try again";
339 const char *error_msg = "Unknown error";
340 switch (error_num) {
341 case HOST_NOT_FOUND: error_msg = ai_host_not_found; break;
342 case NO_ADDRESS: error_msg = ai_no_address;
343 case NO_RECOVERY: error_msg = ai_no_recovery;
344 case TRY_AGAIN: error_msg = ai_try_again;
346 Error2("getipnodebyname(\"%s\", ...): %s", node, error_msg);
347 } else {
348 switch (family) {
349 #if WITH_IP4
350 case PF_INET:
351 *socklen = sizeof(sau->ip4);
352 sau->soa.sa_family = PF_INET;
353 memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4);
354 break;
355 #endif
356 #if WITH_IP6
357 case PF_INET6:
358 *socklen = sizeof(sau->ip6);
359 sau->soa.sa_family = PF_INET6;
360 memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16);
361 break;
362 #endif
365 freehostent(host);
368 #else /* !HAVE_PROTOTYPE_LIB_getipnodebyname */
370 if (node != NULL) {
371 /* this is not a typical IP6 resolver function - but Linux
372 "man gethostbyname" says that the only supported address type with
373 this function is AF_INET _at present_, so maybe this fallback will
374 be useful somewhere sometimesin a future even for IP6 */
375 if (family == PF_UNSPEC) {
376 #if WITH_IP4 && WITH_IP6
377 family = xioopts.default_ip=='6'?PF_INET6:PF_INET;
378 #elif WITH_IP6
379 family = PF_INET6;
380 #else
381 family = PF_INET;
382 #endif
384 /*!!! try gethostbyname2 for IP6 */
385 if ((host = Gethostbyname(node)) == NULL) {
386 Error2("gethostbyname(\"%s\"): %s", node,
387 h_errno == NETDB_INTERNAL ? strerror(errno) :
388 hstrerror(h_errno));
389 #if HAVE_RESOLV_H
390 if (res_opts0 | res_opts1) {
391 _res.options = (_res.options & (~res_opts0&~res_opts1) |
392 save_res_opts& ( res_opts0| res_opts1));
394 #endif
395 return STAT_RETRYLATER;
397 if (host->h_addrtype != family) {
398 Error2("xioaddrinfo(): \"%s\" does not resolve to %s",
399 node, family==PF_INET?"IP4":"IP6");
400 } else {
401 switch (family) {
402 #if WITH_IP4
403 case PF_INET:
404 *socklen = sizeof(sau->ip4);
405 sau->soa.sa_family = PF_INET;
406 memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4);
407 break;
408 #endif /* WITH_IP4 */
409 #if WITH_IP6
410 case PF_INET6:
411 *socklen = sizeof(sau->ip6);
412 sau->soa.sa_family = PF_INET6;
413 memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16);
414 break;
415 #endif /* WITH_IP6 */
420 #endif
422 #if WITH_TCP || WITH_UDP
423 if (service) {
424 port = parseport(service, protocol);
426 if (port >= 0) {
427 switch (family) {
428 #if WITH_IP4
429 case PF_INET: sau->ip4.sin_port = port; break;
430 #endif /* WITH_IP4 */
431 #if WITH_IP6
432 case PF_INET6: sau->ip6.sin6_port = port; break;
433 #endif /* WITH_IP6 */
436 #endif /* WITH_TCP || WITH_UDP */
438 if (numnode) free(numnode);
440 #if HAVE_RESOLV_H
441 if (res_opts0 | res_opts1) {
442 _res.options = (_res.options & (~res_opts0&~res_opts1) |
443 save_res_opts& ( res_opts0| res_opts1));
445 #endif /* HAVE_RESOLV_H */
446 return STAT_OK;
450 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
451 /* converts the ancillary message in *cmsg into a form useable for further
452 processing. knows the specifics of common message types.
453 these are valid for IPv4 and IPv6
454 returns the number of resulting syntax elements in *num
455 returns a sequence of \0 terminated type strings in *typbuff
456 returns a sequence of \0 terminated name strings in *nambuff
457 returns a sequence of \0 terminated value strings in *valbuff
458 the respective len parameters specify the available space in the buffers
459 returns STAT_OK on success
460 returns STAT_WARNING if a buffer was too short and data truncated.
462 int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num,
463 char *typbuff, int typlen,
464 char *nambuff, int namlen,
465 char *envbuff, int envlen,
466 char *valbuff, int vallen) {
467 const char *cmsgtype, *cmsgname = NULL, *cmsgenvn = NULL, *cmsgfmt = NULL;
468 size_t msglen;
469 char scratch1[16]; /* can hold an IPv4 address in ASCII */
470 #if WITH_IP4 && defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
471 char scratch2[16];
472 char scratch3[16];
473 #endif
474 int rc = 0;
476 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
477 envbuff[0] = '\0';
478 switch (cmsg->cmsg_type) {
479 default:
480 *num = 1;
481 typbuff[0] = '\0'; strncat(typbuff, "IP", typlen-1);
482 snprintf(nambuff, namlen, "type_%u", cmsg->cmsg_type);
483 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
484 return STAT_OK;
485 #if WITH_IP4
486 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
487 case IP_PKTINFO: {
488 struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
489 *num = 3;
490 typbuff[0] = '\0'; strncat(typbuff, "IP_PKTINFO", typlen-1);
491 snprintf(nambuff, namlen, "%s%c%s%c%s", "if", '\0', "locaddr", '\0', "dstaddr");
492 snprintf(envbuff, envlen, "%s%c%s%c%s", "IP_IF", '\0',
493 "IP_LOCADDR", '\0', "IP_DSTADDR");
494 snprintf(valbuff, vallen, "%s%c%s%c%s",
495 xiogetifname(pktinfo->ipi_ifindex, scratch1, -1), '\0',
496 #if HAVE_PKTINFO_IPI_SPEC_DST
497 inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr),
498 scratch2, sizeof(scratch2)),
499 #else
501 #endif
502 '\0',
503 inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr),
504 scratch3, sizeof(scratch3)));
506 return STAT_OK;
507 #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
508 #endif /* WITH_IP4 */
509 #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR
510 case IP_RECVERR: {
511 struct sock_extended_err *err =
512 (struct sock_extended_err *)CMSG_DATA(cmsg);
513 *num = 6;
514 typbuff[0] = '\0'; strncat(typbuff, "IP_RECVERR", typlen-1);
515 snprintf(nambuff, namlen, "%s%c%s%c%s%c%s%c%s%c%s",
516 "errno", '\0', "origin", '\0', "type", '\0',
517 "code", '\0', "info", '\0', "data");
518 snprintf(envbuff, envlen, "%s%c%s%c%s%c%s%c%s%c%s",
519 "IP_RECVERR_ERRNO", '\0', "IP_RECVERR_ORIGIN", '\0',
520 "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0',
521 "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA");
522 snprintf(valbuff, vallen, "%u%c%u%c%u%c%u%c%u%c%u",
523 err->ee_errno, '\0', err->ee_origin, '\0', err->ee_type, '\0',
524 err->ee_code, '\0', err->ee_info, '\0', err->ee_data);
525 return STAT_OK;
527 #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */
528 #ifdef IP_RECVIF
529 case IP_RECVIF: {
530 /* spec in FreeBSD: /usr/include/net/if_dl.h */
531 struct sockaddr_dl *sadl = (struct sockaddr_dl *)CMSG_DATA(cmsg);
532 *num = 1;
533 typbuff[0] = '\0'; strncat(typbuff, "IP_RECVIF", typlen-1);
534 nambuff[0] = '\0'; strncat(nambuff, "if", namlen-1);
535 envbuff[0] = '\0'; strncat(envbuff, "IP_IF", envlen-1);
536 valbuff[0] = '\0';
537 strncat(valbuff,
538 xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1);
539 return STAT_OK;
541 #endif /* defined(IP_RECVIF) */
542 #if WITH_IP4
543 #ifdef IP_RECVDSTADDR
544 case IP_RECVDSTADDR:
545 *num = 1;
546 typbuff[0] = '\0'; strncat(typbuff, "IP_RECVDSTADDR", typlen-1);
547 nambuff[0] = '\0'; strncat(nambuff, "dstaddr", namlen-1);
548 envbuff[0] = '\0'; strncat(envbuff, "IP_DSTADDR", envlen-1);
549 inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen);
550 return STAT_OK;
551 #endif
552 #endif /* WITH_IP4 */
553 case IP_OPTIONS:
554 #ifdef IP_RECVOPTS
555 case IP_RECVOPTS:
556 #endif
557 cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgfmt = NULL; break;
558 case IP_TOS:
559 cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgfmt = "%u"; break;
560 case IP_TTL: /* Linux */
561 #ifdef IP_RECVTTL
562 case IP_RECVTTL: /* FreeBSD */
563 #endif
564 cmsgtype = "IP_TTL"; cmsgname = "ttl"; cmsgfmt = "%u"; break;
566 /* when we come here we provide a single parameter
567 with type in cmsgtype, name in cmsgname, printf format in cmsgfmt */
568 *num = 1;
569 if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING;
570 typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1);
571 if (strlen(cmsgname) >= namlen) rc = STAT_WARNING;
572 nambuff[0] = '\0'; strncat(nambuff, cmsgname, namlen-1);
573 if (cmsgenvn) {
574 if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING;
575 envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1);
576 } else {
577 envbuff[0] = '\0';
579 if (cmsgfmt != NULL) {
580 snprintf(valbuff, vallen, cmsgfmt, *(unsigned char *)CMSG_DATA(cmsg));
581 } else {
582 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
584 return rc;
586 #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */
588 #endif /* _WITH_IP4 || _WITH_IP6 */