You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
14 KiB
582 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1989-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ip6util.c
|
|
|
|
Abstract:
|
|
|
|
Some IP6 utilities
|
|
|
|
Author:
|
|
|
|
Jiandong Ruan
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifndef isxdigit_W
|
|
#define isxdigit_W(x) \
|
|
((x >= L'0' && x <= L'9') || \
|
|
(x >= L'a' && x <= L'f') || \
|
|
(x >= L'A' && x <= L'F'))
|
|
#endif
|
|
|
|
#ifndef isdigit_W
|
|
#define isdigit_W(x) (x >= L'0' && x <= L'9')
|
|
#endif
|
|
|
|
ULONG __inline
|
|
xdigit2hex(WCHAR ch)
|
|
{
|
|
ASSERT(isxdigit_W(ch));
|
|
|
|
if (ch <= L'9') {
|
|
return (ch - L'0');
|
|
} else if (ch >= L'A' && ch <= L'F') {
|
|
return (ch - L'A') + 10;
|
|
} else {
|
|
return (ch - L'a') + 10;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
inet_addr6W(
|
|
IN WCHAR *str,
|
|
IN OUT PSMB_IP6_ADDRESS addr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert an unicode string into a IP6 address (network order).
|
|
|
|
The L(3) grammar of the IP6 address:
|
|
|
|
start: head COLON COLON tail
|
|
head: hexs
|
|
tail: hexs
|
|
hexs: hex COLON hexs
|
|
|
|
|
COLON: ':'
|
|
|
|
A 5-state automata is used to parse the string,
|
|
states: {S, A, B, C, D}
|
|
Starting state: S
|
|
Accepting state: {S, B, C}
|
|
|
|
state transition rules:
|
|
S ==> S on a hex digit
|
|
S ==> A on a COLON
|
|
|
|
A ==> S on a hex digit
|
|
A ==> B on a COLON
|
|
|
|
B ==> C on a hex digit
|
|
|
|
C ==> C on a hex digit
|
|
C ==> D on a COLON
|
|
|
|
D ==> C on a hex digit
|
|
|
|
Arguments:
|
|
|
|
str The unicode string containing the IP6 address
|
|
addr The output IP6 address
|
|
|
|
Return Value:
|
|
|
|
TRUE if the string is accepted by the automata and an IP6 address (network order)
|
|
is return in the 'addr'
|
|
FALSE if the string is rejected by the automata
|
|
result in 'addr' is undetermined.
|
|
|
|
--*/
|
|
{
|
|
enum { STATE_S, STATE_A, STATE_B, STATE_C, STATE_D } state;
|
|
int i, num, tail;
|
|
ULONG hex;
|
|
WCHAR ch;
|
|
|
|
state = STATE_S;
|
|
|
|
addr->sin6_scope_id = 0;
|
|
|
|
num = 0;
|
|
tail = 0;
|
|
hex = 0;
|
|
while ((ch = *str++)) {
|
|
if (ch == '%') {
|
|
break;
|
|
}
|
|
if (!isxdigit_W(ch) && ch != L':') {
|
|
return FALSE;
|
|
}
|
|
|
|
switch(state) {
|
|
case STATE_S:
|
|
if (L':' == ch) {
|
|
state = STATE_A;
|
|
} else {
|
|
hex <<= 4;
|
|
hex |= xdigit2hex(ch);
|
|
if (hex > 0xFFFFU) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_A:
|
|
if (L':' == ch) {
|
|
state = STATE_B;
|
|
} else {
|
|
if (num >= 8) {
|
|
return FALSE;
|
|
}
|
|
addr->sin6_addr[num++] = htons((USHORT)hex);
|
|
hex = xdigit2hex(ch);
|
|
state = STATE_S;
|
|
}
|
|
break;
|
|
|
|
case STATE_B:
|
|
if (L':' == ch) {
|
|
return FALSE;
|
|
}
|
|
if (num >= 8) {
|
|
return FALSE;
|
|
}
|
|
addr->sin6_addr[num++] = htons((USHORT)hex);
|
|
tail = num;
|
|
hex = xdigit2hex(ch);
|
|
state = STATE_C;
|
|
break;
|
|
|
|
case STATE_C:
|
|
if (L':' == ch) {
|
|
state = STATE_D;
|
|
} else {
|
|
hex <<= 4;
|
|
hex |= xdigit2hex(ch);
|
|
if (hex > 0xFFFFU) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_D:
|
|
if (L':' == ch) {
|
|
return FALSE;
|
|
}
|
|
if (num >= 8) {
|
|
return FALSE;
|
|
}
|
|
addr->sin6_addr[num++] = htons((USHORT)hex);
|
|
hex = xdigit2hex(ch);
|
|
state = STATE_C;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reject it since it ends up with a rejecting state.
|
|
//
|
|
if (state == STATE_A || state == STATE_D) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (num >= 8) {
|
|
return FALSE;
|
|
}
|
|
addr->sin6_addr[num++] = htons((USHORT)hex);
|
|
|
|
if (state == STATE_B) {
|
|
for (i = num; i < 8; i++) {
|
|
addr->sin6_addr[i] = 0;
|
|
}
|
|
return TRUE;
|
|
} else if (state == STATE_S) {
|
|
return (8 == num);
|
|
}
|
|
|
|
ASSERT (state == STATE_C);
|
|
|
|
ASSERT(tail <= num);
|
|
for (i = num - 1; i >= tail; i--) {
|
|
addr->sin6_addr[8 - num + i] = addr->sin6_addr[i];
|
|
}
|
|
for (i = tail; i < 8 - num + tail; i++) {
|
|
addr->sin6_addr[i] = 0;
|
|
}
|
|
|
|
//
|
|
// Parse the scope ID
|
|
//
|
|
if (ch == '%') {
|
|
LONG scope_id;
|
|
|
|
scope_id = 0;
|
|
while ((ch = *str++)) {
|
|
if (!isdigit_W(ch)) {
|
|
return FALSE;
|
|
}
|
|
scope_id = scope_id * 10 + (ch - L'0');
|
|
}
|
|
addr->sin6_scope_id = scope_id;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
inet_ntoa6W(
|
|
OUT WCHAR *Buffer,
|
|
IN DWORD Size,
|
|
IN PSMB_IP6_ADDRESS addr
|
|
)
|
|
{
|
|
USHORT ch, tmp;
|
|
int i, tail, len, curtail, curlen;
|
|
DWORD j, k;
|
|
|
|
tail = 8;
|
|
len = 0;
|
|
curtail = curlen = 0;
|
|
for (i = 0; i < 8; i++) {
|
|
if (0 == addr->sin6_addr[i]) {
|
|
curlen++;
|
|
} else {
|
|
if (curlen > len) {
|
|
tail = curtail;
|
|
len = curlen;
|
|
}
|
|
curtail = i + 1;
|
|
curlen = 0;
|
|
}
|
|
}
|
|
if (curlen > len) {
|
|
tail = curtail;
|
|
len = curlen;
|
|
}
|
|
|
|
j = 0;
|
|
for (i = 0; i < tail; i++) {
|
|
ch = htons(addr->sin6_addr[i]);
|
|
if (ch) {
|
|
k = 4;
|
|
while (0 == (ch & 0xf000)) {
|
|
ch <<= 4;
|
|
k--;
|
|
}
|
|
} else {
|
|
k = 1;
|
|
}
|
|
while (j < Size) {
|
|
tmp = (ch & 0xf000) >> 12;
|
|
ch <<= 4;
|
|
if (tmp < 10) {
|
|
Buffer[j] = L'0' + tmp;
|
|
} else {
|
|
Buffer[j] = L'A' + (tmp - 10);
|
|
}
|
|
j++;
|
|
k--;
|
|
if (k == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (k) {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
|
|
if (i != tail - 1) {
|
|
if (j < Size) {
|
|
Buffer[j++] = L':';
|
|
} else {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tail == 8) {
|
|
if (j < Size) {
|
|
Buffer[j] = L'\0';
|
|
return TRUE;
|
|
} else {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (j < Size) {
|
|
Buffer[j++] = L':';
|
|
} else {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
|
|
if (tail + len >= 8) {
|
|
if (j < Size) {
|
|
Buffer[j++] = L':';
|
|
} else {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
for (i = tail + len; i < 8; i++) {
|
|
if (j < Size) {
|
|
Buffer[j++] = L':';
|
|
} else {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
|
|
ch = htons(addr->sin6_addr[i]);
|
|
if (ch) {
|
|
k = 4;
|
|
while (0 == (ch & 0xf000)) {
|
|
ch <<= 4;
|
|
k--;
|
|
}
|
|
} else {
|
|
k = 1;
|
|
}
|
|
while (j < Size) {
|
|
tmp = (ch & 0xf000) >> 12;
|
|
ch <<= 4;
|
|
if (tmp < 10) {
|
|
Buffer[j] = L'0' + tmp;
|
|
} else {
|
|
Buffer[j] = L'A' + (tmp - 10);
|
|
}
|
|
j++;
|
|
k--;
|
|
if (k == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (k) {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (j < Size) {
|
|
Buffer[j] = L'\0';
|
|
return TRUE;
|
|
} else {
|
|
Buffer[Size-1] = L'\0';
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
inet_ntoa6(
|
|
OUT CHAR *Buffer,
|
|
IN DWORD Size,
|
|
IN PSMB_IP6_ADDRESS addr
|
|
)
|
|
{
|
|
DWORD i;
|
|
WCHAR wBuf[40];
|
|
|
|
if (!inet_ntoa6W(wBuf, 40, addr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Don't call Rtl routine to convert
|
|
// Unicode into Oem because we may
|
|
// run at DISPATCH level
|
|
//
|
|
// For this particular case, we can
|
|
// simply do type-cast coping.
|
|
//
|
|
for (i = 0; i < Size; i++) {
|
|
Buffer[i] = (BYTE)(wBuf[i]);
|
|
if (wBuf[i] == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Buffer is too small
|
|
//
|
|
Buffer[Size-1] = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
/*********************************************************************************
|
|
* inet_addr Copy from winsocket
|
|
*********************************************************************************/
|
|
|
|
/*
|
|
* Internet address interpretation routine.
|
|
* All the network library routines call this
|
|
* routine to interpret entries in the data bases
|
|
* which are expected to be an address.
|
|
* The value returned is in network order.
|
|
*/
|
|
unsigned long PASCAL
|
|
inet_addrW(
|
|
IN WCHAR *cp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function interprets the character string specified by the cp
|
|
parameter. This string represents a numeric Internet address
|
|
expressed in the Internet standard ".'' notation. The value
|
|
returned is a number suitable for use as an Internet address. All
|
|
Internet addresses are returned in network order (bytes ordered from
|
|
left to right).
|
|
|
|
Internet Addresses
|
|
|
|
Values specified using the "." notation take one of the following
|
|
forms:
|
|
|
|
a.b.c.d a.b.c a.b a
|
|
|
|
When four parts are specified, each is interpreted as a byte of data
|
|
and assigned, from left to right, to the four bytes of an Internet
|
|
address. Note that when an Internet address is viewed as a 32-bit
|
|
integer quantity on the Intel architecture, the bytes referred to
|
|
above appear as "d.c.b.a''. That is, the bytes on an Intel
|
|
processor are ordered from right to left.
|
|
|
|
Note: The following notations are only used by Berkeley, and nowhere
|
|
else on the Internet. In the interests of compatibility with their
|
|
software, they are supported as specified.
|
|
|
|
When a three part address is specified, the last part is interpreted
|
|
as a 16-bit quantity and placed in the right most two bytes of the
|
|
network address. This makes the three part address format
|
|
convenient for specifying Class B network addresses as
|
|
"128.net.host''.
|
|
|
|
When a two part address is specified, the last part is interpreted
|
|
as a 24-bit quantity and placed in the right most three bytes of the
|
|
network address. This makes the two part address format convenient
|
|
for specifying Class A network addresses as "net.host''.
|
|
|
|
When only one part is given, the value is stored directly in the
|
|
network address without any byte rearrangement.
|
|
|
|
Arguments:
|
|
|
|
cp - A character string representing a number expressed in the
|
|
Internet standard "." notation.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, inet_addr() returns an in_addr structure
|
|
containing a suitable binary representation of the Internet address
|
|
given. Otherwise, it returns the value INADDR_NONE.
|
|
|
|
--*/
|
|
|
|
{
|
|
register unsigned long val, base, n;
|
|
register WCHAR c;
|
|
unsigned long parts[4], *pp = parts;
|
|
|
|
again:
|
|
/*
|
|
* Collect number up to ``.''.
|
|
* Values are specified as for C:
|
|
* 0x=hex, 0=octal, other=decimal.
|
|
*/
|
|
val = 0; base = 10;
|
|
if (*cp == L'0') {
|
|
base = 8, cp++;
|
|
if (*cp == L'x' || *cp == L'X')
|
|
base = 16, cp++;
|
|
}
|
|
|
|
while (c = *cp) {
|
|
if (isdigit(c)) {
|
|
val = (val * base) + (c - L'0');
|
|
cp++;
|
|
continue;
|
|
}
|
|
if (base == 16 && isxdigit(c)) {
|
|
val = (val << 4) + (c + 10 - (islower(c) ? L'a' : L'A'));
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (*cp == L'.') {
|
|
/*
|
|
* Internet format:
|
|
* a.b.c.d
|
|
* a.b.c (with c treated as 16-bits)
|
|
* a.b (with b treated as 24 bits)
|
|
*/
|
|
/* GSS - next line was corrected on 8/5/89, was 'parts + 4' */
|
|
if (pp >= parts + 3) {
|
|
return ((unsigned long) -1);
|
|
}
|
|
*pp++ = val, cp++;
|
|
goto again;
|
|
}
|
|
/*
|
|
* Check for trailing characters.
|
|
*/
|
|
if (*cp && !isspace(*cp)) {
|
|
return (INADDR_NONE);
|
|
}
|
|
*pp++ = val;
|
|
/*
|
|
* Concoct the address according to
|
|
* the number of parts specified.
|
|
*/
|
|
n = (unsigned long)(pp - parts);
|
|
switch ((int) n) {
|
|
|
|
case 1: /* a -- 32 bits */
|
|
val = parts[0];
|
|
break;
|
|
|
|
case 2: /* a.b -- 8.24 bits */
|
|
if ((parts[0] > 0xff) || (parts[1] > 0xffffff)) {
|
|
return(INADDR_NONE);
|
|
}
|
|
val = (parts[0] << 24) | (parts[1] & 0xffffff);
|
|
break;
|
|
|
|
case 3: /* a.b.c -- 8.8.16 bits */
|
|
if ((parts[0] > 0xff) || (parts[1] > 0xff) ||
|
|
(parts[2] > 0xffff)) {
|
|
return(INADDR_NONE);
|
|
}
|
|
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
|
|
(parts[2] & 0xffff);
|
|
break;
|
|
|
|
case 4: /* a.b.c.d -- 8.8.8.8 bits */
|
|
if ((parts[0] > 0xff) || (parts[1] > 0xff) ||
|
|
(parts[2] > 0xff) || (parts[3] > 0xff)) {
|
|
return(INADDR_NONE);
|
|
}
|
|
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
|
|
((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
|
|
break;
|
|
|
|
default:
|
|
return (INADDR_NONE);
|
|
}
|
|
val = htonl(val);
|
|
return (val);
|
|
}
|
|
|