configure option --enable-msglevel now takes textual level in upper and capitalized
[socat.git] / xio-ip.c
blob9ad642c2c9e025488a89454772ec3f5662a19b91
1 /* source: xio-ip.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 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"
17 #include "nestlex.h"
20 #if WITH_IP4 || WITH_IP6
22 #ifdef IP_OPTIONS
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 };
24 #endif
25 #ifdef IP_PKTINFO
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 };
27 #endif
28 #ifdef IP_RECVTOS
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 };
30 #endif
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 };
33 #endif
34 #ifdef IP_RECVOPTS
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 };
36 #endif
37 #ifdef IP_RETOPTS
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 };
39 #endif
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 };
42 #ifdef IP_HDRINCL
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 };
44 #endif
45 #ifdef IP_RECVERR
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 };
47 #endif
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 };
50 #endif
51 #ifdef IP_MTU
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 };
53 #endif
54 #ifdef IP_TRANSPARENT
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};
56 #endif
57 #ifdef IP_FREEBIND
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 };
59 #endif
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};
62 #endif
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};
68 #ifdef IP_PKTOPTIONS
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 };
70 #endif
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 };
73 #endif
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 };
76 #endif
77 #ifdef IP_RECVDSTADDR
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 };
79 #endif
80 #ifdef IP_RECVIF
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 };
82 #endif
84 #if WITH_RES_DEPRECATED
85 # define WITH_RES_AAONLY 1
86 # define WITH_RES_PRIMARY 1
87 #endif /* WITH_RES_DEPRECATED */
88 #if HAVE_RESOLV_H
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 };
90 #if WITH_RES_AAONLY
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 };
92 #endif
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 };
94 #if WITH_RES_PRIMARY
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 };
96 #endif
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 */
107 #if HAVE_RESOLV_H
108 int Res_init(void) {
109 int result;
110 Debug("res_init()");
111 result = res_init();
112 Debug1("res_init() -> %d", result);
113 return result;
115 #endif /* HAVE_RESOLV_H */
117 #if HAVE_RESOLV_H
118 unsigned long res_opts() {
119 return _res.options;
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)
126 [::2] (IPv6 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;
142 size_t nodelen;
143 unsigned long save_res_opts = 0;
144 #if HAVE_GETADDRINFO
145 struct addrinfo hints = {0};
146 struct addrinfo *res = NULL;
147 #else /* HAVE_PROTOTYPE_LIB_getipnodebyname || nothing */
148 struct hostent *host;
149 #endif
150 int error_num;
152 #if HAVE_RESOLV_H
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");
171 #ifdef WITH_VSOCK
172 if (family == AF_VSOCK) {
173 error_num = sockaddr_vm_parse(&sau->vm, node, service);
174 if (error_num < 0)
175 return STAT_NORETRY;
177 return STAT_OK;
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)) {
184 char *extra;
185 port = htons(strtoul(service, &extra, 0));
186 if (*extra != '\0') {
187 Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"",
188 service, extra);
190 service = NULL;
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 */
198 #if WITH_IP6
199 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') {
200 if ((numnode = Malloc(nodelen-1)) == NULL) {
201 #if HAVE_RESOLV_H
202 if (res_opts0 | res_opts1) {
203 _res.options = (_res.options & (~res_opts0&~res_opts1) |
204 save_res_opts& ( res_opts0| res_opts1));
206 #endif
207 return STAT_NORETRY;
209 strncpy(numnode, node+1, nodelen-2); /* ok */
210 numnode[nodelen-2] = '\0';
211 node = numnode;
212 #if HAVE_GETADDRINFO
213 hints.ai_flags |= AI_NUMERICHOST;
214 #endif /* HAVE_GETADDRINFO */
215 if (family == PF_UNSPEC) family = PF_INET6;
216 #endif /* WITH_IP6 */
219 #if HAVE_GETADDRINFO
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;
232 do {
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;
238 continue;
240 if (error_num == EAI_SERVICE && protocol != 0) {
241 hints.ai_protocol = 0;
242 continue;
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);
254 #if HAVE_RESOLV_H
255 if (res_opts0 | res_opts1) {
256 _res.options = (_res.options & (~res_opts0&~res_opts1) |
257 save_res_opts& ( res_opts0| res_opts1));
259 #endif
260 return STAT_RETRYLATER;
262 } while (1);
263 service = NULL; /* do not resolve later again */
265 record = res;
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) {
271 int trypf;
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) {
276 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 */
283 record = res;
284 family = res[0].ai_addr->sa_family;
288 switch (family) {
289 #if WITH_IP4
290 case PF_INET:
291 if (*socklen > record->ai_addrlen) {
292 *socklen = record->ai_addrlen;
294 memcpy(&sau->ip4, record->ai_addr, *socklen);
295 break;
296 #endif /* WITH_IP4 */
297 #if WITH_IP6
298 case PF_INET6:
299 #if _AIX
300 /* older AIX versions pass wrong length, so we correct it */
301 record->ai_addr->sa_len = sizeof(struct sockaddr_in6);
302 #endif
303 if (*socklen > record->ai_addrlen) {
304 *socklen = record->ai_addrlen;
306 memcpy(&sau->ip6, record->ai_addr, *socklen);
307 break;
308 #endif /* WITH_IP6 */
309 default:
310 Error1("address resolved to unknown protocol family %d",
311 record->ai_addr->sa_family);
312 break;
314 freeaddrinfo(res);
315 } else {
316 switch (family) {
317 #if WITH_IP4
318 case PF_INET: *socklen = sizeof(sau->ip4); break;
319 #endif /* WITH_IP4 */
320 #if WITH_IP6
321 case PF_INET6: *socklen = sizeof(sau->ip6); break;
322 #endif /* WITH_IP6 */
326 #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */
328 if (node != NULL) {
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;
333 #elif WITH_IP6
334 family = PF_INET6;
335 #else
336 family = PF_INET;
337 #endif
339 host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num);
340 if (host == NULL) {
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";
346 switch (error_num) {
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);
353 } else {
354 switch (family) {
355 #if WITH_IP4
356 case PF_INET:
357 *socklen = sizeof(sau->ip4);
358 sau->soa.sa_family = PF_INET;
359 memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4);
360 break;
361 #endif
362 #if WITH_IP6
363 case PF_INET6:
364 *socklen = sizeof(sau->ip6);
365 sau->soa.sa_family = PF_INET6;
366 memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16);
367 break;
368 #endif
371 freehostent(host);
374 #else /* !HAVE_PROTOTYPE_LIB_getipnodebyname */
376 if (node != NULL) {
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;
384 #elif WITH_IP6
385 family = PF_INET6;
386 #else
387 family = PF_INET;
388 #endif
390 /*!!! try gethostbyname2 for IP6 */
391 if ((host = Gethostbyname(node)) == NULL) {
392 Error2("gethostbyname(\"%s\"): %s", node,
393 h_errno == NETDB_INTERNAL ? strerror(errno) :
394 hstrerror(h_errno));
395 #if HAVE_RESOLV_H
396 if (res_opts0 | res_opts1) {
397 _res.options = (_res.options & (~res_opts0&~res_opts1) |
398 save_res_opts& ( res_opts0| res_opts1));
400 #endif
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");
406 } else {
407 switch (family) {
408 #if WITH_IP4
409 case PF_INET:
410 *socklen = sizeof(sau->ip4);
411 sau->soa.sa_family = PF_INET;
412 memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4);
413 break;
414 #endif /* WITH_IP4 */
415 #if WITH_IP6
416 case PF_INET6:
417 *socklen = sizeof(sau->ip6);
418 sau->soa.sa_family = PF_INET6;
419 memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16);
420 break;
421 #endif /* WITH_IP6 */
426 #endif
428 #if WITH_TCP || WITH_UDP
429 if (service) {
430 port = parseport(service, protocol);
432 if (port >= 0) {
433 switch (family) {
434 #if WITH_IP4
435 case PF_INET: sau->ip4.sin_port = port; break;
436 #endif /* WITH_IP4 */
437 #if WITH_IP6
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);
446 #if HAVE_RESOLV_H
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 */
452 return STAT_OK;
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) {
473 int cmsgctr = 0;
474 const char *cmsgtype, *cmsgname = NULL, *cmsgenvn = NULL;
475 size_t msglen;
476 char scratch1[16]; /* can hold an IPv4 address in ASCII */
477 #if WITH_IP4 && defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
478 char scratch2[16];
479 char scratch3[16];
480 #endif
481 int rc = 0;
483 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
484 envbuff[0] = '\0';
485 switch (cmsg->cmsg_type) {
486 default:
487 *num = 1;
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);
491 return STAT_OK;
492 #if WITH_IP4
493 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
494 case IP_PKTINFO: {
495 struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
496 *num = 3;
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)),
506 #else
508 #endif
509 '\0',
510 inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr),
511 scratch3, sizeof(scratch3)));
513 return STAT_OK;
514 #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
515 #endif /* WITH_IP4 */
516 #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR
517 case IP_RECVERR: {
518 struct sock_extended_err *err =
519 (struct sock_extended_err *)CMSG_DATA(cmsg);
520 *num = 6;
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);
532 return STAT_OK;
534 #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */
535 #ifdef IP_RECVIF
536 case IP_RECVIF: {
537 /* spec in FreeBSD: /usr/include/net/if_dl.h */
538 struct sockaddr_dl *sadl = (struct sockaddr_dl *)CMSG_DATA(cmsg);
539 *num = 1;
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);
543 valbuff[0] = '\0';
544 strncat(valbuff,
545 xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1);
546 return STAT_OK;
548 #endif /* defined(IP_RECVIF) */
549 #if WITH_IP4
550 #ifdef IP_RECVDSTADDR
551 case IP_RECVDSTADDR:
552 *num = 1;
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);
557 return STAT_OK;
558 #endif
559 #endif /* WITH_IP4 */
560 case IP_OPTIONS:
561 #ifdef IP_RECVOPTS
562 case IP_RECVOPTS:
563 #endif
564 cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1; break;
565 case IP_TOS:
566 cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen; break;
567 case IP_TTL: /* Linux */
568 #ifdef IP_RECVTTL
569 case IP_RECVTTL: /* FreeBSD */
570 #endif
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 */
575 *num = 1;
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);
580 if (cmsgenvn) {
581 if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING;
582 envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1);
583 } else {
584 envbuff[0] = '\0';
586 switch (cmsgctr) {
587 case sizeof(char):
588 snprintf(valbuff, vallen, "%u", *(unsigned char *)CMSG_DATA(cmsg)); break;
589 case sizeof(int):
590 snprintf(valbuff, vallen, "%u", (*(unsigned int *)CMSG_DATA(cmsg))); break;
591 case 0:
592 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); break;
593 default: break;
595 return rc;
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;
607 char *tokp = token;
608 int parsres;
610 /* parse first IP address, expect ':' */
611 parsres =
612 nestlex((const char **)&tokp, &buffp, &bufspc,
613 ends, NULL, NULL, nests,
614 true, false, false);
615 if (parsres < 0) {
616 Error1("option too long: \"%s\"", token);
617 return -1;
618 } else if (parsres > 0) {
619 Error1("syntax error in \"%s\"", token);
620 return -1;
622 if (*tokp != ':') {
623 Error1("syntax in option %s: missing ':'", token);
625 *buffp++ = '\0';
626 opt->value.u_ip_mreq_source.mcaddr = strdup(buff); /*!!! NULL */
628 ++tokp;
629 /* parse second IP address, expect ':' or '\0'' */
630 buffp = buff;
631 /*! result= */
632 parsres =
633 nestlex((const char **)&tokp, &buffp, &bufspc,
634 ends, NULL, NULL, nests,
635 true, false, false);
636 if (parsres < 0) {
637 Error1("option too long: \"%s\"", token);
638 return -1;
639 } else if (parsres > 0) {
640 Error1("syntax error in \"%s\"", token);
641 return -1;
643 if (*tokp != ':') {
644 Error1("syntax in option %s: missing ':'", token);
646 *buffp++ = '\0';
647 opt->value.u_ip_mreq_source.ifaddr = strdup(buff); /*!!! NULL */
649 ++tokp;
650 /* parse third IP address, expect ':' or '\0'' */
651 buffp = buff;
652 /*! result= */
653 parsres =
654 nestlex((const char **)&tokp, &buffp, &bufspc,
655 ends, NULL, NULL, nests,
656 true, false, false);
657 if (parsres < 0) {
658 Error1("option too long: \"%s\"", token);
659 return -1;
660 } else if (parsres > 0) {
661 Error1("syntax error in \"%s\"", token);
662 return -1;
664 if (*tokp) {
665 Error1("syntax in option %s: trailing cruft", token);
667 *buffp++ = '\0';
668 opt->value.u_ip_mreq_source.srcaddr = strdup(buff); /*!!! NULL */
670 Info4("setting option \"%s\" to {0x%08x,0x%08x,0x08x}",
671 ent->desc->defname,
672 opt->value.u_ip_mreq_source.mcaddr,
673 opt->value.u_ip_mreq_source.ifaddr,
674 opt->value.u_ip_mreq_source.srcaddr);
675 return 0;
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 */
689 /*! result */
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 ip4_mreq_src.imr_multiaddr,
712 ip4_mreq_src.imr_interface,
713 ip4_mreq_src.imr_sourceaddr,
714 sizeof(struct ip_mreq_source),
715 strerror(errno));
716 opt->desc = ODESC_ERROR;
717 return -1;
719 return 0;
721 #endif /* HAVE_STRUCT_IP_MREQ_SOURCE */
723 #endif /* _WITH_IP4 || _WITH_IP6 */