vm: fix a null dereference on out-of-memory
[minix.git] / lib / libc / net / rthdr.c
blobf008c58980b4103971cb78b10c5349d662aab9ab
1 /* $NetBSD: rthdr.c,v 1.17 2009/02/05 23:22:39 lukem Exp $ */
3 /*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: rthdr.c,v 1.17 2009/02/05 23:22:39 lukem Exp $");
35 #endif /* LIBC_SCCS and not lint */
37 #include "namespace.h"
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netinet/ip6.h>
45 #include <assert.h>
46 #include <string.h>
47 #include <stdio.h>
49 #ifdef __weak_alias
50 __weak_alias(inet6_rthdr_add,_inet6_rthdr_add)
51 __weak_alias(inet6_rthdr_getaddr,_inet6_rthdr_getaddr)
52 __weak_alias(inet6_rthdr_getflags,_inet6_rthdr_getflags)
53 __weak_alias(inet6_rthdr_init,_inet6_rthdr_init)
54 __weak_alias(inet6_rthdr_lasthop,_inet6_rthdr_lasthop)
55 __weak_alias(inet6_rthdr_segments,_inet6_rthdr_segments)
56 __weak_alias(inet6_rthdr_space,_inet6_rthdr_space)
57 __weak_alias(inet6_rth_space, _inet6_rth_space)
58 __weak_alias(inet6_rth_init, _inet6_rth_init)
59 __weak_alias(inet6_rth_add, _inet6_rth_add)
60 __weak_alias(inet6_rth_reverse, _inet6_rth_reverse)
61 __weak_alias(inet6_rth_segments, _inet6_rth_segments)
62 __weak_alias(inet6_rth_getaddr, _inet6_rth_getaddr)
63 #endif
66 * RFC2292 API
69 size_t
70 inet6_rthdr_space(type, seg)
71 int type, seg;
73 switch (type) {
74 case IPV6_RTHDR_TYPE_0:
75 if (seg < 1 || seg > 23)
76 return (0);
77 return (CMSG_SPACE(sizeof(struct in6_addr) * seg +
78 sizeof(struct ip6_rthdr0)));
79 default:
80 return (0);
84 struct cmsghdr *
85 inet6_rthdr_init(bp, type)
86 void *bp;
87 int type;
89 struct cmsghdr *ch;
90 struct ip6_rthdr *rthdr;
92 _DIAGASSERT(bp != NULL);
94 ch = (struct cmsghdr *)bp;
95 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(ch);
97 ch->cmsg_level = IPPROTO_IPV6;
98 ch->cmsg_type = IPV6_RTHDR;
100 switch (type) {
101 case IPV6_RTHDR_TYPE_0:
102 #ifdef COMPAT_RFC2292
103 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0) -
104 sizeof(struct in6_addr));
105 #else
106 ch->cmsg_len = CMSG_LEN(sizeof(struct ip6_rthdr0));
107 #endif
108 (void)memset(rthdr, 0, sizeof(struct ip6_rthdr0));
109 rthdr->ip6r_type = IPV6_RTHDR_TYPE_0;
110 return (ch);
111 default:
112 return (NULL);
117 inet6_rthdr_add(cmsg, addr, flags)
118 struct cmsghdr *cmsg;
119 const struct in6_addr *addr;
120 u_int flags;
122 struct ip6_rthdr *rthdr;
124 _DIAGASSERT(cmsg != NULL);
125 _DIAGASSERT(addr != NULL);
127 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
129 switch (rthdr->ip6r_type) {
130 case IPV6_RTHDR_TYPE_0:
132 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
133 if (flags != IPV6_RTHDR_LOOSE && flags != IPV6_RTHDR_STRICT)
134 return (-1);
135 if (rt0->ip6r0_segleft == 23)
136 return (-1);
137 if (flags != IPV6_RTHDR_LOOSE)
138 return (-1);
139 rt0->ip6r0_segleft++;
140 (void)memcpy(((caddr_t)(void *)rt0) +
141 ((rt0->ip6r0_len + 1) << 3), addr, sizeof(struct in6_addr));
142 rt0->ip6r0_len += sizeof(struct in6_addr) >> 3;
143 cmsg->cmsg_len = CMSG_LEN((rt0->ip6r0_len + 1) << 3);
144 break;
146 default:
147 return (-1);
150 return (0);
154 inet6_rthdr_lasthop(cmsg, flags)
155 struct cmsghdr *cmsg;
156 unsigned int flags;
158 struct ip6_rthdr *rthdr;
160 _DIAGASSERT(cmsg != NULL);
162 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
164 switch (rthdr->ip6r_type) {
165 case IPV6_RTHDR_TYPE_0:
167 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
168 if (rt0->ip6r0_segleft > 23)
169 return (-1);
170 if (flags != IPV6_RTHDR_LOOSE)
171 return (-1);
172 break;
174 default:
175 return (-1);
178 return (0);
181 #if 0
183 inet6_rthdr_reverse(in, out)
184 const struct cmsghdr *in;
185 struct cmsghdr *out;
188 return (-1);
190 #endif
193 inet6_rthdr_segments(cmsg)
194 const struct cmsghdr *cmsg;
196 const struct ip6_rthdr *rthdr;
198 _DIAGASSERT(cmsg != NULL);
200 rthdr = __UNCONST(CCMSG_DATA(cmsg));
202 switch (rthdr->ip6r_type) {
203 case IPV6_RTHDR_TYPE_0:
205 const struct ip6_rthdr0 *rt0 =
206 (const struct ip6_rthdr0 *)(const void *)rthdr;
208 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
209 return (-1);
211 return (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
214 default:
215 return (-1);
219 struct in6_addr *
220 inet6_rthdr_getaddr(cmsg, idx)
221 struct cmsghdr *cmsg;
222 int idx;
224 struct ip6_rthdr *rthdr;
226 _DIAGASSERT(cmsg != NULL);
228 rthdr = (struct ip6_rthdr *)(void *)CMSG_DATA(cmsg);
230 switch (rthdr->ip6r_type) {
231 case IPV6_RTHDR_TYPE_0:
233 struct ip6_rthdr0 *rt0 = (struct ip6_rthdr0 *)(void *)rthdr;
234 int naddr;
236 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
237 return NULL;
238 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
239 if (idx <= 0 || naddr < idx)
240 return NULL;
241 #ifdef COMPAT_RFC2292
242 return ((struct in6_addr *)(void *)(rt0 + 1)) + idx - 1;
243 #else
244 return ((struct in6_addr *)(void *)(rt0 + 1)) + idx;
245 #endif
248 default:
249 return NULL;
254 inet6_rthdr_getflags(cmsg, idx)
255 const struct cmsghdr *cmsg;
256 int idx;
258 const struct ip6_rthdr *rthdr;
260 _DIAGASSERT(cmsg != NULL);
262 rthdr = __UNCONST(CCMSG_DATA(cmsg));
264 switch (rthdr->ip6r_type) {
265 case IPV6_RTHDR_TYPE_0:
267 const struct ip6_rthdr0 *rt0 = (const struct ip6_rthdr0 *)
268 (const void *)rthdr;
269 int naddr;
271 if (rt0->ip6r0_len % 2 || 46 < rt0->ip6r0_len)
272 return (-1);
273 naddr = (rt0->ip6r0_len * 8) / sizeof(struct in6_addr);
274 if (idx < 0 || naddr < idx)
275 return (-1);
276 return IPV6_RTHDR_LOOSE;
279 default:
280 return (-1);
285 * RFC3542 (2292bis) API
288 socklen_t
289 inet6_rth_space(int type, int segments)
291 switch (type) {
292 case IPV6_RTHDR_TYPE_0:
293 return (((segments * 2) + 1) << 3);
294 default:
295 return (0); /* type not suppported */
299 void *
300 inet6_rth_init(void *bp, socklen_t bp_len, int type, int segments)
302 struct ip6_rthdr *rth;
303 struct ip6_rthdr0 *rth0;
305 _DIAGASSERT(bp != NULL);
307 rth = (struct ip6_rthdr *)bp;
309 switch (type) {
310 case IPV6_RTHDR_TYPE_0:
311 /* length validation */
312 if (bp_len < inet6_rth_space(IPV6_RTHDR_TYPE_0, segments))
313 return (NULL);
315 memset(bp, 0, bp_len);
316 rth0 = (struct ip6_rthdr0 *)(void *)rth;
317 rth0->ip6r0_len = segments * 2;
318 rth0->ip6r0_type = IPV6_RTHDR_TYPE_0;
319 rth0->ip6r0_segleft = 0;
320 rth0->ip6r0_reserved = 0;
321 break;
322 default:
323 return (NULL); /* type not supported */
326 return (bp);
330 inet6_rth_add(void *bp, const struct in6_addr *addr)
332 struct ip6_rthdr *rth;
333 struct ip6_rthdr0 *rth0;
334 struct in6_addr *nextaddr;
336 _DIAGASSERT(bp != NULL);
338 rth = (struct ip6_rthdr *)bp;
340 switch (rth->ip6r_type) {
341 case IPV6_RTHDR_TYPE_0:
342 rth0 = (struct ip6_rthdr0 *)(void *)rth;
343 nextaddr = (struct in6_addr *)(void *)(rth0 + 1)
344 + rth0->ip6r0_segleft;
345 *nextaddr = *addr;
346 rth0->ip6r0_segleft++;
347 break;
348 default:
349 return (-1); /* type not supported */
352 return (0);
356 inet6_rth_reverse(const void *in, void *out)
358 const struct ip6_rthdr *rth_in;
359 const struct ip6_rthdr0 *rth0_in;
360 struct ip6_rthdr0 *rth0_out;
361 int i, segments;
363 _DIAGASSERT(in != NULL);
364 _DIAGASSERT(out != NULL);
366 rth_in = (const struct ip6_rthdr *)in;
368 switch (rth_in->ip6r_type) {
369 case IPV6_RTHDR_TYPE_0:
370 rth0_in = (const struct ip6_rthdr0 *)in;
371 rth0_out = (struct ip6_rthdr0 *)out;
373 /* parameter validation XXX too paranoid? */
374 if (rth0_in->ip6r0_len % 2)
375 return (-1);
376 segments = rth0_in->ip6r0_len / 2;
378 /* we can't use memcpy here, since in and out may overlap */
379 memmove((void *)rth0_out, (const void *)rth0_in,
380 (unsigned int)(((rth0_in->ip6r0_len) + 1) << 3));
381 rth0_out->ip6r0_segleft = segments;
383 /* reverse the addresses */
384 for (i = 0; i < segments / 2; i++) {
385 struct in6_addr addr_tmp, *addr1, *addr2;
387 addr1 = (struct in6_addr *)(void *)(rth0_out + 1) + i;
388 addr2 = (struct in6_addr *)(void *)(rth0_out + 1) +
389 (segments - i - 1);
390 addr_tmp = *addr1;
391 *addr1 = *addr2;
392 *addr2 = addr_tmp;
395 break;
396 default:
397 return (-1); /* type not supported */
400 return (0);
404 inet6_rth_segments(const void *bp)
406 const struct ip6_rthdr *rh;
407 const struct ip6_rthdr0 *rh0;
408 unsigned int addrs;
410 _DIAGASSERT(bp != NULL);
412 rh = (const struct ip6_rthdr *)bp;
414 switch (rh->ip6r_type) {
415 case IPV6_RTHDR_TYPE_0:
416 rh0 = (const struct ip6_rthdr0 *)bp;
419 * Validation for a type-0 routing header.
420 * Is this too strict?
422 if ((rh0->ip6r0_len % 2) != 0 ||
423 (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft)
424 return (-1);
426 return (addrs);
427 default:
428 return (-1); /* unknown type */
432 struct in6_addr *
433 inet6_rth_getaddr(const void *bp, int idx)
435 const struct ip6_rthdr *rh;
436 const struct ip6_rthdr0 *rh0;
437 unsigned int addrs;
439 _DIAGASSERT(bp != NULL);
441 rh = (const struct ip6_rthdr *)bp;
443 switch (rh->ip6r_type) {
444 case IPV6_RTHDR_TYPE_0:
445 rh0 = (const struct ip6_rthdr0 *)bp;
448 * Validation for a type-0 routing header.
449 * Is this too strict?
451 if ((rh0->ip6r0_len % 2) != 0 ||
452 (addrs = (rh0->ip6r0_len / 2)) < rh0->ip6r0_segleft)
453 return (NULL);
455 if (idx < 0 || addrs <= (unsigned int)idx)
456 return (NULL);
458 return (((struct in6_addr *)(void *)__UNCONST(rh0 + 1)) + idx);
459 default:
460 return (NULL); /* unknown type */