/*++ Copyright (c) 1996-2002 Microsoft Corporation Module Name: straddr.c Abstract: Domain Name System (DNS) Library Routines to string to\from address conversions. Author: Jim Gilroy (jamesg) December 1996 Revision History: jamesg June 2000 New IP6 parsing. jamesg Oct 2000 Created this module. --*/ #include "local.h" #include "ws2tcpip.h" // IP6 inaddr definitions // // String to address // BOOL Dns_Ip6StringToAddress_A( OUT PIP6_ADDRESS pIp6Addr, IN PCSTR pString ) /*++ Routine Description: Convert string to IP6 address. Arguments: pAddress -- ptr to IP6 address to be filled in pString -- string with IP6 address Return Value: TRUE if successful. FALSE otherwise. --*/ { DNS_STATUS status; PCHAR pstringEnd = NULL; DNSDBG( PARSE2, ( "Dns_Ip6StringToAddress_A( %s )\n", pString )); // // convert to IP6 address // status = RtlIpv6StringToAddressA( pString, & pstringEnd, (PIN6_ADDR) pIp6Addr ); return( status == NO_ERROR && *pstringEnd==0 ); } BOOL Dns_Ip6StringToAddressEx_A( OUT PIP6_ADDRESS pIp6Addr, IN PCSTR pchString, IN DWORD dwStringLength ) /*++ Routine Description: Convert string to IP6 address. This version handles non-NULL-terminated strings for DNS server file load. Arguments: pAddress -- ptr to IP6 address to be filled in pchString -- string with IP6 address dwStringLength -- string length Return Value: TRUE if successful. FALSE otherwise. --*/ { CHAR tempBuf[ IP6_ADDRESS_STRING_BUFFER_LENGTH ]; PCSTR pstring; DNSDBG( PARSE2, ( "Dns_Ip6StringToAddressEx_A( %.*s )\n" "\tpchString = %p\n", dwStringLength, pchString, pchString )); // // copy string if given length // if no length assume NULL terminated // pstring = pchString; if ( dwStringLength ) { DWORD bufLength = IP6_ADDRESS_STRING_BUFFER_LENGTH; if ( ! Dns_StringCopy( tempBuf, & bufLength, (PCHAR) pstring, dwStringLength, DnsCharSetAnsi, DnsCharSetAnsi ) ) { return( FALSE ); } pstring = tempBuf; } // convert to IP6 address return Dns_Ip6StringToAddress_A( pIp6Addr, pstring ); } BOOL Dns_Ip6StringToAddress_W( OUT PIP6_ADDRESS pIp6Addr, IN PCWSTR pwString ) /*++ Routine Description: Build IP6 address from wide string. Arguments: pwString -- unicode IP6 string pIp6Addr -- addr to recv IP6 address Return Value: TRUE if successful conversion. FALSE on bad string. --*/ { DNS_STATUS status; PWCHAR pstringEnd = NULL; DNSDBG( PARSE2, ( "Dns_Ip6StringToAddress_W( %S )\n", pwString )); // // convert to IP6 address // status = RtlIpv6StringToAddressW( pwString, & pstringEnd, (PIN6_ADDR) pIp6Addr ); return( status == NO_ERROR && *pstringEnd==0 ); } BOOL Dns_Ip4StringToAddress_A( OUT PIP4_ADDRESS pIp4Addr, IN PCSTR pString ) /*++ Routine Description: Build IP4 address from narrow string. Arguments: pIp4Addr -- addr to recv IP6 address pString -- unicode IP4 string Return Value: TRUE if successful conversion. FALSE on bad string. --*/ { IP4_ADDRESS ip; // if inet_addr() returns error, verify then error out ip = inet_addr( pString ); if ( ip == INADDR_BROADCAST && strcmp( pString, "255.255.255.255" ) != 0 ) { return( FALSE ); } *pIp4Addr = ip; return( TRUE ); } BOOL Dns_Ip4StringToAddressEx_A( OUT PIP4_ADDRESS pIp4Addr, IN PCSTR pchString, IN DWORD dwStringLength ) /*++ Routine Description: Build IP4 address from narrow string. This version handles non-NULL terminated strings Arguments: pIp4Addr -- addr to recv IP6 address pString -- unicode IP4 string dwStringLength -- string length; 0 if NULL terminated Return Value: TRUE if successful conversion. FALSE on bad string. --*/ { CHAR tempBuf[ IP4_ADDRESS_STRING_BUFFER_LENGTH ]; PCSTR pstring; DNSDBG( PARSE2, ( "Dns_Ip4StringToAddressEx_A( %.*s )\n" "\tpchString = %p\n", dwStringLength, pchString, pchString )); // // copy string if given length // if no length assume NULL terminated // pstring = pchString; if ( dwStringLength ) { DWORD bufLength = IP4_ADDRESS_STRING_BUFFER_LENGTH; if ( ! Dns_StringCopy( tempBuf, & bufLength, (PCHAR) pstring, dwStringLength, DnsCharSetAnsi, DnsCharSetAnsi ) ) { return( FALSE ); } pstring = tempBuf; } return Dns_Ip4StringToAddress_A( pIp4Addr, pstring ); } BOOL Dns_Ip4StringToAddress_W( OUT PIP4_ADDRESS pIp4Addr, IN PCWSTR pwString ) /*++ Routine Description: Build IP4 address from wide string. Arguments: pIp4Addr -- addr to recv IP6 address pwString -- unicode IP6 string Return Value: TRUE if successful conversion. FALSE on bad string. --*/ { CHAR bufAddr[ IP4_ADDRESS_STRING_BUFFER_LENGTH ]; DWORD bufLength = IP4_ADDRESS_STRING_BUFFER_LENGTH; // convert to narrow string // - UTF8 quicker and just fine for numeric if ( ! Dns_StringCopy( bufAddr, & bufLength, (PCHAR) pwString, 0, // length unknown DnsCharSetUnicode, DnsCharSetUtf8 ) ) { return( FALSE ); } return Dns_Ip4StringToAddress_A( pIp4Addr, bufAddr ); } // // Combined IP4/IP6 string-to-address // BOOL Dns_StringToDnsAddr_W( OUT PDNS_ADDR pAddr, IN PCWSTR pString ) /*++ Routine Description: Build address (IP4 or IP6) from reverse lookup name. Arguments: pAddr -- DNS_ADDR to receive address pString -- address string Return Value: TRUE if successful. FALSE on error. GetLastError() for status. --*/ { return Dns_StringToDnsAddrEx( pAddr, (PCSTR) pString, 0, // any family TRUE, // unicode FALSE // forward ); } BOOL Dns_StringToDnsAddr_A( OUT PDNS_ADDR pAddr, IN PCSTR pString ) { return Dns_StringToDnsAddrEx( pAddr, pString, 0, // any family FALSE, // not unicode FALSE // forward ); } BOOL Dns_StringToAddress_W( OUT PCHAR pAddrBuf, IN OUT PDWORD pBufLength, IN PCWSTR pString, IN OUT PDWORD pAddrFamily ) /*++ Routine Description: Build address (IP4 or IP6) from address string. Arguments: pAddrBuf -- buffer to receive address pBufLength -- ptr to address length input - length of buffer output - length of address found pString -- address string pAddrFamily -- ptr to address family input - zero for any family or particular family to check output - family found; zero if no conversion Return Value: TRUE if successful. FALSE on error. GetLastError() for status. --*/ { return Dns_StringToAddressEx( pAddrBuf, pBufLength, (PCSTR) pString, pAddrFamily, TRUE, // unicode FALSE // forward ); } BOOL Dns_StringToAddress_A( OUT PCHAR pAddrBuf, IN OUT PDWORD pBufLength, IN PCSTR pString, IN OUT PDWORD pAddrFamily ) { return Dns_StringToAddressEx( pAddrBuf, pBufLength, pString, pAddrFamily, FALSE, // ANSI FALSE // forward ); } // // Address to string // PWCHAR Dns_Ip6AddressToString_W( OUT PWCHAR pwString, IN PIP6_ADDRESS pIp6Addr ) /*++ Routine Description: Convert IP6 address to string format. Arguments: pwString -- buffer to hold string; MUST be at least IPV6_ADDRESS_STRING_LENGTH+1 in length pAddress -- IP6 address to convert to string Return Value: Ptr to next location in buffer (the terminating NULL). --*/ { // DCR: could be macro return RtlIpv6AddressToStringW( (PIN6_ADDR) pIp6Addr, pwString ); } PCHAR Dns_Ip6AddressToString_A( OUT PCHAR pchString, IN PIP6_ADDRESS pIp6Addr ) /*++ Routine Description: Convert IP6 address to string format. Arguments: pchString -- buffer to hold string; MUST be at least IPV6_ADDRESS_STRING_LENGTH+1 in length pAddress -- IP6 address to convert to string Return Value: Ptr to next location in buffer (the terminating NULL). --*/ { // DCR: could be macro return RtlIpv6AddressToStringA( (PIN6_ADDR) pIp6Addr, pchString ); } // // Address to string -- IP4 // PWCHAR Dns_Ip4AddressToString_W( OUT PWCHAR pwString, IN PIP4_ADDRESS pIp4Addr ) /*++ Routine Description: Convert IP4 address to string format. Arguments: pwString -- buffer to hold string; MUST be at least IPV6_ADDRESS_STRING_LENGTH+1 in length pAddress -- IP4 address to convert to string Return Value: Ptr to next location in buffer (the terminating NULL). --*/ { IP4_ADDRESS ip = *pIp4Addr; // // convert IP4 address to string // - address is in net order, lead byte in low memory // pwString += wsprintfW( pwString, L"%u.%u.%u.%u", (UCHAR) (ip & 0x000000ff), (UCHAR) ((ip & 0x0000ff00) >> 8), (UCHAR) ((ip & 0x00ff0000) >> 16), (UCHAR) ((ip & 0xff000000) >> 24) ); return( pwString ); } PCHAR Dns_Ip4AddressToString_A( OUT PCHAR pString, IN PIP4_ADDRESS pIp4Addr ) /*++ Routine Description: Convert IP4 address to string format. Arguments: pchString -- buffer to hold string; MUST be at least IPV6_ADDRESS_STRING_LENGTH+1 in length pAddress -- IP4 address to convert to string Return Value: Ptr to next location in buffer (the terminating NULL). --*/ { IP4_ADDRESS ip = *pIp4Addr; // // convert IP4 address to string // - address is in net order, lead byte in low memory // pString += sprintf( pString, "%u.%u.%u.%u", (UCHAR) (ip & 0x000000ff), (UCHAR) ((ip & 0x0000ff00) >> 8), (UCHAR) ((ip & 0x00ff0000) >> 16), (UCHAR) ((ip & 0xff000000) >> 24) ); return( pString ); } // // Address-to-string -- combined IP4/6 // PCHAR Dns_AddressToString_A( OUT PCHAR pchString, IN OUT PDWORD pStringLength, IN PBYTE pAddr, IN DWORD AddrLength, IN DWORD AddrFamily ) /*++ Routine Description: Convert address to string format. Arguments: pchString -- buffer to hold string; MUST be at least IPV6_ADDRESS_STRING_LENGTH+1 in length pStringLength -- string buffer length pAddr -- ptr to address AddrLength -- address length AddrFamily -- address family (AF_INET, AF_INET6) Return Value: Ptr to next location in buffer (the terminating NULL). NULL if no conversion. --*/ { DWORD length = *pStringLength; // dispatch to conversion routine for this type if ( AddrFamily == AF_INET ) { if ( length < IP4_ADDRESS_STRING_LENGTH+1 ) { length = IP4_ADDRESS_STRING_LENGTH+1; goto Failed; } return Dns_Ip4AddressToString_A( pchString, (PIP4_ADDRESS) pAddr ); } if ( AddrFamily == AF_INET6 ) { if ( length < IP6_ADDRESS_STRING_LENGTH+1 ) { length = IP6_ADDRESS_STRING_LENGTH+1; goto Failed; } return Dns_Ip6AddressToString_A( pchString, (PIP6_ADDRESS) pAddr ); } Failed: *pStringLength = length; return NULL; } // // DNS_ADDR-to-string // PCHAR Dns_DnsAddrToString_A( OUT PCHAR pBuffer, IN OUT PDWORD pBufLength, IN PDNS_ADDR pAddr ) /*++ Routine Description: Convert address to string format. Arguments: pBuffer -- buffer to hold string; if pBufLength not given, then MUST be at least IPV6_ADDRESS_STRING_LENGTH+1 in length pBufLength -- ptr to string buffer length pAddr -- ptr to address Return Value: Ptr to next location in buffer (the terminating NULL). NULL if no conversion. --*/ { DNS_STATUS status; // // DCR: handle no buffer length given -- handles IP6 default? // // // print support for IP4/IP6 // if ( DnsAddr_IsIp4(pAddr) ) { status = RtlIpv4AddressToStringExA( &pAddr->SockaddrIn.sin_addr, pAddr->SockaddrIn.sin_port, pBuffer, pBufLength ); } else if ( DnsAddr_IsIp6(pAddr) ) { status = RtlIpv6AddressToStringExA( &pAddr->SockaddrIn6.sin6_addr, pAddr->SockaddrIn6.sin6_scope_id, pAddr->SockaddrIn6.sin6_port, pBuffer, pBufLength ); } else { status = ERROR_INVALID_PARAMETER; if ( !pBufLength || *pBufLength > 40 ) { sprintf( pBuffer, "Invalid DNS_ADDR at %p", pAddr ); } pBuffer = NULL; } // // return ptr to next char in buffer // if ( status == NO_ERROR ) { pBuffer += *pBufLength; } else { pBuffer = NULL; } return pBuffer; } PWCHAR Dns_DnsAddrToString_W( OUT PWCHAR pBuffer, IN PDWORD pBufLength, IN PDNS_ADDR pAddr ) { DNS_STATUS status; // // print support for IP4/IP6 // if ( DnsAddr_IsIp4(pAddr) ) { status = RtlIpv4AddressToStringExW( &pAddr->SockaddrIn.sin_addr, pAddr->SockaddrIn.sin_port, pBuffer, pBufLength ); } else if ( DnsAddr_IsIp6(pAddr) ) { status = RtlIpv6AddressToStringExW( &pAddr->SockaddrIn6.sin6_addr, pAddr->SockaddrIn6.sin6_scope_id, pAddr->SockaddrIn6.sin6_port, pBuffer, pBufLength ); } else { status = ERROR_INVALID_PARAMETER; if ( !pBufLength || *pBufLength > 40 ) { wsprintfW( pBuffer, L"Invalid DNS_ADDR at %p", pAddr ); } pBuffer = NULL; } // // return ptr to next char in buffer // if ( status == NO_ERROR ) { pBuffer += *pBufLength; } else { pBuffer = NULL; } return pBuffer; } // // Reverse lookup address-to-name IP4 // PCHAR Dns_Ip4AddressToReverseName_A( OUT PCHAR pBuffer, IN IP4_ADDRESS IpAddress ) /*++ Routine Description: Write reverse lookup name, given corresponding IP Arguments: pBuffer -- ptr to buffer for reverse lookup name; MUST contain at least DNS_MAX_REVERSE_NAME_BUFFER_LENGTH bytes IpAddress -- IP address to create Return Value: Ptr to next location in buffer. --*/ { DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseName_A()\n" )); // // write digits for each octect in IP address // - note, it is in net order so lowest octect, is in highest memory // pBuffer += sprintf( pBuffer, "%u.%u.%u.%u.in-addr.arpa.", (UCHAR) ((IpAddress & 0xff000000) >> 24), (UCHAR) ((IpAddress & 0x00ff0000) >> 16), (UCHAR) ((IpAddress & 0x0000ff00) >> 8), (UCHAR) (IpAddress & 0x000000ff) ); return( pBuffer ); } PWCHAR Dns_Ip4AddressToReverseName_W( OUT PWCHAR pBuffer, IN IP4_ADDRESS IpAddress ) /*++ Routine Description: Write reverse lookup name, given corresponding IP Arguments: pBuffer -- ptr to buffer for reverse lookup name; MUST contain at least DNS_MAX_REVERSE_NAME_BUFFER_LENGTH wide chars IpAddress -- IP address to create Return Value: Ptr to next location in buffer. --*/ { DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseName_W()\n" )); // // write digits for each octect in IP address // - note, it is in net order so lowest octect, is in highest memory // pBuffer += wsprintfW( pBuffer, L"%u.%u.%u.%u.in-addr.arpa.", (UCHAR) ((IpAddress & 0xff000000) >> 24), (UCHAR) ((IpAddress & 0x00ff0000) >> 16), (UCHAR) ((IpAddress & 0x0000ff00) >> 8), (UCHAR) (IpAddress & 0x000000ff) ); return( pBuffer ); } PCHAR Dns_Ip4AddressToReverseNameAlloc_A( IN IP4_ADDRESS IpAddress ) /*++ Routine Description: Create reverse lookup name string, given corresponding IP. Caller must free the string. Arguments: IpAddress -- IP address to create Return Value: Ptr to new reverse lookup string. --*/ { PCHAR pch; PCHAR pchend; DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseNameAlloc_A()\n" )); // // allocate space for string // pch = ALLOCATE_HEAP( DNS_MAX_REVERSE_NAME_BUFFER_LENGTH ); if ( !pch ) { return( NULL ); } // // write string for IP // pchend = Dns_Ip4AddressToReverseName_A( pch, IpAddress ); if ( !pchend ) { FREE_HEAP( pch ); return( NULL ); } return( pch ); } PWCHAR Dns_Ip4AddressToReverseNameAlloc_W( IN IP4_ADDRESS IpAddress ) /*++ Routine Description: Create reverse lookup name string, given corresponding IP. Caller must free the string. Arguments: IpAddress -- IP address to create Return Value: Ptr to new reverse lookup string. --*/ { PWCHAR pch; PWCHAR pchend; DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseNameAlloc_W()\n" )); // // allocate space for string // pch = ALLOCATE_HEAP( DNS_MAX_REVERSE_NAME_BUFFER_LENGTH * sizeof(WCHAR) ); if ( !pch ) { return( NULL ); } // // write string for IP // pchend = Dns_Ip4AddressToReverseName_W( pch, IpAddress ); if ( !pchend ) { FREE_HEAP( pch ); return( NULL ); } return( pch ); } // // Reverse lookup address-to-name -- IP6 // PCHAR Dns_Ip6AddressToReverseName_A( OUT PCHAR pBuffer, IN IP6_ADDRESS Ip6Addr ) /*++ Routine Description: Write reverse lookup name, given corresponding IP6 address Arguments: pBuffer -- ptr to buffer for reverse lookup name; MUST contain at least DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH bytes Ip6Addr -- IP6 address to create reverse string for Return Value: Ptr to next location in buffer. --*/ { DWORD i; DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseName_A()\n" )); // // write digit for each nibble in IP6 address // // note we are reversing net order here // since address is in net order and we are filling // in least to most significant order // - go DOWN through DWORDS // - go DOWN through the BYTES // - but we must put the lowest (least significant) nibble // first as our bits are not in "bit net order" // which is sending the highest bit in the byte first // #if 0 i = 4; while ( i-- ) { DWORD thisDword = Ip6Address.IP6Dword[i]; pBuffer += sprintf( pBuffer, "%u.%u.%u.%u.%u.%u.%u.%u.", (thisDword & 0x0f000000) >> 24, (thisDword & 0xf0000000) >> 28, (thisDword & 0x000f0000) >> 16, (thisDword & 0x00f00000) >> 20, (thisDword & 0x00000f00) >> 8, (thisDword & 0x0000f000) >> 12, (thisDword & 0x0000000f) , (thisDword & 0x000000f0) >> 4 ); } #endif i = 16; while ( i-- ) { BYTE thisByte = Ip6Addr.IP6Byte[i]; pBuffer += sprintf( pBuffer, "%x.%x.", (thisByte & 0x0f), (thisByte & 0xf0) >> 4 ); } pBuffer += sprintf( pBuffer, DNS_IP6_REVERSE_DOMAIN_STRING ); return( pBuffer ); } PWCHAR Dns_Ip6AddressToReverseName_W( OUT PWCHAR pBuffer, IN IP6_ADDRESS Ip6Addr ) /*++ Routine Description: Write reverse lookup name, given corresponding IP6 address Arguments: pBuffer -- ptr to buffer for reverse lookup name; MUST contain at least DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH wide chars Ip6Addr -- IP6 address to create reverse string for Return Value: Ptr to next location in buffer. --*/ { DWORD i; DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseName_W()\n" )); // // write digit for each nibble in IP6 address // - in net order so lowest nibble is in highest memory // i = 16; while ( i-- ) { BYTE thisByte = Ip6Addr.IP6Byte[i]; pBuffer += wsprintfW( pBuffer, L"%x.%x.", (thisByte & 0x0f), (thisByte & 0xf0) >> 4 ); } pBuffer += wsprintfW( pBuffer, DNS_IP6_REVERSE_DOMAIN_STRING_W ); return( pBuffer ); } PCHAR Dns_Ip6AddressToReverseNameAlloc_A( IN IP6_ADDRESS Ip6Addr ) /*++ Routine Description: Create reverse lookup name given corresponding IP. Caller must free the string. Arguments: Ip6Addr -- IP6 address to create reverse name for Return Value: Ptr to new reverse lookup name string. --*/ { PCHAR pch; PCHAR pchend; DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseNameAlloc_A()\n" )); // // allocate space for string // pch = ALLOCATE_HEAP( DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH ); if ( !pch ) { return( NULL ); } // // write string for IP // pchend = Dns_Ip6AddressToReverseName_A( pch, Ip6Addr ); if ( !pchend ) { FREE_HEAP( pch ); return( NULL ); } return( pch ); } PWCHAR Dns_Ip6AddressToReverseNameAlloc_W( IN IP6_ADDRESS Ip6Addr ) /*++ Routine Description: Create reverse lookup name given corresponding IP. Caller must free the string. Arguments: Ip6Addr -- IP6 address to create reverse name for Return Value: Ptr to new reverse lookup name string. --*/ { PWCHAR pch; PWCHAR pchend; DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseNameAlloc_W()\n" )); // // allocate space for string // pch = (PWCHAR) ALLOCATE_HEAP( DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH * sizeof(WCHAR) ); if ( !pch ) { return( NULL ); } // // write string for IP // pchend = Dns_Ip6AddressToReverseName_W( pch, Ip6Addr ); if ( !pchend ) { FREE_HEAP( pch ); return( NULL ); } return( pch ); } // // Reverse name-to-address -- IP4 // BOOL Dns_Ip4ReverseNameToAddress_A( OUT PIP4_ADDRESS pIp4Addr, IN PCSTR pszName ) /*++ Routine Description: Get IP for reverse lookup name. Arguments: pIp4Addr -- addr to receive IP address if found pszName -- name to lookup Return Value: TRUE -- if reverse lookup name converted to IP FALSE -- if not IP4 reverse lookup name --*/ { #define SIZE_IP4REV (sizeof(".in-addr.arpa")-1) CHAR nameBuffer[ DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH+1 ]; DWORD nameLength; IP4_ADDRESS ip; PCHAR pch; DWORD i; DWORD byte; DNSDBG( TRACE, ( "Dns_Ip4ReverseNameToAddress_A( %s )\n", pszName )); // // validate name // fail if // - too long // - too short // - not in in-addr.arpa domain // nameLength = strlen( pszName ); if ( nameLength > DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH ) { return( FALSE ); } if ( pszName[nameLength-1] == '.' ) { nameLength--; } if ( nameLength <= SIZE_IP4REV ) { return( FALSE ); } nameLength -= SIZE_IP4REV; if ( _strnicmp( ".in-addr.arpa", &pszName[nameLength], SIZE_IP4REV ) != 0 ) { return( FALSE ); } // // copy reverse dotted decimal piece of name // RtlCopyMemory( nameBuffer, pszName, nameLength ); nameBuffer[nameLength] = 0; // // read digits // ip = 0; i = 0; pch = nameBuffer + nameLength; while ( 1 ) { --pch; if ( *pch == '.' ) { *pch = 0; pch++; } else if ( pch == nameBuffer ) { } else { continue; } // convert byte byte = strtoul( pch, NULL, 10 ); if ( byte > 255 ) { return( FALSE ); } if ( i > 3 ) { return( FALSE ); } ip |= byte << (8*i); // terminate at string beginning // or continue back up string if ( pch == nameBuffer ) { break; } i++; pch--; } *pIp4Addr = ip; DNSDBG( TRACE, ( "Success on Dns_Ip4ReverseNameToAddress_A( %s ) => %s\n", pszName, IP4_STRING(ip) )); return( TRUE ); } BOOL Dns_Ip4ReverseNameToAddress_W( OUT PIP4_ADDRESS pIp4Addr, IN PCWSTR pwsName ) /*++ Routine Description: Get IP for reverse lookup name. Arguments: pIp4Addr -- addr to receive IP address if found pszName -- name to lookup Return Value: TRUE -- if reverse lookup name converted to IP FALSE -- if not IP4 reverse lookup name --*/ { CHAR nameBuffer[ DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH+1 ]; DWORD bufLength; DWORD nameLengthUtf8; DNSDBG( TRACE, ( "Dns_Ip4ReverseNameToAddress_W( %S )\n", pwsName )); // // convert to UTF8 // - use UTF8 since conversion to it is trivial and it // is identical to ANSI for all reverse lookup names // bufLength = DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH + 1; nameLengthUtf8 = Dns_StringCopy( nameBuffer, & bufLength, (PCHAR) pwsName, 0, // NULL terminated DnsCharSetUnicode, DnsCharSetUtf8 ); if ( nameLengthUtf8 == 0 ) { return FALSE; } // // call ANSI routine to do conversion // return Dns_Ip4ReverseNameToAddress_A( pIp4Addr, (PCSTR) nameBuffer ); } // // Reverse name-to-address -- IP6 // BOOL Dns_Ip6ReverseNameToAddress_A( OUT PIP6_ADDRESS pIp6Addr, IN PCSTR pszName ) /*++ Routine Description: Get IP6 address for reverse lookup name. Arguments: pIp6Addr -- addr to receive IP address if found pszName -- name to lookup Return Value: TRUE -- if reverse lookup name converted to IP FALSE -- if not IP4 reverse lookup name --*/ { #define SIZE_IP6REV (sizeof(".ip6.arpa")-1) CHAR nameBuffer[ DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH+1 ]; DWORD nameLength; PCHAR pch; BYTE byteArray[16]; DWORD byteCount; DWORD nibble; DWORD highNibble; BOOL fisLow; DNSDBG( TRACE, ( "Dns_Ip6ReverseNameToAddress_A()\n" )); // // validate name // fail if // - too long // - too short // - not in in6.int domain // nameLength = strlen( pszName ); if ( nameLength > DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH ) { return( FALSE ); } if ( pszName[nameLength-1] == '.' ) { nameLength--; } if ( nameLength <= SIZE_IP6REV ) { return( FALSE ); } nameLength -= SIZE_IP6REV; if ( _strnicmp( ".ip6.arpa", &pszName[nameLength], SIZE_IP6REV ) != 0 ) { return( FALSE ); } // // copy name // RtlCopyMemory( nameBuffer, pszName, nameLength ); nameBuffer[nameLength] = 0; // // clear IP6 address // - need for partial reverse lookup name // RtlZeroMemory( byteArray, sizeof(byteArray) ); // // read digits // byteCount = 0; fisLow = FALSE; pch = nameBuffer + nameLength; while ( 1 ) { if ( byteCount > 15 ) { return( FALSE ); } --pch; if ( *pch == '.' ) { *pch = 0; pch++; } else if ( pch == nameBuffer ) { } else { // DCR: multi-digit nibbles in reverse name -- error? continue; } // convert nibble // - zero test special as // A) faster // B) strtoul() uses for error case if ( *pch == '0' ) { nibble = 0; } else { nibble = strtoul( pch, NULL, 16 ); if ( nibble == 0 || nibble > 15 ) { return( FALSE ); } } // save high nibble // on low nibble, write byte to IP6 address if ( !fisLow ) { highNibble = nibble; fisLow = TRUE; } else { //byteArray[byteCount++] = (BYTE) (lowNibble | (nibble << 4)); pIp6Addr->IP6Byte[byteCount++] = (BYTE) ( (highNibble<<4) | nibble ); fisLow = FALSE; } // terminate at string beginning // or continue back up string if ( pch == nameBuffer ) { break; } pch--; } //*pIp6Addr = *(PIP6_ADDRESS) byteArray; DNSDBG( TRACE, ( "Success on Dns_Ip6ReverseNameToAddress_A( %s )\n", pszName )); return( TRUE ); } BOOL Dns_Ip6ReverseNameToAddress_W( OUT PIP6_ADDRESS pIp6Addr, IN PCWSTR pwsName ) /*++ Routine Description: Get IP for reverse lookup name. Arguments: pIp6Addr -- addr to receive IP address if found pszName -- name to lookup Return Value: TRUE -- if reverse lookup name converted to IP FALSE -- if not IP6 reverse lookup name --*/ { CHAR nameBuffer[ DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH+1 ]; DWORD bufLength; DWORD nameLengthUtf8; DNSDBG( TRACE, ( "Dns_Ip6ReverseNameToAddress_W( %S )\n", pwsName )); // // convert to UTF8 // - use UTF8 since conversion to it is trivial and it // is identical to ANSI for all reverse lookup names // bufLength = DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH + 1; nameLengthUtf8 = Dns_StringCopy( nameBuffer, & bufLength, (PCHAR) pwsName, 0, // NULL terminated DnsCharSetUnicode, DnsCharSetUtf8 ); if ( nameLengthUtf8 == 0 ) { return FALSE; } // // call ANSI routine to do conversion // return Dns_Ip6ReverseNameToAddress_A( pIp6Addr, (PCSTR) nameBuffer ); } // // Combined IP4/IP6 reverse-name-to-address // BOOL Dns_ReverseNameToDnsAddr_W( OUT PDNS_ADDR pAddr, IN PCWSTR pString ) /*++ Routine Description: Build address (IP4 or IP6) from reverse lookup name. Arguments: pAddr -- DNS_ADDR to receive address pString -- address string Return Value: TRUE if successful. FALSE on error. GetLastError() for status. --*/ { return Dns_StringToDnsAddrEx( pAddr, (PCSTR) pString, 0, // any family TRUE, // unicode TRUE // reverse ); } BOOL Dns_ReverseNameToDnsAddr_A( OUT PDNS_ADDR pAddr, IN PCSTR pString ) { return Dns_StringToDnsAddrEx( pAddr, pString, 0, // any family FALSE, // not unicode TRUE // reverse ); } // // Could discontinue these and use DnsAddr // version as NameToAddress_X version // BOOL Dns_ReverseNameToAddress_W( OUT PCHAR pAddrBuf, IN OUT PDWORD pBufLength, IN PCWSTR pString, IN OUT PDWORD pAddrFamily ) /*++ Routine Description: Build address (IP4 or IP6) from reverse lookup name. Arguments: pAddrBuf -- buffer to receive address pBufLength -- ptr to address length input - length of buffer output - length of address found pString -- address string pAddrFamily -- ptr to address family input - zero for any family or particular family to check output - family found; zero if no conversion Return Value: TRUE if successful. FALSE on error. GetLastError() for status. --*/ { return Dns_StringToAddressEx( pAddrBuf, pBufLength, (PCSTR) pString, pAddrFamily, TRUE, // unicode TRUE // reverse ); } BOOL Dns_ReverseNameToAddress_A( OUT PCHAR pAddrBuf, IN OUT PDWORD pBufLength, IN PCSTR pString, IN OUT PDWORD pAddrFamily ) { return Dns_StringToAddressEx( pAddrBuf, pBufLength, pString, pAddrFamily, FALSE, // ANSI TRUE // reverse ); } // // Combined string-to-address private workhorse // BOOL Dns_StringToAddressEx( OUT PCHAR pAddrBuf, IN OUT PDWORD pBufLength, IN PCSTR pString, IN OUT PDWORD pAddrFamily, IN BOOL fUnicode, IN BOOL fReverse ) /*++ Routine Description: Build address (IP4 or IP6 from string) This routine is capable of all string-to-address conversions and is the backbone of all the combined string-to-address conversion routines. Arguments: pAddrBuf -- buffer to receive address pBufLength -- ptr to address length input - length of buffer output - length of address found pString -- address string pAddrFamily -- ptr to address family input - zero for any family or particular family to check output - family found; zero if no conversion fUnicode -- unicode string fReverse -- reverse lookup string Return Value: TRUE if successful. FALSE on error. --*/ { DNS_STATUS status = NO_ERROR; DWORD length = 0; INT family = *pAddrFamily; DWORD bufLength = *pBufLength; BOOL fconvert; PCSTR preverseString; CHAR nameBuffer[ DNS_MAX_REVERSE_NAME_BUFFER_LENGTH+1 ]; DNSDBG( TRACE, ( "Dns_StringToAddressEx( %S%s )\n", fUnicode ? pString : "", fUnicode ? "" : pString )); // // convert reverse to ANSI // // reverse lookups are done in ANSI; convert here to avoid // double string conversion to check both IP4 and IP6 // if ( fReverse ) { preverseString = pString; if ( fUnicode ) { DWORD reverseLength = DNS_MAX_REVERSE_NAME_BUFFER_LENGTH; if ( ! Dns_StringCopy( nameBuffer, & reverseLength, (PCHAR) pString, 0, // NULL terminated DnsCharSetUnicode, DnsCharSetUtf8 ) ) { return FALSE; } preverseString = nameBuffer; } } // // check IP4 // if ( family == 0 || family == AF_INET ) { IP4_ADDRESS ip; if ( fReverse ) { fconvert = Dns_Ip4ReverseNameToAddress_A( & ip, preverseString ); } else { if ( fUnicode ) { fconvert = Dns_Ip4StringToAddress_W( & ip, (PCWSTR)pString ); } else { fconvert = Dns_Ip4StringToAddress_A( & ip, pString ); } } if ( fconvert ) { length = sizeof(IP4_ADDRESS); family = AF_INET; if ( bufLength < length ) { status = ERROR_MORE_DATA; } else { * (PIP4_ADDRESS) pAddrBuf = ip; } DNSDBG( INIT2, ( "Converted string to IP4 address %s\n", IP4_STRING(ip) )); goto Done; } } // // check IP6 // if ( family == 0 || family == AF_INET6 ) { IP6_ADDRESS ip; if ( fReverse ) { fconvert = Dns_Ip6ReverseNameToAddress_A( & ip, preverseString ); } else { if ( fUnicode ) { fconvert = Dns_Ip6StringToAddress_W( & ip, (PCWSTR)pString ); } else { fconvert = Dns_Ip6StringToAddress_A( & ip, pString ); } } if ( fconvert ) { length = sizeof(IP6_ADDRESS); if ( bufLength < length ) { status = ERROR_MORE_DATA; } else { family = AF_INET6; * (PIP6_ADDRESS) pAddrBuf = ip; } IF_DNSDBG( INIT2 ) { DnsDbg_Ip6Address( "Converted string to IP6 address: ", (PIP6_ADDRESS) pAddrBuf, "\n" ); } goto Done; } } length = 0; family = 0; status = DNS_ERROR_INVALID_IP_ADDRESS; Done: if ( status ) { SetLastError( status ); } *pAddrFamily = family; *pBufLength = length; DNSDBG( TRACE, ( "Leave Dns_StringToAddressEx()\n" "\tstatus = %d\n" "\tptr = %p\n" "\tlength = %d\n" "\tfamily = %d\n", status, pAddrBuf, length, family )); return( status==ERROR_SUCCESS ); } BOOL Dns_StringToDnsAddrEx( OUT PDNS_ADDR pAddr, IN PCSTR pString, IN DWORD Family, OPTIONAL IN BOOL fUnicode, IN BOOL fReverse ) /*++ Routine Description: Build address (IP4 or IP6 from string) This routine is capable of all string-to-address conversions and is the backbone of all the combined string-to-address conversion routines. Arguments: pAddr -- ptr to DNS_ADDR to receive address pString -- address string Family -- if only accepting specific family; zero for any fUnicode -- unicode string fReverse -- reverse lookup string Return Value: TRUE if successful. FALSE on error. --*/ { DNS_STATUS status = NO_ERROR; DWORD length = 0; BOOL fconvert; PCSTR preverseString; CHAR nameBuffer[ DNS_MAX_REVERSE_NAME_BUFFER_LENGTH+1 ]; DNSDBG( TRACE, ( "Dns_StringToDnsAddrEx( %S%s, rev=%d, unicode=%d )\n", fUnicode ? pString : "", fUnicode ? "" : pString, fReverse, fUnicode )); // // convert reverse to ANSI // // reverse lookups are done in ANSI; convert here to avoid // double string conversion to check both IP4 and IP6 // if ( fReverse ) { preverseString = pString; if ( fUnicode ) { DWORD bufLength = DNS_MAX_REVERSE_NAME_BUFFER_LENGTH; if ( ! Dns_StringCopy( nameBuffer, & bufLength, (PCHAR) pString, 0, // NULL terminated DnsCharSetUnicode, DnsCharSetUtf8 ) ) { return FALSE; } preverseString = nameBuffer; } } // // check IP4 // if ( Family == 0 || Family == AF_INET ) { IP4_ADDRESS ip; if ( fReverse ) { fconvert = Dns_Ip4ReverseNameToAddress_A( & ip, preverseString ); } else { if ( fUnicode ) { fconvert = Dns_Ip4StringToAddress_W( & ip, (PCWSTR)pString ); } else { fconvert = Dns_Ip4StringToAddress_A( & ip, pString ); } } if ( fconvert ) { DnsAddr_BuildFromIp4( pAddr, ip, 0 // no port ); DNSDBG( INIT2, ( "Converted string to IP4 address %s\n", IP4_STRING(ip) )); goto Done; } } // // check IP6 // if ( Family == 0 || Family == AF_INET6 ) { if ( fReverse ) { IP6_ADDRESS ip; fconvert = Dns_Ip6ReverseNameToAddress_A( & ip, preverseString ); if ( fconvert ) { DnsAddr_BuildFromIp6( pAddr, &ip, 0, // no scope 0 // no port ); IF_DNSDBG( INIT2 ) { DnsDbg_Ip6Address( "Converted string to IP6 address: ", (PIP6_ADDRESS) &ip, "\n" ); } goto Done; } } else { DnsAddr_Clear( pAddr ); if ( fUnicode ) { status = RtlIpv6StringToAddressExW( (PCWSTR) pString, & pAddr->SockaddrIn6.sin6_addr, & pAddr->SockaddrIn6.sin6_scope_id, & pAddr->SockaddrIn6.sin6_port ); } else { status = RtlIpv6StringToAddressExA( (PCSTR) pString, & pAddr->SockaddrIn6.sin6_addr, & pAddr->SockaddrIn6.sin6_scope_id, & pAddr->SockaddrIn6.sin6_port ); } if ( status == NO_ERROR ) { pAddr->SockaddrIn6.sin6_family = AF_INET6; pAddr->SockaddrLength = sizeof(SOCKADDR_IN6); goto Done; } } } DnsAddr_Clear( pAddr ); status = DNS_ERROR_INVALID_IP_ADDRESS; Done: if ( status ) { SetLastError( status ); } DNSDBG( TRACE, ( "Leave Dns_StringToDnsAddrEx()\n" "\tstatus = %d\n", status )); return( status==ERROR_SUCCESS ); } // // UPNP IP6 literal hack // WCHAR g_Ip6LiteralDomain[] = L".ipv6-literal.net"; DWORD g_Ip6LiteralDomainSize = sizeof(g_Ip6LiteralDomain); VOID Dns_Ip6AddressToLiteralName( OUT PWCHAR pBuffer, IN PIP6_ADDRESS pIp6 ) /*++ Routine Description: Write UPNP-hack-literal. Arguments: pBuffer -- ptr to buffer for reverse lookup name; MUST contain DNS_MAX_NAME_LENGTH characters. pIp6 -- IP6 address to create reverse string for Return Value: None --*/ { DWORD length; DNSDBG( TRACE, ( "Dns_Ip6AddressToLiteralName()\n" )); // // convert IP6 to address // Dns_Ip6AddressToString_W( pBuffer, pIp6 ); length = wcslen( pBuffer ); // // replace all colons with dashes // String_ReplaceCharW( pBuffer, L':', L'-' ); // // write ip6 literal domain immediately after // wcscpy( pBuffer + length, g_Ip6LiteralDomain ); DNSDBG( TRACE, ( "Leave Dns_Ip6AddressToLiteralName() => %S\n", pBuffer )); } BOOL Dns_Ip6LiteralNameToAddress( OUT PSOCKADDR_IN6 pSockAddr, IN PCWSTR pwsString ) /*++ Routine Description: IP6 literal to IP6 sockaddr. Arguments: pSock6Addr -- address to fill with IP6 corresponding to literal pwsString -- literal string Return Value: TRUE if IP6 literal found and convert. FALSE if not IP6 literal. --*/ { WCHAR nameBuf[ DNS_MAX_NAME_LENGTH ]; DWORD iter; DWORD length; DWORD size; PWCHAR pch; PWCHAR pdomain; DNS_STATUS status; DNSDBG( TRACE, ( "Dns_Ip6LiteralNameToAddress( %S )\n", pwsString )); // // test for literal // - test undotted // - test as FQDN // note that even FQDN test is safe, as we insist // that string size is GREATER than literal size // length = wcslen( pwsString ); size = (length+1) * sizeof(WCHAR); if ( size <= g_Ip6LiteralDomainSize ) { DNSDBG( INIT2, ( "Stopped UPNP parse -- short string.\n" )); return FALSE; } pdomain = (PWSTR) ((PBYTE)pwsString + size - g_Ip6LiteralDomainSize); if ( ! RtlEqualMemory( pdomain, g_Ip6LiteralDomain, g_Ip6LiteralDomainSize-sizeof(WCHAR) ) ) { pdomain--; if ( pwsString[length-1] != L'.' || ! RtlEqualMemory( pdomain, g_Ip6LiteralDomain, g_Ip6LiteralDomainSize-sizeof(WCHAR) ) ) { DNSDBG( INIT2, ( "Stopped UPNP parse -- no tag match.\n" )); return FALSE; } } // // copy literal to buffer // if ( length >= DNS_MAX_NAME_LENGTH ) { DNSDBG( INIT2, ( "Stopped UPNP parse -- big string.\n" )); return FALSE; } length = (DWORD) ((PWSTR)pdomain - pwsString); RtlCopyMemory( nameBuf, pwsString, length*sizeof(WCHAR) ); nameBuf[ length ] = 0; // // replace dashes with colons // replace 's' with % for scope // String_ReplaceCharW( nameBuf, L'-', L':' ); String_ReplaceCharW( nameBuf, L's', L'%' ); DNSDBG( INIT2, ( "Reconverted IP6 literal %S\n", nameBuf )); // // convert to IP6 address // status = RtlIpv6StringToAddressExW( (PCWSTR) nameBuf, & pSockAddr->sin6_addr, & pSockAddr->sin6_scope_id, & pSockAddr->sin6_port ); if ( status == NO_ERROR ) { if ( IN6_IS_ADDR_LINKLOCAL( &pSockAddr->sin6_addr ) || IN6_IS_ADDR_SITELOCAL( &pSockAddr->sin6_addr ) ) { pSockAddr->sin6_flowinfo = 0; pSockAddr->sin6_family = AF_INET6; } else { status = ERROR_INVALID_PARAMETER; } } return( status == NO_ERROR ); } // // End straddr.c //