VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/net/netaddrstr2.cpp@ 72426

Last change on this file since 72426 was 71435, checked in by vboxsync, 7 years ago

IPRT/net: RTNetStrToIPv4AddrEx - return VWRN_TRAILING_SPACES and
VWRN_TRAILING_CHARS. Adapt callers. Add more test cases to make sure
trailers are handled as before.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: netaddrstr2.cpp 71435 2018-03-21 14:30:29Z vboxsync $ */
2/** @file
3 * IPRT - Network Address String Handling.
4 */
5
6/*
7 * Copyright (C) 2013-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/net.h>
33
34#include <iprt/asm.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37#include <iprt/stream.h>
38#include "internal/string.h"
39
40
41DECLHIDDEN(int) rtNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr,
42 char **ppszNext)
43{
44 char *pszNext;
45 int rc;
46
47 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
48 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
49
50 rc = RTStrToUInt8Ex(pcszAddr, &pszNext, 10, &pAddr->au8[0]);
51 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
52 return VERR_INVALID_PARAMETER;
53 if (*pszNext++ != '.')
54 return VERR_INVALID_PARAMETER;
55
56 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
57 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
58 return VERR_INVALID_PARAMETER;
59 if (*pszNext++ != '.')
60 return VERR_INVALID_PARAMETER;
61
62 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
63 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
64 return VERR_INVALID_PARAMETER;
65 if (*pszNext++ != '.')
66 return VERR_INVALID_PARAMETER;
67
68 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
69 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES && rc != VWRN_TRAILING_CHARS)
70 return VERR_INVALID_PARAMETER;
71
72 if (ppszNext != NULL)
73 *ppszNext = pszNext;
74 return rc;
75}
76
77
78RTDECL(int) RTNetStrToIPv4AddrEx(const char *pcszAddr, PRTNETADDRIPV4 pAddr,
79 char **ppszNext)
80{
81 return rtNetStrToIPv4AddrEx(pcszAddr, pAddr, ppszNext);
82}
83RT_EXPORT_SYMBOL(RTNetStrToIPv4AddrEx);
84
85
86RTDECL(int) RTNetStrToIPv4Addr(const char *pcszAddr, PRTNETADDRIPV4 pAddr)
87{
88 char *pszNext;
89 int rc;
90
91 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
92 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
93
94 pcszAddr = RTStrStripL(pcszAddr);
95 rc = rtNetStrToIPv4AddrEx(pcszAddr, pAddr, &pszNext);
96 if (RT_FAILURE(rc) || rc == VWRN_TRAILING_CHARS)
97 return VERR_INVALID_PARAMETER;
98
99 return VINF_SUCCESS;
100}
101RT_EXPORT_SYMBOL(RTNetStrToIPv4Addr);
102
103
104RTDECL(bool) RTNetIsIPv4AddrStr(const char *pcszAddr)
105{
106 RTNETADDRIPV4 addrIPv4;
107 char *pszNext;
108 int rc;
109
110 if (pcszAddr == NULL)
111 return false;
112
113 rc = rtNetStrToIPv4AddrEx(pcszAddr, &addrIPv4, &pszNext);
114 if (rc != VINF_SUCCESS)
115 return false;
116
117 if (*pszNext != '\0')
118 return false;
119
120 return true;
121}
122RT_EXPORT_SYMBOL(RTNetIsIPv4AddrStr);
123
124
125RTDECL(bool) RTNetStrIsIPv4AddrAny(const char *pcszAddr)
126{
127 RTNETADDRIPV4 addrIPv4;
128 char *pszNext;
129 int rc;
130
131 if (pcszAddr == NULL)
132 return false;
133
134 pcszAddr = RTStrStripL(pcszAddr);
135 rc = rtNetStrToIPv4AddrEx(pcszAddr, &addrIPv4, &pszNext);
136 if (RT_FAILURE(rc) || rc == VWRN_TRAILING_CHARS)
137 return false;
138
139 if (addrIPv4.u != 0u) /* INADDR_ANY? */
140 return false;
141
142 return true;
143}
144RT_EXPORT_SYMBOL(RTNetStrIsIPv4AddrAny);
145
146
147RTDECL(int) RTNetMaskToPrefixIPv4(PCRTNETADDRIPV4 pMask, int *piPrefix)
148{
149 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
150
151 if (pMask->u == 0)
152 {
153 if (piPrefix != NULL)
154 *piPrefix = 0;
155 return VINF_SUCCESS;
156 }
157
158 const uint32_t uMask = RT_N2H_U32(pMask->u);
159
160 uint32_t uPrefixMask = UINT32_C(0xffffffff);
161 int iPrefixLen = 32;
162
163 while (iPrefixLen > 0)
164 {
165 if (uMask == uPrefixMask)
166 {
167 if (piPrefix != NULL)
168 *piPrefix = iPrefixLen;
169 return VINF_SUCCESS;
170 }
171
172 --iPrefixLen;
173 uPrefixMask <<= 1;
174 }
175
176 return VERR_INVALID_PARAMETER;
177}
178RT_EXPORT_SYMBOL(RTNetMaskToPrefixIPv4);
179
180
181RTDECL(int) RTNetPrefixToMaskIPv4(int iPrefix, PRTNETADDRIPV4 pMask)
182{
183 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
184
185 if (RT_UNLIKELY(iPrefix < 0 || 32 < iPrefix))
186 return VERR_INVALID_PARAMETER;
187
188 if (RT_LIKELY(iPrefix != 0))
189 pMask->u = RT_H2N_U32(UINT32_C(0xffffffff) << (32 - iPrefix));
190 else /* avoid UB in the shift */
191 pMask->u = 0;
192
193 return VINF_SUCCESS;
194}
195RT_EXPORT_SYMBOL(RTNetPrefixToMaskIPv4);
196
197
198static int rtNetStrToHexGroup(const char *pcszValue, char **ppszNext,
199 uint16_t *pu16)
200{
201 char *pszNext;
202 int rc;
203
204 rc = RTStrToUInt16Ex(pcszValue, &pszNext, 16, pu16);
205 if (RT_FAILURE(rc))
206 return rc;
207
208 if ( rc != VINF_SUCCESS
209 && rc != VWRN_TRAILING_CHARS
210 && rc != VWRN_TRAILING_SPACES)
211 {
212 return -rc; /* convert warning to error */
213 }
214
215 /* parser always accepts 0x prefix */
216 if (pcszValue[0] == '0' && (pcszValue[1] == 'x' || pcszValue[1] == 'X'))
217 {
218 if (pu16)
219 *pu16 = 0;
220 if (ppszNext)
221 *ppszNext = (/* UNCONST */ char *)pcszValue + 1; /* to 'x' */
222 return VWRN_TRAILING_CHARS;
223 }
224
225 /* parser accepts leading zeroes "000000f" */
226 if (pszNext - pcszValue > 4)
227 return VERR_PARSE_ERROR;
228
229 if (ppszNext)
230 *ppszNext = pszNext;
231 return rc;
232}
233
234
235/*
236 * This function deals only with the hex-group IPv6 address syntax
237 * proper (with possible embedded IPv4).
238 */
239DECLHIDDEN(int) rtNetStrToIPv6AddrBase(const char *pcszAddr, PRTNETADDRIPV6 pAddrResult,
240 char **ppszNext)
241{
242 RTNETADDRIPV6 ipv6;
243 RTNETADDRIPV4 ipv4;
244 const char *pcszPos;
245 char *pszNext;
246 int iGroup;
247 uint16_t u16;
248 int rc;
249
250 RT_ZERO(ipv6);
251
252 pcszPos = pcszAddr;
253
254 if (pcszPos[0] == ':') /* compressed zero run at the beginning? */
255 {
256 if (pcszPos[1] != ':')
257 return VERR_PARSE_ERROR;
258
259 pcszPos += 2; /* skip over "::" */
260 pszNext = (/* UNCONST */ char *)pcszPos;
261 iGroup = 1;
262 }
263 else
264 {
265 /*
266 * Scan forward until we either get complete address or find
267 * "::" compressed zero run.
268 */
269 pszNext = NULL; /* (MSC incorrectly thinks it may be used unitialized) */
270 for (iGroup = 0; iGroup < 8; ++iGroup)
271 {
272 /* check for embedded IPv4 at the end */
273 if (iGroup == 6)
274 {
275 rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
276 if (rc == VINF_SUCCESS)
277 {
278 ipv6.au32[3] = ipv4.au32[0];
279 iGroup = 8; /* filled 6 and 7 */
280 break; /* we are done */
281 }
282 }
283
284 rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
285 if (RT_FAILURE(rc))
286 return VERR_PARSE_ERROR;
287
288 ipv6.au16[iGroup] = RT_H2N_U16(u16);
289
290 if (iGroup == 7)
291 pcszPos = pszNext;
292 else
293 {
294 /* skip the colon that delimits this group */
295 if (*pszNext != ':')
296 return VERR_PARSE_ERROR;
297 pcszPos = pszNext + 1;
298
299 /* compressed zero run? */
300 if (*pcszPos == ':')
301 {
302 ++pcszPos; /* skip over :: */
303 pszNext += 2; /* skip over :: (in case we are done) */
304 iGroup += 2; /* current field and the zero in the next */
305 break;
306 }
307 }
308 }
309 }
310
311 if (iGroup != 8)
312 {
313 /*
314 * iGroup is the first group that can be filled by the part of
315 * the address after "::".
316 */
317 RTNETADDRIPV6 ipv6Tail;
318 const int iMaybeStart = iGroup;
319 int j;
320
321 RT_ZERO(ipv6Tail);
322
323 /*
324 * We try to accept longest match; we'll shift if necessary.
325 * Unlike the first loop, a failure to parse a group doesn't
326 * mean invalid address.
327 */
328 for (; iGroup < 8; ++iGroup)
329 {
330 /* check for embedded IPv4 at the end */
331 if (iGroup <= 6)
332 {
333 rc = rtNetStrToIPv4AddrEx(pcszPos, &ipv4, &pszNext);
334 if (rc == VINF_SUCCESS)
335 {
336 ipv6Tail.au16[iGroup] = ipv4.au16[0];
337 ipv6Tail.au16[iGroup + 1] = ipv4.au16[1];
338 iGroup = iGroup + 2; /* these two are done */
339 break; /* the rest is trailer */
340 }
341 }
342
343 rc = rtNetStrToHexGroup(pcszPos, &pszNext, &u16);
344 if (RT_FAILURE(rc))
345 break;
346
347 ipv6Tail.au16[iGroup] = RT_H2N_U16(u16);
348
349 if (iGroup == 7)
350 pcszPos = pszNext;
351 else
352 {
353 if (*pszNext != ':')
354 {
355 ++iGroup; /* this one is done */
356 break; /* the rest is trailer */
357 }
358
359 pcszPos = pszNext + 1;
360 }
361 }
362
363 for (j = 7, --iGroup; iGroup >= iMaybeStart; --j, --iGroup)
364 ipv6.au16[j] = ipv6Tail.au16[iGroup];
365 }
366
367 if (pAddrResult != NULL)
368 memcpy(pAddrResult, &ipv6, sizeof(ipv6));
369 if (ppszNext != NULL)
370 *ppszNext = pszNext;
371 return VINF_SUCCESS;
372}
373
374
375DECLHIDDEN(int) rtNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
376 char **ppszZone, char **ppszNext)
377{
378 char *pszNext, *pszZone;
379 int rc;
380
381 rc = rtNetStrToIPv6AddrBase(pcszAddr, pAddr, &pszNext);
382 if (RT_FAILURE(rc))
383 return rc;
384
385 if (*pszNext != '%') /* is there a zone id? */
386 {
387 pszZone = NULL;
388 }
389 else
390 {
391 pszZone = pszNext + 1; /* skip '%' zone id delimiter */
392 if (*pszZone == '\0')
393 return VERR_PARSE_ERROR; /* empty zone id */
394
395 /*
396 * XXX: this is speculative as zone id syntax is
397 * implementation dependent, so we kinda guess here (accepting
398 * unreserved characters from URI syntax).
399 */
400 for (pszNext = pszZone; *pszNext != '\0'; ++pszNext)
401 {
402 const char c = *pszNext;
403 if ( !('0' <= c && c <= '9')
404 && !('a' <= c && c <= 'z')
405 && !('A' <= c && c <= 'Z')
406 && c != '_'
407 && c != '.'
408 && c != '-'
409 && c != '~')
410 {
411 break;
412 }
413 }
414 }
415
416 if (ppszZone != NULL)
417 *ppszZone = pszZone;
418 if (ppszNext != NULL)
419 *ppszNext = pszNext;
420
421 if (*pszNext == '\0') /* all input string consumed */
422 return VINF_SUCCESS;
423 else
424 {
425 while (*pszNext == ' ' || *pszNext == '\t')
426 ++pszNext;
427 if (*pszNext == '\0')
428 return VWRN_TRAILING_SPACES;
429 else
430 return VWRN_TRAILING_CHARS;
431 }
432}
433
434
435RTDECL(int) RTNetStrToIPv6AddrEx(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
436 char **ppszNext)
437{
438 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
439 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
440
441 return rtNetStrToIPv6AddrBase(pcszAddr, pAddr, ppszNext);
442}
443RT_EXPORT_SYMBOL(RTNetStrToIPv6AddrEx);
444
445
446RTDECL(int) RTNetStrToIPv6Addr(const char *pcszAddr, PRTNETADDRIPV6 pAddr,
447 char **ppszZone)
448{
449 int rc;
450
451 AssertPtrReturn(pcszAddr, VERR_INVALID_PARAMETER);
452 AssertPtrReturn(pAddr, VERR_INVALID_PARAMETER);
453 AssertPtrReturn(ppszZone, VERR_INVALID_PARAMETER);
454
455 pcszAddr = RTStrStripL(pcszAddr);
456 rc = rtNetStrToIPv6AddrEx(pcszAddr, pAddr, ppszZone, NULL);
457 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
458 return VERR_INVALID_PARAMETER;
459
460 return VINF_SUCCESS;
461}
462RT_EXPORT_SYMBOL(RTNetStrToIPv6Addr);
463
464
465RTDECL(bool) RTNetIsIPv6AddrStr(const char *pcszAddr)
466{
467 RTNETADDRIPV6 addrIPv6;
468 int rc;
469
470 if (pcszAddr == NULL)
471 return false;
472
473 rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, NULL, NULL);
474 if (rc != VINF_SUCCESS)
475 return false;
476
477 return true;
478}
479RT_EXPORT_SYMBOL(RTNetIsIPv6AddrStr);
480
481
482RTDECL(bool) RTNetStrIsIPv6AddrAny(const char *pcszAddr)
483{
484 RTNETADDRIPV6 addrIPv6;
485 char *pszZone, *pszNext;
486 int rc;
487
488 if (pcszAddr == NULL)
489 return false;
490
491 pcszAddr = RTStrStripL(pcszAddr);
492 rc = rtNetStrToIPv6AddrEx(pcszAddr, &addrIPv6, &pszZone, &pszNext);
493 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
494 return false;
495
496 if (pszZone != NULL)
497 return false;
498
499 if (addrIPv6.s.Lo != 0 || addrIPv6.s.Hi != 0) /* in6addr_any? */
500 return false;
501
502 return true;
503}
504RT_EXPORT_SYMBOL(RTNetStrIsIPv6AddrAny);
505
506
507RTDECL(int) RTNetMaskToPrefixIPv6(PCRTNETADDRIPV6 pMask, int *piPrefix)
508{
509 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
510
511 int iPrefix = 0;
512 unsigned int i;
513
514 for (i = 0; i < RT_ELEMENTS(pMask->au8); ++i)
515 {
516 int iBits;
517 switch (pMask->au8[i])
518 {
519 case 0x00: iBits = 0; break;
520 case 0x80: iBits = 1; break;
521 case 0xc0: iBits = 2; break;
522 case 0xe0: iBits = 3; break;
523 case 0xf0: iBits = 4; break;
524 case 0xf8: iBits = 5; break;
525 case 0xfc: iBits = 6; break;
526 case 0xfe: iBits = 7; break;
527 case 0xff: iBits = 8; break;
528 default: /* non-contiguous mask */
529 return VERR_INVALID_PARAMETER;
530 }
531
532 iPrefix += iBits;
533 if (iBits != 8)
534 break;
535 }
536
537 for (++i; i < RT_ELEMENTS(pMask->au8); ++i)
538 if (pMask->au8[i] != 0)
539 return VERR_INVALID_PARAMETER;
540
541 if (piPrefix != NULL)
542 *piPrefix = iPrefix;
543 return VINF_SUCCESS;
544}
545RT_EXPORT_SYMBOL(RTNetMaskToPrefixIPv6);
546
547
548RTDECL(int) RTNetPrefixToMaskIPv6(int iPrefix, PRTNETADDRIPV6 pMask)
549{
550 AssertReturn(pMask != NULL, VERR_INVALID_PARAMETER);
551
552 if (RT_UNLIKELY(iPrefix < 0 || 128 < iPrefix))
553 return VERR_INVALID_PARAMETER;
554
555 for (unsigned int i = 0; i < RT_ELEMENTS(pMask->au32); ++i)
556 {
557 if (iPrefix == 0)
558 {
559 pMask->au32[i] = 0;
560 }
561 else if (iPrefix >= 32)
562 {
563 pMask->au32[i] = UINT32_C(0xffffffff);
564 iPrefix -= 32;
565 }
566 else
567 {
568 pMask->au32[i] = RT_H2N_U32(UINT32_C(0xffffffff) << (32 - iPrefix));
569 iPrefix = 0;
570 }
571 }
572
573 return VINF_SUCCESS;
574}
575RT_EXPORT_SYMBOL(RTNetPrefixToMaskIPv6);
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette