Version 1.8.0.0
[socat.git] / xio-ip6.c
blobbd94bdd0b2b7a8c02b87678f479ef98fb2cf9147
1 /* source: xio-ip6.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 IP6 related functions */
7 #include "xiosysincludes.h"
9 #if WITH_IP6
11 #include "xioopen.h"
12 #include "xio-ascii.h"
13 #include "xio-socket.h"
14 #include "xio-ip.h" /* xioresolve() */
16 #include "xio-ip6.h"
17 #include "nestlex.h"
20 static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen);
23 #ifdef IPV6_V6ONLY
24 const struct optdesc opt_ipv6_v6only = { "ipv6-v6only", "ipv6only", OPT_IPV6_V6ONLY, GROUP_SOCK_IP6, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_V6ONLY };
25 #endif
26 #ifdef IPV6_JOIN_GROUP
27 const struct optdesc opt_ipv6_join_group = { "ipv6-join-group", "join-group", OPT_IPV6_JOIN_GROUP, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_IP_MREQN, OFUNC_SOCKOPT, SOL_IPV6, IPV6_JOIN_GROUP };
28 #endif
29 #ifdef MCAST_JOIN_SOURCE_GROUP
30 const struct optdesc opt_ipv6_join_source_group = { "ipv6-join-source-group", "join-source-group", OPT_IPV6_JOIN_SOURCE_GROUP, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_GROUP_SOURCE_REQ, OFUNC_SOCKOPT, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP };
31 #endif
32 #ifdef IPV6_PKTINFO
33 const struct optdesc opt_ipv6_pktinfo = { "ipv6-pktinfo", "pktinfo", OPT_IPV6_PKTINFO, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_PKTINFO };
34 #endif
35 #ifdef IPV6_RECVPKTINFO
36 const struct optdesc opt_ipv6_recvpktinfo = { "ipv6-recvpktinfo", "recvpktinfo", OPT_IPV6_RECVPKTINFO, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVPKTINFO };
37 #endif
38 #ifdef IPV6_RTHDR
39 const struct optdesc opt_ipv6_rthdr = { "ipv6-rthdr", "rthdr", OPT_IPV6_RTHDR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RTHDR };
40 #endif
41 #ifdef IPV6_RECVRTHDR
42 const struct optdesc opt_ipv6_recvrthdr = { "ipv6-recvrthdr", "recvrthdr", OPT_IPV6_RECVRTHDR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVRTHDR };
43 #endif
44 #ifdef IPV6_AUTHHDR
45 const struct optdesc opt_ipv6_authhdr = { "ipv6-authhdr", "authhdr", OPT_IPV6_AUTHHDR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_AUTHHDR };
46 #endif
47 #ifdef IPV6_DSTOPTS
48 const struct optdesc opt_ipv6_dstopts = { "ipv6-dstopts", "dstopts", OPT_IPV6_DSTOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_DSTOPTS };
49 #endif
50 #ifdef IPV6_RECVDSTOPTS
51 const struct optdesc opt_ipv6_recvdstopts = { "ipv6-recvdstopts", "recvdstopts", OPT_IPV6_RECVDSTOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVDSTOPTS };
52 #endif
53 #ifdef IPV6_HOPOPTS
54 const struct optdesc opt_ipv6_hopopts = { "ipv6-hopopts", "hopopts", OPT_IPV6_HOPOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_HOPOPTS };
55 #endif
56 #ifdef IPV6_RECVHOPOPTS
57 const struct optdesc opt_ipv6_recvhopopts = { "ipv6-recvhopopts", "recvhopopts", OPT_IPV6_RECVHOPOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVHOPOPTS };
58 #endif
59 #ifdef IPV6_FLOWINFO /* is in linux/in6.h */
60 const struct optdesc opt_ipv6_flowinfo= { "ipv6-flowinfo","flowinfo",OPT_IPV6_FLOWINFO,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_FLOWINFO };
61 #endif
62 #ifdef IPV6_HOPLIMIT
63 const struct optdesc opt_ipv6_hoplimit= { "ipv6-hoplimit","hoplimit",OPT_IPV6_HOPLIMIT,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_HOPLIMIT };
64 #endif
65 const struct optdesc opt_ipv6_unicast_hops= { "ipv6-unicast-hops","unicast-hops",OPT_IPV6_UNICAST_HOPS,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_UNICAST_HOPS };
66 #ifdef IPV6_RECVHOPLIMIT
67 const struct optdesc opt_ipv6_recvhoplimit= { "ipv6-recvhoplimit","recvhoplimit",OPT_IPV6_RECVHOPLIMIT,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVHOPLIMIT };
68 #endif
69 #ifdef IPV6_RECVERR
70 const struct optdesc opt_ipv6_recverr = { "ipv6-recverr", "recverr", OPT_IPV6_RECVERR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVERR };
71 #endif
72 #ifdef IPV6_TCLASS
73 const struct optdesc opt_ipv6_tclass = { "ipv6-tclass", "tclass", OPT_IPV6_TCLASS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_TCLASS };
74 #endif
75 #ifdef IPV6_RECVTCLASS
76 const struct optdesc opt_ipv6_recvtclass = { "ipv6-recvtclass", "recvtclass", OPT_IPV6_RECVTCLASS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVTCLASS };
77 #endif
78 #ifdef IPV6_RECVPATHMTU
79 const struct optdesc opt_ipv6_recvpathmtu = { "ipv6-recvpathmtu", "recvpathmtu", OPT_IPV6_RECVPATHMTU, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVPATHMTU };
80 #endif
82 /* Returns canonical form of IPv6 address.
83 IPv6 address may be enclose in brackets.
84 Returns STAT_OK on success, STAT_NORETRY on failure. */
85 int xioip6_pton(
86 const char *src,
87 struct in6_addr *dst,
88 const int ai_flags[2])
90 union sockaddr_union sockaddr;
91 socklen_t sockaddrlen = sizeof(sockaddr);
93 if (src[0] == '[') {
94 char plainaddr[INET6_ADDRSTRLEN];
95 char *clos;
97 strncpy(plainaddr, src+1, INET6_ADDRSTRLEN);
98 plainaddr[INET6_ADDRSTRLEN-1] = '\0';
99 if ((clos = strchr(plainaddr, ']')) != NULL)
100 *clos = '\0';
101 return xioip6_pton(plainaddr, dst, ai_flags);
103 if (xioresolve(src, NULL, PF_INET6, 0, 0, &sockaddr, &sockaddrlen,
104 ai_flags)
105 != STAT_OK) {
106 return STAT_NORETRY;
108 *dst = sockaddr.ip6.sin6_addr;
109 return STAT_OK;
112 int xioparsenetwork_ip6(
113 const char *rangename,
114 struct xiorange *range,
115 const int ai_flags[2])
117 char *delimpos; /* absolute address of delimiter */
118 size_t delimind; /* index of delimiter in string */
119 unsigned int bits; /* netmask bits */
120 char *endptr;
121 char *baseaddr;
122 union sockaddr_union sockaddr;
123 socklen_t sockaddrlen = sizeof(sockaddr);
124 union xioin6_u *rangeaddr = (union xioin6_u *)&range->netaddr.ip6.sin6_addr;
125 union xioin6_u *rangemask = (union xioin6_u *)&range->netmask.ip6.sin6_addr;
126 union xioin6_u *nameaddr = (union xioin6_u *)&sockaddr.ip6.sin6_addr;
128 if ((delimpos = strchr(rangename, '/')) == NULL) {
129 Error1("xioparsenetwork_ip6(\"%s\",,): missing mask bits delimiter '/'",
130 rangename);
131 return STAT_NORETRY;
133 delimind = delimpos - rangename;
134 if (rangename[0] != '[' || rangename[delimind-1] != ']') {
135 Error1("missing brackets for IPv6 range definition \"%s\"",
136 rangename);
137 return STAT_NORETRY;
140 if ((baseaddr = strndup(rangename+1,delimind-2)) == NULL) {
141 Error1("strdup(\"%s\"): out of memory", rangename+1);
142 return STAT_NORETRY;
144 baseaddr[delimind-2] = '\0';
145 if (xioresolve(baseaddr, NULL, PF_INET6, 0, 0, &sockaddr, &sockaddrlen,
146 ai_flags)
147 != STAT_OK) {
148 return STAT_NORETRY;
150 rangeaddr->u6_addr32[0] = nameaddr->u6_addr32[0];
151 rangeaddr->u6_addr32[1] = nameaddr->u6_addr32[1];
152 rangeaddr->u6_addr32[2] = nameaddr->u6_addr32[2];
153 rangeaddr->u6_addr32[3] = nameaddr->u6_addr32[3];
154 bits = strtoul(delimpos+1, &endptr, 10);
155 if (! ((*(delimpos+1) != '\0') && (*endptr == '\0'))) {
156 Error1("not a valid netmask in \"%s\"", rangename);
157 bits = 128; /* most secure selection */
158 } else if (bits > 128) {
159 Error1("netmask \"%s\" is too large", rangename);
160 bits = 128;
163 /* I am starting to dislike C...uint32_t << 32 is undefined... */
164 if (bits == 0) {
165 rangemask->u6_addr32[0] = 0;
166 rangemask->u6_addr32[1] = 0;
167 rangemask->u6_addr32[2] = 0;
168 rangemask->u6_addr32[3] = 0;
169 } else if (bits <= 32) {
170 rangemask->u6_addr32[0] = htonl(0xffffffff << (32-bits));
171 rangemask->u6_addr32[1] = 0;
172 rangemask->u6_addr32[2] = 0;
173 rangemask->u6_addr32[3] = 0;
174 } else if (bits <= 64) {
175 rangemask->u6_addr32[0] = 0xffffffff;
176 rangemask->u6_addr32[1] = htonl(0xffffffff << (64-bits));
177 rangemask->u6_addr32[2] = 0;
178 rangemask->u6_addr32[3] = 0;
179 } else if (bits <= 96) {
180 rangemask->u6_addr32[0] = 0xffffffff;
181 rangemask->u6_addr32[1] = 0xffffffff;
182 rangemask->u6_addr32[2] = htonl(0xffffffff << (96-bits));
183 rangemask->u6_addr32[3] = 0;
184 } else {
185 rangemask->u6_addr32[0] = 0xffffffff;
186 rangemask->u6_addr32[1] = 0xffffffff;
187 rangemask->u6_addr32[2] = 0xffffffff;
188 rangemask->u6_addr32[3] = htonl(0xffffffff << (128-bits));
190 return 0;
193 int xiorange_ip6andmask(struct xiorange *range) {
194 int i;
195 #if 0
196 range->addr.s6_addr32[0] &= range->mask.s6_addr32[0];
197 range->addr.s6_addr32[1] &= range->mask.s6_addr32[1];
198 range->addr.s6_addr32[2] &= range->mask.s6_addr32[2];
199 range->addr.s6_addr32[3] &= range->mask.s6_addr32[3];
200 #else
201 for (i = 0; i < 16; ++i) {
202 range->netaddr.ip6.sin6_addr.s6_addr[i] &=
203 range->netmask.ip6.sin6_addr.s6_addr[i];
205 #endif
206 return 0;
209 /* check if peer address is within permitted range.
210 return >= 0 if so. */
211 int xiocheckrange_ip6(struct sockaddr_in6 *pa, struct xiorange *range) {
212 union xioin6_u masked;
213 int i;
214 char peername[256];
215 union xioin6_u *rangeaddr = (union xioin6_u *)&range->netaddr.ip6.sin6_addr;
216 union xioin6_u *rangemask = (union xioin6_u *)&range->netmask.ip6.sin6_addr;
218 Debug16("permitted client subnet: [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
219 htons(rangeaddr->u6_addr16[0]), htons(rangeaddr->u6_addr16[1]),
220 htons(rangeaddr->u6_addr16[2]), htons(rangeaddr->u6_addr16[3]),
221 htons(rangeaddr->u6_addr16[4]), htons(rangeaddr->u6_addr16[5]),
222 htons(rangeaddr->u6_addr16[6]), htons(rangeaddr->u6_addr16[7]),
223 htons(rangemask->u6_addr16[0]), htons(rangemask->u6_addr16[1]),
224 htons(rangemask->u6_addr16[2]), htons(rangemask->u6_addr16[3]),
225 htons(rangemask->u6_addr16[4]), htons(rangemask->u6_addr16[5]),
226 htons(rangemask->u6_addr16[6]), htons(rangemask->u6_addr16[7]));
227 Debug1("client address is %s",
228 sockaddr_inet6_info(pa, peername, sizeof(peername)));
230 for (i = 0; i < 4; ++i) {
231 masked.u6_addr32[i] = ((union xioin6_u *)&pa->sin6_addr.s6_addr[0])->u6_addr32[i] & rangemask->u6_addr32[i];
233 Debug8("masked address is [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
234 htons(masked.u6_addr16[0]), htons(masked.u6_addr16[1]),
235 htons(masked.u6_addr16[2]), htons(masked.u6_addr16[3]),
236 htons(masked.u6_addr16[4]), htons(masked.u6_addr16[5]),
237 htons(masked.u6_addr16[6]), htons(masked.u6_addr16[7]));
239 if (masked.u6_addr32[0] != rangeaddr->u6_addr32[0] ||
240 masked.u6_addr32[1] != rangeaddr->u6_addr32[1] ||
241 masked.u6_addr32[2] != rangeaddr->u6_addr32[2] ||
242 masked.u6_addr32[3] != rangeaddr->u6_addr32[3]) {
243 Debug1("client address %s is not permitted", peername);
244 return -1;
246 return 0;
250 #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)
251 /* provides info about the ancillary message:
252 converts the ancillary message in *cmsg into a form useable for further
253 processing. knows the specifics of common message types.
254 returns the number of resulting syntax elements in *num
255 returns a sequence of \0 terminated type strings in *typbuff
256 returns a sequence of \0 terminated name strings in *nambuff
257 returns a sequence of \0 terminated value strings in *valbuff
258 the respective len parameters specify the available space in the buffers
259 returns STAT_OK on success
261 int xiolog_ancillary_ip6(
262 struct single *sfd,
263 struct cmsghdr *cmsg,
264 int *num,
265 char *typbuff, int typlen,
266 char *nambuff, int namlen,
267 char *envbuff, int envlen,
268 char *valbuff, int vallen)
270 char scratch1[42]; /* can hold an IPv6 address in ASCII */
271 char scratch2[32];
272 size_t msglen;
274 *num = 1; /* good for most message types */
275 msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg);
276 envbuff[0] = '\0';
277 switch (cmsg->cmsg_type) {
278 #if defined(IPV6_PKTINFO) && HAVE_STRUCT_IN6_PKTINFO
279 case IPV6_PKTINFO: {
280 struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
281 *num = 2;
282 typbuff[0] = '\0'; strncat(typbuff, "IPV6_PKTINFO", typlen-1);
283 snprintf(nambuff, namlen, "%s%c%s", "dstaddr", '\0', "if");
284 snprintf(envbuff, envlen, "%s%c%s", "IPV6_DSTADDR", '\0', "IPV6_IF");
285 snprintf(valbuff, vallen, "%s%c%s",
286 inet6addr_info(&pktinfo->ipi6_addr, scratch1, sizeof(scratch1)),
287 '\0', xiogetifname(pktinfo->ipi6_ifindex, scratch2, -1));
289 return STAT_OK;
290 #endif /* defined(IPV6_PKTINFO) && HAVE_STRUCT_IN6_PKTINFO */
291 #ifdef IPV6_HOPLIMIT
292 case IPV6_HOPLIMIT:
293 typbuff[0] = '\0'; strncat(typbuff, "IPV6_HOPLIMIT", typlen-1);
294 nambuff[0] = '\0'; strncat(nambuff, "hoplimit", namlen-1);
296 int *intp = (int *)CMSG_DATA(cmsg);
297 snprintf(valbuff, vallen, "%d", *intp);
299 return STAT_OK;
300 #endif /* defined(IPV6_HOPLIMIT) */
301 #ifdef IPV6_RTHDR
302 case IPV6_RTHDR:
303 typbuff[0] = '\0'; strncat(typbuff, "IPV6_RTHDR", typlen-1);
304 nambuff[0] = '\0'; strncat(nambuff, "rthdr", namlen-1);
305 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
306 return STAT_OK;
307 #endif /* defined(IPV6_RTHDR) */
308 #ifdef IPV6_AUTHHDR
309 case IPV6_AUTHHDR:
310 typbuff[0] = '\0'; strncat(typbuff, "IPV6_AUTHHDR", typlen-1);
311 nambuff[0] = '\0'; strncat(nambuff, "authhdr", namlen-1);
312 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
313 return STAT_OK;
314 #endif
315 #ifdef IPV6_DSTOPTS
316 case IPV6_DSTOPTS:
317 typbuff[0] = '\0'; strncat(typbuff, "IPV6_DSTOPTS", typlen-1);
318 nambuff[0] = '\0'; strncat(nambuff, "dstopts", namlen-1);
319 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
320 return STAT_OK;
321 #endif /* defined(IPV6_DSTOPTS) */
322 #ifdef IPV6_HOPOPTS
323 case IPV6_HOPOPTS:
324 typbuff[0] = '\0'; strncat(typbuff, "IPV6_HOPOPTS", typlen-1);
325 nambuff[0] = '\0'; strncat(nambuff, "hopopts", namlen-1);
326 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
327 return STAT_OK;
328 #endif /* defined(IPV6_HOPOPTS) */
329 #ifdef IPV6_FLOWINFO
330 case IPV6_FLOWINFO:
331 typbuff[0] = '\0'; strncat(typbuff, "IPV6_FLOWINFO", typlen-1);
332 nambuff[0] = '\0'; strncat(nambuff, "flowinfo", namlen-1);
333 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
334 return STAT_OK;
335 #endif
336 #ifdef IPV6_TCLASS
337 case IPV6_TCLASS: {
338 unsigned int u;
339 typbuff[0] = '\0'; strncat(typbuff, "IPV6_TCLASS", typlen-1);
340 nambuff[0] = '\0'; strncat(nambuff, "tclass", namlen-1);
341 u = ntohl(*(unsigned int *)CMSG_DATA(cmsg));
342 xiodump((const unsigned char *)&u, msglen, valbuff, vallen, 0);
343 return STAT_OK;
345 #endif
346 default:
347 snprintf(typbuff, typlen, "IPV6.%u", cmsg->cmsg_type);
348 nambuff[0] = '\0'; strncat(nambuff, "data", namlen-1);
349 xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0);
350 return STAT_OK;
352 return STAT_OK;
354 #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */
357 /* convert the IP6 socket address to human readable form. buff should be at
358 least 50 chars long. output includes the port number */
359 static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen) {
360 if (xio_snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
361 #if HAVE_IP6_SOCKADDR==0
362 (sa->s6_addr[0]<<8)+sa->s6_addr[1],
363 (sa->s6_addr[2]<<8)+sa->s6_addr[3],
364 (sa->s6_addr[4]<<8)+sa->s6_addr[5],
365 (sa->s6_addr[6]<<8)+sa->s6_addr[7],
366 (sa->s6_addr[8]<<8)+sa->s6_addr[9],
367 (sa->s6_addr[10]<<8)+sa->s6_addr[11],
368 (sa->s6_addr[12]<<8)+sa->s6_addr[13],
369 (sa->s6_addr[14]<<8)+sa->s6_addr[15]
370 #elif HAVE_IP6_SOCKADDR==1
371 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[0]),
372 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[1]),
373 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[2]),
374 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[3]),
375 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[4]),
376 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[5]),
377 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[6]),
378 ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[7])
379 #elif HAVE_IP6_SOCKADDR==2
380 ntohs(((unsigned short *)&sa->u6_addr16)[0]),
381 ntohs(((unsigned short *)&sa->u6_addr16)[1]),
382 ntohs(((unsigned short *)&sa->u6_addr16)[2]),
383 ntohs(((unsigned short *)&sa->u6_addr16)[3]),
384 ntohs(((unsigned short *)&sa->u6_addr16)[4]),
385 ntohs(((unsigned short *)&sa->u6_addr16)[5]),
386 ntohs(((unsigned short *)&sa->u6_addr16)[6]),
387 ntohs(((unsigned short *)&sa->u6_addr16)[7])
388 #elif HAVE_IP6_SOCKADDR==3
389 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[0]),
390 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[1]),
391 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[2]),
392 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[3]),
393 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[4]),
394 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[5]),
395 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[6]),
396 ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[7])
397 #elif HAVE_IP6_SOCKADDR==4
398 (sa->_S6_un._S6_u8[0]<<8)|(sa->_S6_un._S6_u8[1]&0xff),
399 (sa->_S6_un._S6_u8[2]<<8)|(sa->_S6_un._S6_u8[3]&0xff),
400 (sa->_S6_un._S6_u8[4]<<8)|(sa->_S6_un._S6_u8[5]&0xff),
401 (sa->_S6_un._S6_u8[6]<<8)|(sa->_S6_un._S6_u8[7]&0xff),
402 (sa->_S6_un._S6_u8[8]<<8)|(sa->_S6_un._S6_u8[9]&0xff),
403 (sa->_S6_un._S6_u8[10]<<8)|(sa->_S6_un._S6_u8[11]&0xff),
404 (sa->_S6_un._S6_u8[12]<<8)|(sa->_S6_un._S6_u8[13]&0xff),
405 (sa->_S6_un._S6_u8[14]<<8)|(sa->_S6_un._S6_u8[15]&0xff)
406 #elif HAVE_IP6_SOCKADDR==5
407 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[0]),
408 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[1]),
409 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[2]),
410 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[3]),
411 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[4]),
412 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[5]),
413 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[6]),
414 ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[7])
415 #endif
416 ) >= blen) {
417 Warn("sockaddr_inet6_info(): buffer too short");
418 buff[blen-1] = '\0';
420 return buff;
424 /* returns information that can be used for constructing an environment
425 variable describing the socket address.
426 if idx is 0, this function writes "ADDR" into namebuff and the IP address
427 into valuebuff, and returns 1 (which means that one more info is there).
428 if idx is 1, it writes "PORT" into namebuff and the port number into
429 valuebuff, and returns 0 (no more info)
430 namelen and valuelen contain the max. allowed length of output chars in the
431 respective buffer.
432 on error this function returns -1.
435 xiosetsockaddrenv_ip6(int idx, char *namebuff, size_t namelen,
436 char *valuebuff, size_t valuelen,
437 struct sockaddr_in6 *sa, int ipproto) {
438 switch (idx) {
439 case 0:
440 strcpy(namebuff, "ADDR");
441 snprintf(valuebuff, valuelen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
442 (sa->sin6_addr.s6_addr[0]<<8)+
443 sa->sin6_addr.s6_addr[1],
444 (sa->sin6_addr.s6_addr[2]<<8)+
445 sa->sin6_addr.s6_addr[3],
446 (sa->sin6_addr.s6_addr[4]<<8)+
447 sa->sin6_addr.s6_addr[5],
448 (sa->sin6_addr.s6_addr[6]<<8)+
449 sa->sin6_addr.s6_addr[7],
450 (sa->sin6_addr.s6_addr[8]<<8)+
451 sa->sin6_addr.s6_addr[9],
452 (sa->sin6_addr.s6_addr[10]<<8)+
453 sa->sin6_addr.s6_addr[11],
454 (sa->sin6_addr.s6_addr[12]<<8)+
455 sa->sin6_addr.s6_addr[13],
456 (sa->sin6_addr.s6_addr[14]<<8)+
457 sa->sin6_addr.s6_addr[15]);
458 switch (ipproto) {
459 case IPPROTO_TCP:
460 case IPPROTO_UDP:
461 #ifdef IPPROTO_SCTP
462 case IPPROTO_SCTP:
463 #endif
464 return 1; /* there is port information to also be retrieved */
465 default:
466 return 0; /* no port info coming */
468 case 1:
469 strcpy(namebuff, "PORT");
470 snprintf(valuebuff, valuelen, "%u", ntohs(sa->sin6_port));
471 return 0;
473 return -1;
477 #if defined(HAVE_STRUCT_IPV6_MREQ)
478 int xioapply_ipv6_join_group(
479 struct single *sfd,
480 struct opt *opt)
482 struct ipv6_mreq ip6_mreq = {{{{0}}}};
483 union sockaddr_union sockaddr1;
484 socklen_t socklen1 = sizeof(sockaddr1.ip6);
485 int res;
487 /* Always two parameters */
488 /* First parameter is multicast address */
489 if ((res =
490 xioresolve(opt->value.u_string/*multiaddr*/, NULL,
491 sfd->para.socket.la.soa.sa_family,
492 SOCK_DGRAM, IPPROTO_IP,
493 &sockaddr1, &socklen1,
494 sfd->para.socket.ip.ai_flags))
495 != STAT_OK) {
496 return res;
498 ip6_mreq.ipv6mr_multiaddr = sockaddr1.ip6.sin6_addr;
499 if (ifindex(opt->value2.u_string/*param2*/,
500 &ip6_mreq.ipv6mr_interface, -1)
501 < 0) {
502 Error1("interface \"%s\" not found",
503 opt->value2.u_string/*param2*/);
504 ip6_mreq.ipv6mr_interface = htonl(0);
507 if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor,
508 &ip6_mreq, sizeof(ip6_mreq)) < 0) {
509 Error6("setsockopt(%d, %d, %d, {...,0x%08x}, "F_Zu"): %s",
510 sfd->fd, opt->desc->major, opt->desc->minor,
511 ip6_mreq.ipv6mr_interface,
512 sizeof(ip6_mreq),
513 strerror(errno));
514 opt->desc = ODESC_ERROR;
515 return -1;
517 return 0;
519 #endif /* defined(HAVE_STRUCT_IPV6_MREQ) */
521 #if HAVE_STRUCT_GROUP_SOURCE_REQ
522 int xiotype_ip6_join_source_group(
523 char *token, const struct optname *ent, struct opt *opt)
525 /* We do not resolve the addresses here because we do not yet know
526 if we are coping with an IPv4 or IPv6 socat address */
527 const char *ends[] = { ":", NULL };
528 const char *nests[] = { "[","]", NULL };
529 char buff[512], *buffp=buff; size_t bufspc = sizeof(buff)-1;
530 char *tokp = token;
531 int parsres;
533 /* Parse first IP address (mcast group), expect ':' */
534 parsres =
535 nestlex((const char **)&tokp, &buffp, &bufspc,
536 ends, NULL, NULL, nests,
537 true, false, false);
538 if (parsres < 0) {
539 Error1("option too long: \"%s\"", token);
540 return -1;
541 } else if (parsres > 0) {
542 Error1("syntax error in \"%s\"", token);
543 return -1;
545 if (*tokp != ':') {
546 Error1("syntax in option %s: missing ':'", token);
548 *buffp++ = '\0';
549 if ((opt->value.u_string/*mcaddr*/ = strdup(buff)) == NULL) {
550 int _errno = errno;
551 Error1("strdup(\"%s\"): out of memory", buff);
552 errno = _errno;
553 return -1;
556 ++tokp;
557 /* Parse interface name/index, expect ':' or '\0'' */
558 buffp = buff;
559 parsres =
560 nestlex((const char **)&tokp, &buffp, &bufspc,
561 ends, NULL, NULL, nests,
562 true, false, false);
563 if (parsres < 0) {
564 Error1("option too long: \"%s\"", token);
565 return -1;
566 } else if (parsres > 0) {
567 Error1("syntax error in \"%s\"", token);
568 return -1;
570 if (*tokp != ':') {
571 Error1("syntax in option %s: missing ':'", token);
573 *buffp++ = '\0';
574 if ((opt->value2.u_string/*ifindex*/ = Malloc(IF_NAMESIZE)) == NULL) {
575 int _errno = errno;
576 free(opt->value.u_string);
577 errno = _errno;
578 return -1;
580 strncpy(opt->value2.u_string/*ifindex*/, buff, IF_NAMESIZE);
582 ++tokp;
583 /* Parse second IP address (source address), expect ':' or '\0'' */
584 buffp = buff;
585 parsres =
586 nestlex((const char **)&tokp, &buffp, &bufspc,
587 ends, NULL, NULL, nests,
588 true, false, false);
589 if (parsres < 0) {
590 Error1("option too long: \"%s\"", token);
591 return -1;
592 } else if (parsres > 0) {
593 Error1("syntax error in \"%s\"", token);
594 return -1;
596 if (*tokp) {
597 Error1("syntax in option %s: trailing cruft", token);
599 *buffp++ = '\0';
600 if ((opt->value3.u_string/*srcaddr*/ = strdup(buff)) == NULL) {
601 int _errno = errno;
602 Error1("strdup(\"%s\"): out of memory", buff);
603 free(opt->value.u_string);
604 errno = _errno;
605 return -1;
608 Info4("setting option \"%s\" to {\"%s\",\"%s\",\"%s\"}",
609 ent->desc->defname,
610 opt->value.u_string/*mcaddr*/,
611 opt->value2.u_string/*ifindex*/,
612 opt->value3.u_string/*srcaddr*/);
614 if (!xioparms.experimental) {
615 Warn1("option %s is experimental", opt->desc->defname);
618 return 0;
621 int xioapply_ip6_join_source_group(struct single *sfd, struct opt *opt) {
622 struct group_source_req ip6_gsr = {0};
623 union sockaddr_union sockaddr1;
624 socklen_t socklen1 = sizeof(sockaddr1.ip6);
625 union sockaddr_union sockaddr2;
626 socklen_t socklen2 = sizeof(sockaddr2.ip6);
627 int res;
629 /* First parameter is always multicast address */
630 if ((res =
631 xioresolve(opt->value.u_string/*mcaddr*/, NULL,
632 sfd->para.socket.la.soa.sa_family,
633 SOCK_DGRAM, IPPROTO_IP, &sockaddr1, &socklen1,
634 sfd->para.socket.ip.ai_flags))
635 != STAT_OK) {
636 return res;
638 memcpy(&ip6_gsr.gsr_group, &sockaddr1.ip6, socklen1);
639 /* Second parameter is interface name/index */
640 if (ifindex(opt->value2.u_string/*ifindex*/,
641 &ip6_gsr.gsr_interface, -1)
642 < 0) {
643 Error1("interface \"%s\" not found",
644 opt->value.u_string/*ifindex*/);
645 ip6_gsr.gsr_interface = 0;
647 /* Third parameter is source address */
648 if ((res =
649 xioresolve(opt->value3.u_string/*srcaddr*/, NULL,
650 sfd->para.socket.la.soa.sa_family,
651 SOCK_DGRAM, IPPROTO_IP, &sockaddr2, &socklen2,
652 sfd->para.socket.ip.ai_flags))
653 != STAT_OK) {
654 return res;
656 memcpy(&ip6_gsr.gsr_source, &sockaddr2.ip6, socklen2);
657 if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor,
658 &ip6_gsr, sizeof(ip6_gsr)) < 0) {
659 Error6("setsockopt(%d, %d, %d, {%d,...}, "F_Zu"): %s",
660 sfd->fd, opt->desc->major, opt->desc->minor,
661 ip6_gsr.gsr_interface,
662 sizeof(ip6_gsr),
663 strerror(errno));
664 opt->desc = ODESC_ERROR;
665 return -1;
667 return 0;
669 #endif /* HAVE_STRUCT_GROUP_SOURCE_REQ */
671 #endif /* WITH_IP6 */