Version 1.8.0.0
[socat.git] / xio-ip.c
blobc7c776a8b25361c9dffbea713a706afc76953c16
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_SPEC, SOL_IP, IP_ADD_MEMBERSHIP };
73 #endif
74 #if defined(HAVE_STRUCT_IP_MREQ_SOURCE) && defined(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 #ifdef AI_ADDRCONFIG
85 const struct optdesc opt_ai_addrconfig = { "ai-addrconfig", "addrconfig", OPT_AI_ADDRCONFIG, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_ADDRCONFIG };
86 #endif
87 #ifdef AI_V4MAPPED
88 const struct optdesc opt_ai_v4mapped = { "ai-v4mapped", "v4mapped", OPT_AI_V4MAPPED, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_V4MAPPED };
89 #endif
90 #ifdef AI_PASSIVE
91 const struct optdesc opt_ai_passive = { "ai-passive", "passive", OPT_AI_PASSIVE, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_PASSIVE };
92 #endif
94 #if WITH_RESOLVE
95 #if WITH_RES_DEPRECATED
96 # define WITH_RES_AAONLY 1
97 # define WITH_RES_PRIMARY 1
98 #endif /* WITH_RES_DEPRECATED */
99 #if HAVE_RESOLV_H
100 const struct optdesc opt_res_debug = { "res-debug", NULL, OPT_RES_DEBUG, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_DEBUG };
101 #if WITH_RES_AAONLY
102 const struct optdesc opt_res_aaonly = { "res-aaonly", "aaonly", OPT_RES_AAONLY, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_AAONLY };
103 #endif
104 const struct optdesc opt_res_usevc = { "res-usevc", "usevc", OPT_RES_USEVC, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_USEVC };
105 #if WITH_RES_PRIMARY
106 const struct optdesc opt_res_primary = { "res-primary", "primary", OPT_RES_PRIMARY, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_PRIMARY };
107 #endif
108 const struct optdesc opt_res_igntc = { "res-igntc", "igntc", OPT_RES_IGNTC, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_IGNTC };
109 const struct optdesc opt_res_recurse = { "res-recurse", "recurse", OPT_RES_RECURSE, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_RECURSE };
110 const struct optdesc opt_res_defnames = { "res-defnames", "defnames", OPT_RES_DEFNAMES, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_DEFNAMES };
111 const struct optdesc opt_res_stayopen = { "res-stayopen", "stayopen", OPT_RES_STAYOPEN, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_STAYOPEN };
112 const struct optdesc opt_res_dnsrch = { "res-dnsrch", "dnsrch", OPT_RES_DNSRCH, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_DNSRCH };
113 #if HAVE_RES_RETRANS
114 const struct optdesc opt_res_retrans = { "res-retrans", "retrans", OPT_RES_RETRANS, GROUP_SOCK_IP, PH_OFFSET, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.retrans), XIO_SIZEOF(para.socket.ip.res.retrans), RES_MAXRETRANS };
115 #endif
116 #if HAVE_RES_RETRY
117 const struct optdesc opt_res_retry = { "res-retry", NULL, OPT_RES_RETRY, GROUP_SOCK_IP, PH_OFFSET, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.retry), XIO_SIZEOF(para.socket.ip.res.retry), RES_MAXRETRY };
118 #endif
119 #if HAVE_RES_NSADDR_LIST
120 const struct optdesc opt_res_nsaddr = { "res-nsaddr", "dns", OPT_RES_NSADDR, GROUP_SOCK_IP, PH_OFFSET, TYPE_IP4SOCK, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.nsaddr), XIO_SIZEOF(para.socket.ip.res.retry), RES_MAXRETRY };
121 #endif
122 #endif /* HAVE_RESOLV_H */
123 #endif /* WITH_RESOLVE */
124 #endif /* WITH_IP4 || WITH_IP6 */
127 int xioinit_ip(
128 int *pf,
129 char ipv)
131 if (*pf == PF_UNSPEC) {
132 switch (ipv) {
133 case '0': *pf = PF_UNSPEC; break;
134 #if WITH_IP4
135 case '4': *pf = PF_INET; break;
136 #endif
137 #if WITH_IP6
138 case '6': *pf = PF_INET6; break;
139 #endif
142 return 0;
146 #if HAVE_RESOLV_H
148 int Res_init(void) {
149 int result;
150 Debug("res_init()");
151 result = res_init();
152 Debug1("res_init() -> %d", result);
153 return result;
156 #endif /* HAVE_RESOLV_H */
159 /* the ultimate(?) socat resolver function
160 node: the address to be resolved; supported forms:
161 1.2.3.4 (IPv4 address)
162 [::2] (IPv6 address)
163 hostname (hostname resolving to IPv4 or IPv6 address)
164 hostname.domain (fq hostname resolving to IPv4 or IPv6 address)
165 service: the port specification; may be numeric or symbolic
166 family: PF_INET, PF_INET6, or PF_UNSPEC permitting both
167 socktype: SOCK_STREAM, SOCK_DGRAM, ...
168 protocol: IPPROTO_UDP, IPPROTO_TCP
169 sau: an uninitialized storage for the resulting socket address
170 returns: STAT_OK, STAT_RETRYLATER, STAT_NORETRY, prints message
172 int xiogetaddrinfo(const char *node, const char *service,
173 int family, int socktype, int protocol,
174 struct addrinfo **res, const int ai_flags[2]) {
175 char *numnode = NULL;
176 size_t nodelen;
177 #if HAVE_GETADDRINFO
178 struct addrinfo hints = {0};
179 #else /* HAVE_PROTOTYPE_LIB_getipnodebyname || nothing */
180 struct hostent *host;
181 #endif
182 int error_num;
184 if (service && service[0]=='\0') {
185 Error("empty port/service");
188 #if LATER
189 #ifdef WITH_VSOCK
190 if (family == AF_VSOCK) {
191 error_num = sockaddr_vm_parse(&sau->vm, node, service);
192 if (error_num < 0)
193 return STAT_NORETRY;
195 return STAT_OK;
197 #endif /* WITH_VSOCK */
198 #endif /* LATER */
200 /* the resolver functions might handle numeric forms of node names by
201 reverse lookup, that's not what we want.
202 So we detect these and handle them specially */
203 if (0) { /* for canonical reasons */
205 #if WITH_IP6
206 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') {
207 if ((numnode = Malloc(nodelen-1)) == NULL)
208 return STAT_NORETRY;
210 strncpy(numnode, node+1, nodelen-2); /* ok */
211 numnode[nodelen-2] = '\0';
212 node = numnode;
213 #if HAVE_GETADDRINFO
214 hints.ai_flags |= AI_NUMERICHOST;
215 #endif /* HAVE_GETADDRINFO */
216 if (family == PF_UNSPEC) family = PF_INET6;
217 #endif /* WITH_IP6 */
219 if (family == 0)
220 hints.ai_flags |= AI_ADDRCONFIG;
221 #if HAVE_GETADDRINFO
222 if (node != NULL || service != NULL) {
223 struct addrinfo *record;
225 if (ai_flags != NULL) {
226 hints.ai_flags |= ai_flags[0];
227 hints.ai_flags &= ~ai_flags[1];
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 do {
238 error_num = Getaddrinfo(node, service, &hints, res);
239 if (error_num == 0) break;
240 if (error_num == EAI_SOCKTYPE && socktype != 0) {
241 /* there are systems where kernel goes SCTP but not getaddrinfo() */
242 hints.ai_socktype = 0;
243 continue;
245 if (error_num == EAI_SERVICE && protocol != 0) {
246 if (hints.ai_protocol == 0) {
247 Error7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s",
248 node?node:"NULL", service?service:"NULL",
249 hints.ai_flags, hints.ai_family,
250 hints.ai_socktype, hints.ai_protocol,
251 gai_strerror(error_num));
252 if (*res != NULL)
253 freeaddrinfo(*res);
254 if (numnode)
255 free(numnode);
256 return STAT_NORETRY;
258 /* Probably unsupported protocol (e.g. UDP-Lite), fallback to 0 */
259 hints.ai_protocol = 0;
260 continue;
262 if ((error_num = Getaddrinfo(node, service, &hints, res)) != 0) {
263 Error7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s",
264 node?node:"NULL", service?service:"NULL",
265 hints.ai_flags, hints.ai_family,
266 hints.ai_socktype, hints.ai_protocol,
267 (error_num == EAI_SYSTEM)?
268 strerror(errno):gai_strerror(error_num));
269 if (*res != NULL)
270 freeaddrinfo(*res);
271 if (numnode)
272 free(numnode);
274 return STAT_RETRYLATER;
276 } while (1);
277 service = NULL; /* do not resolve later again */
279 #if WITH_MSGLEVEL <= E_DEBUG
280 record = *res;
281 while (record) {
282 char buff[256/*!*/];
283 sockaddr_info(record->ai_addr, record->ai_addrlen, buff, sizeof(buff));
284 Debug5("getaddrinfo() -> flags=0x%02x family=%d socktype=%d protocol=%d addr=%s", record->ai_flags, record->ai_family, record->ai_socktype, record->ai_protocol, buff);
285 record = record->ai_next;
287 #endif /* WITH_MSGLEVEL <= E_DEBUG */
290 #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */
292 if (node != NULL) {
293 /* first fallback is getipnodebyname() */
294 if (family == PF_UNSPEC) {
295 #if WITH_IP4 && WITH_IP6
296 switch (xioparms.default_ip) {
297 case '4': pf = PF_INET; break;
298 case '6': pf = PF_INET6; break;
299 default: break; /* includes \0 */
301 #elif WITH_IP6
302 family = PF_INET6;
303 #else
304 family = PF_INET;
305 #endif
307 host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num);
308 if (host == NULL) {
309 const static char ai_host_not_found[] = "Host not found";
310 const static char ai_no_address[] = "No address";
311 const static char ai_no_recovery[] = "No recovery";
312 const static char ai_try_again[] = "Try again";
313 const char *error_msg = "Unknown error";
314 switch (error_num) {
315 case HOST_NOT_FOUND: error_msg = ai_host_not_found; break;
316 case NO_ADDRESS: error_msg = ai_no_address;
317 case NO_RECOVERY: error_msg = ai_no_recovery;
318 case TRY_AGAIN: error_msg = ai_try_again;
320 Error2("getipnodebyname(\"%s\", ...): %s", node, error_msg);
321 } else {
322 switch (family) {
323 #if WITH_IP4
324 case PF_INET:
325 *socklen = sizeof(sau->ip4);
326 sau->soa.sa_family = PF_INET;
327 memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4);
328 break;
329 #endif
330 #if WITH_IP6
331 case PF_INET6:
332 *socklen = sizeof(sau->ip6);
333 sau->soa.sa_family = PF_INET6;
334 memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16);
335 break;
336 #endif
339 freehostent(host);
342 #elsif 0 /* !HAVE_PROTOTYPE_LIB_getipnodebyname */
344 if (node != NULL) {
345 /* this is not a typical IP6 resolver function - but Linux
346 "man gethostbyname" says that the only supported address type with
347 this function is AF_INET _at present_, so maybe this fallback will
348 be useful somewhere sometimes in a future even for IP6 */
349 if (family == PF_UNSPEC) {
350 #if WITH_IP4 && WITH_IP6
351 switch (xioparms.default_ip) {
352 case '4': pf = PF_INET; break;
353 case '6': pf = PF_INET6; break;
354 default: break; /* includes \0 */
356 #elif WITH_IP6
357 family = PF_INET6;
358 #else
359 family = PF_INET;
360 #endif
362 /*!!! try gethostbyname2 for IP6 */
363 if ((host = Gethostbyname(node)) == NULL) {
364 Error2("gethostbyname(\"%s\"): %s", node,
365 h_errno == NETDB_INTERNAL ? strerror(errno) :
366 hstrerror(h_errno));
367 return STAT_RETRYLATER;
369 if (host->h_addrtype != family) {
370 Error2("xiogetaddrinfo(): \"%s\" does not resolve to %s",
371 node, family==PF_INET?"IP4":"IP6");
372 } else {
373 switch (family) {
374 #if WITH_IP4
375 case PF_INET:
376 *socklen = sizeof(sau->ip4);
377 sau->soa.sa_family = PF_INET;
378 memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4);
379 break;
380 #endif /* WITH_IP4 */
381 #if WITH_IP6
382 case PF_INET6:
383 *socklen = sizeof(sau->ip6);
384 sau->soa.sa_family = PF_INET6;
385 memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16);
386 break;
387 #endif /* WITH_IP6 */
392 #endif
394 if (numnode) free(numnode);
396 return STAT_OK;
399 void xiofreeaddrinfo(struct addrinfo *res) {
400 #if HAVE_GETADDRINFO
401 freeaddrinfo(res);
402 #else
404 #endif
407 /* A simple resolver interface that just returns one address,
408 the first found by calling xiogetaddrinfo().
409 family may be AF_INET, AF_INET6, or AF_UNSPEC;
410 Returns -1 when an error occurred or when no result found.
412 int xioresolve(const char *node, const char *service,
413 int family, int socktype, int protocol,
414 union sockaddr_union *addr, socklen_t *addrlen,
415 const int ai_flags[2])
417 struct addrinfo *res = NULL;
418 struct addrinfo *aip;
419 int rc;
421 rc = xiogetaddrinfo(node, service, family, socktype, protocol,
422 &res, ai_flags);
423 if (rc != 0) {
424 xiofreeaddrinfo(res);
425 return -1;
427 if (res == NULL) {
428 Warn1("xioresolve(node=\"%s\", ...): No result", node);
429 xiofreeaddrinfo(res);
430 return -1;
432 if (res->ai_addrlen > *addrlen) {
433 Warn3("xioresolve(node=\"%s\", addrlen="F_socklen", ...): "F_socklen" bytes required", node, *addrlen, res->ai_addrlen);
434 xiofreeaddrinfo(res);
435 return -1;
437 if (res->ai_next != NULL) {
438 Info4("xioresolve(node=\"%s\", service=%s%s%s, ...): More than one address found", node?node:"NULL", service?"\"":"", service?service:"NULL", service?"\"":"");
441 aip = res;
442 if (ai_flags != NULL && ai_flags[0] & AI_PASSIVE && family == PF_UNSPEC) {
443 /* We select the first IPv6 address, if available,
444 because this might accept IPv4 connections too */
445 while (aip != NULL) {
446 if (aip->ai_family == PF_INET6)
447 break;
448 aip = aip->ai_next;
450 if (aip == NULL)
451 aip = res;
454 memcpy(addr, aip->ai_addr, aip->ai_addrlen);
455 *addrlen = aip->ai_addrlen;
456 xiofreeaddrinfo(res);
457 return 0;
460 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
461 /* Converts the ancillary message in *cmsg into a form useable for further
462 processing. knows the specifics of common message types.
463 These are valid for IPv4 and IPv6
464 Returns the number of resulting syntax elements in *num
465 Returns a sequence of \0 terminated type strings in *typbuff
466 Returns a sequence of \0 terminated name strings in *nambuff
467 Returns a sequence of \0 terminated value strings in *valbuff
468 The respective len parameters specify the available space in the buffers
469 Returns STAT_OK on success
470 Returns STAT_WARNING if a buffer was too short and data truncated.
472 int xiolog_ancillary_ip(
473 struct single *sfd,
474 struct cmsghdr *cmsg,
475 int *num,
476 char *typbuff, int typlen,
477 char *nambuff, int namlen,
478 char *envbuff, int envlen,
479 char *valbuff, int vallen)
481 int cmsgctr = 0;
482 const char *cmsgtype, *cmsgname = NULL, *cmsgenvn = NULL;
483 size_t msglen;
484 char scratch1[16]; /* can hold an IPv4 address in ASCII */
485 #if WITH_IP4 && defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
486 char scratch2[16];
487 char scratch3[16];
488 #endif
489 int rc = 0;
491 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
492 envbuff[0] = '\0';
493 switch (cmsg->cmsg_type) {
494 default:
495 *num = 1;
496 typbuff[0] = '\0'; strncat(typbuff, "IP", typlen-1);
497 snprintf(nambuff, namlen, "type_%u", cmsg->cmsg_type);
498 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
499 return STAT_OK;
500 #if WITH_IP4
501 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO
502 case IP_PKTINFO: {
503 struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
504 *num = 3;
505 typbuff[0] = '\0'; strncat(typbuff, "IP_PKTINFO", typlen-1);
506 snprintf(nambuff, namlen, "%s%c%s%c%s", "if", '\0', "locaddr", '\0', "dstaddr");
507 snprintf(envbuff, envlen, "%s%c%s%c%s", "IP_IF", '\0',
508 "IP_LOCADDR", '\0', "IP_DSTADDR");
509 snprintf(valbuff, vallen, "%s%c%s%c%s",
510 xiogetifname(pktinfo->ipi_ifindex, scratch1, -1), '\0',
511 #if HAVE_PKTINFO_IPI_SPEC_DST
512 inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr),
513 scratch2, sizeof(scratch2)),
514 #else
516 #endif
517 '\0',
518 inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr),
519 scratch3, sizeof(scratch3)));
520 Notice3("Ancillary message: interface \"%s\", locaddr=%s, dstaddr=%s",
521 xiogetifname(pktinfo->ipi_ifindex, scratch1, -1),
522 #if HAVE_PKTINFO_IPI_SPEC_DST
523 inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr),
524 scratch2, sizeof(scratch2)),
525 #else
527 #endif
528 inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr),
529 scratch3, sizeof(scratch3)));
531 return STAT_OK;
532 #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */
533 #endif /* WITH_IP4 */
534 #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR
535 case IP_RECVERR: {
536 struct xio_extended_err {
537 struct sock_extended_err see;
538 __u32 data0;
539 __u32 data1;
540 __u32 data2;
541 __u32 data3;
543 struct xio_extended_err *err =
544 (struct xio_extended_err *)CMSG_DATA(cmsg);
545 *num = 6;
546 typbuff[0] = '\0'; strncat(typbuff, "IP_RECVERR", typlen-1);
547 snprintf(nambuff, namlen, "%s%c%s%c%s%c%s%c%s%c%s",
548 "errno", '\0', "origin", '\0', "type", '\0',
549 "code", '\0', "info", '\0', "data");
550 snprintf(envbuff, envlen, "%s%c%s%c%s%c%s%c%s%c%s",
551 "IP_RECVERR_ERRNO", '\0', "IP_RECVERR_ORIGIN", '\0',
552 "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0',
553 "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA");
554 snprintf(valbuff, vallen, "%u%c%u%c%u%c%u%c%u%c%u",
555 err->see.ee_errno, '\0', err->see.ee_origin, '\0', err->see.ee_type, '\0',
556 err->see.ee_code, '\0', err->see.ee_info, '\0', err->see.ee_data);
557 /* semantic part */
558 switch (err->see.ee_origin) {
559 char addrbuff[40];
560 #if WITH_IP4
561 case SO_EE_ORIGIN_ICMP:
562 if (1) {
563 inet4addr_info(ntohl(err->data1), addrbuff, sizeof(addrbuff));
564 Notice6("received ICMP from %s, type %d, code %d, info %d, data %d, resulting in errno %d",
565 addrbuff, err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno);
567 break;
568 #endif /* WITH_IP4 */
569 #if WITH_IP6
570 case SO_EE_ORIGIN_ICMP6:
571 if (1) {
572 Notice5("received ICMP type %d, code %d, info %d, data %d, resulting in errno %d",
573 err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno);
575 break;
576 #endif /* WITH_IP6 */
577 default:
578 Notice6("received error message origin %d, type %d, code %d, info %d, data %d, generating errno %d",
579 err->see.ee_origin, err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno);
580 break;
582 return STAT_OK;
584 #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */
585 #ifdef IP_RECVIF
586 case IP_RECVIF: {
587 /* spec in FreeBSD: /usr/include/net/if_dl.h */
588 struct sockaddr_dl *sadl = (struct sockaddr_dl *)CMSG_DATA(cmsg);
589 *num = 1;
590 typbuff[0] = '\0'; strncat(typbuff, "IP_RECVIF", typlen-1);
591 nambuff[0] = '\0'; strncat(nambuff, "if", namlen-1);
592 envbuff[0] = '\0'; strncat(envbuff, "IP_IF", envlen-1);
593 valbuff[0] = '\0';
594 strncat(valbuff,
595 xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1);
596 Notice1("IP_RECVIF: %s", valbuff);
597 return STAT_OK;
599 #endif /* defined(IP_RECVIF) */
600 #if WITH_IP4
601 #ifdef IP_RECVDSTADDR
602 case IP_RECVDSTADDR:
603 *num = 1;
604 typbuff[0] = '\0'; strncat(typbuff, "IP_RECVDSTADDR", typlen-1);
605 nambuff[0] = '\0'; strncat(nambuff, "dstaddr", namlen-1);
606 envbuff[0] = '\0'; strncat(envbuff, "IP_DSTADDR", envlen-1);
607 inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen);
608 Notice1("IP_RECVDSTADDR: %s", valbuff);
609 return STAT_OK;
610 #endif
611 #endif /* WITH_IP4 */
612 case IP_OPTIONS:
613 #ifdef IP_RECVOPTS
614 case IP_RECVOPTS:
615 #endif
616 cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1;
617 /*!!!*/
618 break;
619 #if XIO_ANCILLARY_TYPE_SOLARIS
620 case IP_RECVTOS:
621 #else
622 case IP_TOS:
623 #endif
624 cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen;
625 break;
626 case IP_TTL: /* Linux */
627 #ifdef IP_RECVTTL
628 case IP_RECVTTL: /* FreeBSD */
629 #endif
630 cmsgtype = "IP_TTL"; cmsgname = "ttl"; cmsgctr = msglen; break;
632 /* when we come here we provide a single parameter
633 with name in cmsgname, value length in msglen */
634 *num = 1;
635 if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING;
636 typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1);
637 if (strlen(cmsgname) >= namlen) rc = STAT_WARNING;
638 nambuff[0] = '\0'; strncat(nambuff, cmsgname, namlen-1);
639 if (cmsgenvn) {
640 if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING;
641 envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1);
642 } else {
643 envbuff[0] = '\0';
645 switch (cmsgctr) {
646 case sizeof(char):
647 snprintf(valbuff, vallen, "%u", *(unsigned char *)CMSG_DATA(cmsg));
648 Notice2("Ancillary message: %s=%u", cmsgname, *(unsigned char *)CMSG_DATA(cmsg));
649 break;
650 case sizeof(int):
651 snprintf(valbuff, vallen, "%u", (*(unsigned int *)CMSG_DATA(cmsg)));
652 Notice2("Ancillary message: %s=%u", cmsgname, *(unsigned int *)CMSG_DATA(cmsg));
653 break;
654 case 0:
655 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); break;
656 default: break;
658 return rc;
660 #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */
663 #if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN)
664 int xiotype_ip_add_membership(
665 char *tokp,
666 const struct optname *ent,
667 struct opt *opt)
669 /* we do not resolve the addresses here because we do not yet know
670 if we are coping with a IPv4 or IPv6 socat address */
671 const char *ends[] = { ":", NULL };
672 const char *nests[] = { "[","]", NULL };
673 char buff[512], *buffp=buff; size_t bufspc = sizeof(buff)-1;
674 int parsres;
676 /* parse first IP address, expect ':' */
677 /*! result= */
678 parsres =
679 nestlex((const char **)&tokp, &buffp, &bufspc,
680 ends, NULL, NULL, nests,
681 true, false, false);
682 if (parsres < 0) {
683 Error1("option too long: \"%s\"", tokp);
684 return -1;
685 } else if (parsres > 0) {
686 Error1("syntax error in \"%s\"", tokp);
687 return -1;
689 if (*tokp != ':') {
690 Error1("syntax in option %s: missing ':'", tokp);
692 *buffp++ = '\0';
693 if ((opt->value.u_string/*multiaddr*/ = strdup(buff)) == NULL) {
694 Error1("strdup(\"%s\"): out of memory", buff);
695 return -1;
698 ++tokp;
699 /* parse second IP address, expect ':' or '\0'' */
700 buffp = buff;
701 /*! result= */
702 parsres =
703 nestlex((const char **)&tokp, &buffp, &bufspc,
704 ends, NULL, NULL, nests,
705 true, false, false);
706 if (parsres < 0) {
707 Error1("option too long: \"%s\"", tokp);
708 return -1;
709 } else if (parsres > 0) {
710 Error1("syntax error in \"%s\"", tokp);
711 return -1;
713 *buffp++ = '\0';
714 if ((opt->value2.u_string/*param2*/ = strdup(buff)) == NULL) {
715 Error1("strdup(\"%s\"): out of memory", buff);
716 free(opt->value.u_string);
717 return -1;
721 #if HAVE_STRUCT_IP_MREQN
722 if (*tokp++ == ':') {
723 strncpy(opt->value3.u_string/*ifindex*/, tokp, IF_NAMESIZE); /* ok */
724 Info4("setting option \"%s\" to {\"%s\",\"%s\",\"%s\"}",
725 ent->desc->defname,
726 opt->value.u_string/*multiaddr*/,
727 opt->value2.u_string/*param2*/,
728 opt->value3.u_string/*ifindex*/);
729 } else {
730 /*0 opt->value3.u_string = NULL; / * is NULL from init */
731 Info3("setting option \"%s\" to {\"%s\",\"%s\"}",
732 ent->desc->defname,
733 opt->value.u_string/*multiaddr*/,
734 opt->value2.u_string/*param2*/);
736 #else /* !HAVE_STRUCT_IP_MREQN */
737 Info3("setting option \"%s\" to {\"%s\",\"%s\"}",
738 ent->desc->defname,
739 opt->value.u_string/*multiaddr*/,
740 opt->value2.u_string/*param2*/);
741 #endif /* !HAVE_STRUCT_IP_MREQN */
742 return 0;
744 #endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */
746 #if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN)
747 int xioapply_ip_add_membership(
748 struct single *sfd,
749 struct opt *opt)
751 union {
752 #if HAVE_STRUCT_IP_MREQN
753 struct ip_mreqn mreqn;
754 #endif
755 struct ip_mreq mreq;
756 } ip4_mreqn = {{{0}}};
757 /* IPv6 not supported - seems to have different handling */
759 mc:addr:ifname|ifind
760 mc:ifname|ifind
761 mc:addr
763 union sockaddr_union sockaddr1;
764 socklen_t socklen1 = sizeof(sockaddr1.ip4);
765 union sockaddr_union sockaddr2;
766 socklen_t socklen2 = sizeof(sockaddr2.ip4);
768 /* First parameter is always multicast address */
769 /*! result */
770 xioresolve(opt->value.u_string/*multiaddr*/, NULL,
771 sfd->para.socket.la.soa.sa_family,
772 SOCK_DGRAM, IPPROTO_IP, &sockaddr1, &socklen1,
773 sfd->para.socket.ip.ai_flags);
774 ip4_mreqn.mreq.imr_multiaddr = sockaddr1.ip4.sin_addr;
775 if (0) {
776 ; /* for canonical reasons */
777 #if HAVE_STRUCT_IP_MREQN
778 } else if (opt->value3.u_string/*ifindex*/ != NULL) {
779 /* three parameters */
780 /* second parameter is interface address */
781 xioresolve(opt->value2.u_string/*param2*/, NULL,
782 sfd->para.socket.la.soa.sa_family,
783 SOCK_DGRAM, IPPROTO_IP, &sockaddr2, &socklen2,
784 sfd->para.socket.ip.ai_flags);
785 ip4_mreqn.mreq.imr_interface = sockaddr2.ip4.sin_addr;
786 /* third parameter is interface */
787 if (ifindex(opt->value3.u_string/*ifindex*/,
788 (unsigned int *)&ip4_mreqn.mreqn.imr_ifindex, -1)
789 < 0) {
790 Error1("cannot resolve interface \"%s\"",
791 opt->value3.u_string/*ifindex*/);
793 #endif /* HAVE_STRUCT_IP_MREQN */
794 } else {
795 /* two parameters */
796 if (0) {
797 ; /* for canonical reasons */
798 #if HAVE_STRUCT_IP_MREQN
799 /* there is a form with two parameters that uses mreqn */
800 } else if (ifindex(opt->value2.u_string/*param2*/,
801 (unsigned int *)&ip4_mreqn.mreqn.imr_ifindex,
803 >= 0) {
804 /* yes, second param converts to interface */
805 ip4_mreqn.mreq.imr_interface.s_addr = htonl(0);
806 #endif /* HAVE_STRUCT_IP_MREQN */
807 } else {
808 /*! result */
809 xioresolve(opt->value2.u_string/*param2*/, NULL,
810 sfd->para.socket.la.soa.sa_family,
811 SOCK_DGRAM, IPPROTO_IP,
812 &sockaddr2, &socklen2,
813 sfd->para.socket.ip.ai_flags);
814 ip4_mreqn.mreq.imr_interface = sockaddr2.ip4.sin_addr;
818 #if LATER
819 if (0) {
820 ; /* for canonical reasons */
821 } else if (sfd->para.socket.la.soa.sa_family == PF_INET) {
822 } else if (sfd->para.socket.la.soa.sa_family == PF_INET6) {
823 ip6_mreqn.mreq.imr_multiaddr = sockaddr1.ip6.sin6_addr;
824 ip6_mreqn.mreq.imr_interface = sockaddr2.ip6.sin6_addr;
826 #endif
828 #if HAVE_STRUCT_IP_MREQN
829 if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor,
830 &ip4_mreqn.mreqn, sizeof(ip4_mreqn.mreqn)) < 0) {
831 Error8("setsockopt(%d, %d, %d, {0x%08x,0x%08x,%d}, "F_Zu"): %s",
832 sfd->fd, opt->desc->major, opt->desc->minor,
833 ip4_mreqn.mreqn.imr_multiaddr.s_addr,
834 ip4_mreqn.mreqn.imr_address.s_addr,
835 ip4_mreqn.mreqn.imr_ifindex,
836 sizeof(ip4_mreqn.mreqn),
837 strerror(errno));
838 opt->desc = ODESC_ERROR;
839 return -1;
841 #else
842 if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor,
843 &ip4_mreqn.mreq, sizeof(ip4_mreqn.mreq)) < 0) {
844 Error7("setsockopt(%d, %d, %d, {0x%08x,0x%08x}, "F_Zu"): %s",
845 sfd->fd, opt->desc->major, opt->desc->minor,
846 ip4_mreqn.mreq.imr_multiaddr,
847 ip4_mreqn.mreq.imr_interface,
848 sizeof(ip4_mreqn.mreq),
849 strerror(errno));
850 opt->desc = ODESC_ERROR;
851 return -1;
853 #endif
854 return 0;
856 #endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */
859 #if HAVE_STRUCT_IP_MREQ_SOURCE
860 int xiotype_ip_add_source_membership(char *token, const struct optname *ent, struct opt *opt) {
861 /* we do not resolve the addresses here because we do not yet know
862 if we are coping with an IPv4 or IPv6 socat address */
863 const char *ends[] = { ":", NULL };
864 const char *nests[] = { "[","]", NULL };
865 char buff[512], *buffp=buff; size_t bufspc = sizeof(buff)-1;
866 char *tokp = token;
867 int parsres;
869 /* parse first IP address, expect ':' */
870 parsres =
871 nestlex((const char **)&tokp, &buffp, &bufspc,
872 ends, NULL, NULL, nests,
873 true, false, false);
874 if (parsres < 0) {
875 Error1("option too long: \"%s\"", token);
876 return -1;
877 } else if (parsres > 0) {
878 Error1("syntax error in \"%s\"", token);
879 return -1;
881 if (*tokp != ':') {
882 Error1("syntax in option %s: missing ':'", token);
884 *buffp++ = '\0';
885 if ((opt->value.u_string/*mcaddr*/ = strdup(buff)) == NULL) {
886 Error1("strdup(\"%s\"): out of memory", buff);
887 return -1;
890 ++tokp;
891 /* parse second IP address, expect ':' or '\0'' */
892 buffp = buff;
893 /*! result= */
894 parsres =
895 nestlex((const char **)&tokp, &buffp, &bufspc,
896 ends, NULL, NULL, nests,
897 true, false, false);
898 if (parsres < 0) {
899 Error1("option too long: \"%s\"", token);
900 return -1;
901 } else if (parsres > 0) {
902 Error1("syntax error in \"%s\"", token);
903 return -1;
905 if (*tokp != ':') {
906 Error1("syntax in option %s: missing ':'", token);
908 *buffp++ = '\0';
909 if ((opt->value2.u_string/*ifaddr*/ = strdup(buff)) == NULL) {
910 Error1("strdup(\"%s\"): out of memory", buff);
911 free(opt->value.u_string);
912 return -1;
915 ++tokp;
916 /* parse third IP address, expect ':' or '\0'' */
917 buffp = buff;
918 /*! result= */
919 parsres =
920 nestlex((const char **)&tokp, &buffp, &bufspc,
921 ends, NULL, NULL, nests,
922 true, false, false);
923 if (parsres < 0) {
924 Error1("option too long: \"%s\"", token);
925 return -1;
926 } else if (parsres > 0) {
927 Error1("syntax error in \"%s\"", token);
928 return -1;
930 if (*tokp) {
931 Error1("syntax in option %s: trailing cruft", token);
933 *buffp++ = '\0';
934 if ((opt->value3.u_string/*srcaddr*/ = strdup(buff)) == NULL) {
935 Error1("strdup(\"%s\"): out of memory", buff);
936 free(opt->value.u_string);
937 free(opt->value2.u_string);
938 return -1;
941 Info4("setting option \"%s\" to {0x%08x,0x%08x,0x%08x}",
942 ent->desc->defname,
943 ntohl(*(unsigned int *)opt->value.u_string/*mcaddr*/),
944 ntohl(*(unsigned int *)opt->value2.u_string/*ifaddr*/),
945 ntohl(*(unsigned int *)opt->value3.u_string/*srcaddr*/));
946 return 0;
949 int xioapply_ip_add_source_membership(struct single *sfd, struct opt *opt) {
950 struct ip_mreq_source ip4_mreq_src = {{0}};
951 /* IPv6 not supported - seems to have different handling */
952 union sockaddr_union sockaddr1;
953 socklen_t socklen1 = sizeof(sockaddr1.ip4);
954 union sockaddr_union sockaddr2;
955 socklen_t socklen2 = sizeof(sockaddr2.ip4);
956 union sockaddr_union sockaddr3;
957 socklen_t socklen3 = sizeof(sockaddr3.ip4);
958 int rc;
960 /* first parameter is always multicast address */
961 rc = xioresolve(opt->value.u_string/*mcaddr*/, NULL,
962 sfd->para.socket.la.soa.sa_family,
963 SOCK_DGRAM, IPPROTO_IP,
964 &sockaddr1, &socklen1, sfd->para.socket.ip.ai_flags);
965 if (rc < 0) {
966 return -1;
968 ip4_mreq_src.imr_multiaddr = sockaddr1.ip4.sin_addr;
969 /* second parameter is interface address */
970 rc = xioresolve(opt->value.u_string/*ifaddr*/, NULL,
971 sfd->para.socket.la.soa.sa_family,
972 SOCK_DGRAM, IPPROTO_IP,
973 &sockaddr2, &socklen2, sfd->para.socket.ip.ai_flags);
974 if (rc < 0) {
975 return -1;
977 ip4_mreq_src.imr_interface = sockaddr2.ip4.sin_addr;
978 /* third parameter is source address */
979 rc = xioresolve(opt->value.u_string/*srcaddr*/, NULL,
980 sfd->para.socket.la.soa.sa_family,
981 SOCK_DGRAM, IPPROTO_IP,
982 &sockaddr3, &socklen3, sfd->para.socket.ip.ai_flags);
983 if (rc < 0) {
984 return -1;
986 ip4_mreq_src.imr_sourceaddr = sockaddr3.ip4.sin_addr;
987 if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor,
988 &ip4_mreq_src, sizeof(ip4_mreq_src)) < 0) {
989 Error8("setsockopt(%d, %d, %d, {0x%08x,0x%08x,0x%08x}, "F_Zu"): %s",
990 sfd->fd, opt->desc->major, opt->desc->minor,
991 htonl((uint32_t)ip4_mreq_src.imr_multiaddr.s_addr),
992 ip4_mreq_src.imr_interface.s_addr,
993 ip4_mreq_src.imr_sourceaddr.s_addr,
994 sizeof(struct ip_mreq_source),
995 strerror(errno));
996 opt->desc = ODESC_ERROR;
997 return -1;
999 return 0;
1002 #endif /* HAVE_STRUCT_IP_MREQ_SOURCE */
1005 #if WITH_RESOLVE
1006 #if HAVE_RESOLV_H
1008 /* When there are options for resolver then this function saves the current
1009 resolver settings to save_res and applies the options to resolver libs state
1010 in _res.
1011 Returns 1 when there were options (state needs to be restored later, see
1012 xio_res_restore());
1013 Returns 0 when there were no options;
1014 Returns -1 on error. */
1015 int xio_res_init(
1016 struct single *sfd,
1017 struct __res_state *save_res)
1019 if (sfd->para.socket.ip.res.opts[0] ||
1020 sfd->para.socket.ip.res.opts[1] ||
1021 #if HAVE_RES_RETRANS
1022 sfd->para.socket.ip.res.retrans >= 0 ||
1023 #endif
1024 #if HAVE_RES_RETRY
1025 sfd->para.socket.ip.res.retry >= 0 ||
1026 #endif
1027 #if HAVE_RES_NSADDR_LIST
1028 sfd->para.socket.ip.res.nsaddr.sin_family != PF_UNSPEC ||
1029 #endif
1030 0 /* for canonical reasons */
1032 if (!(_res.options & RES_INIT)) {
1033 if (Res_init() < 0) {
1034 Error("res_init() failed");
1035 return -1;
1038 *save_res = _res;
1039 _res.options |= sfd->para.socket.ip.res.opts[0];
1040 _res.options &= ~sfd->para.socket.ip.res.opts[1];
1041 Debug2("changed _res.options from 0x%lx to 0x%lx",
1042 save_res->options, _res.options);
1044 #if HAVE_RES_RETRANS
1045 if (sfd->para.socket.ip.res.retrans >= 0) {
1046 _res.retrans = sfd->para.socket.ip.res.retrans;
1047 Debug2("changed _res.retrans from 0x%x to 0x%x",
1048 save_res->retrans, _res.retrans);
1050 #endif
1051 #if HAVE_RES_RETRY
1052 if (sfd->para.socket.ip.res.retry >= 0) {
1053 _res.retry = sfd->para.socket.ip.res.retry;
1054 Debug2("changed _res.retry from 0x%x to 0x%x",
1055 save_res->retry, _res.retry);
1057 #endif
1058 #if HAVE_RES_NSADDR_LIST
1059 if (sfd->para.socket.ip.res.nsaddr.sin_family == PF_INET) {
1060 _res.nscount = 1;
1061 _res.nsaddr_list[0] = sfd->para.socket.ip.res.nsaddr;
1062 if (_res.nsaddr_list[0].sin_port == htons(0))
1063 _res.nsaddr_list[0].sin_port = htons(53);
1064 Debug10("changed _res.nsaddr_list[0] from %u.%u.%u.%u:%u to %u.%u.%u.%u:%u",
1065 ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[0],
1066 ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[1],
1067 ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[2],
1068 ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[3],
1069 ntohs(save_res->nsaddr_list[0].sin_port),
1070 ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[0],
1071 ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[1],
1072 ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[2],
1073 ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[3],
1074 ntohs(_res.nsaddr_list[0].sin_port));
1076 #endif /* HAVE_RES_NSADDR_LIST */
1078 return 1;
1081 return 0;
1084 int xio_res_restore(
1085 struct __res_state *save_res)
1087 _res = *save_res;
1088 return 0;
1090 #endif /* HAVE_RESOLV_H */
1091 #endif /* WITH_RESOLVE */
1093 #endif /* _WITH_IP4 || _WITH_IP6 */