#include <stdio.h>
#include <string.h>
#include "ipv6text.h"
#ifdef _WIN32
#ifndef snprintf
#define snprintf _snprintf
#endif
#endif
void IPv6IPToString( char *pszOutText, const unsigned char *ip )
{
int idxLongestRunStart = -1;
int nLongestRun = 1; int nCurrentRun = 0;
int idxQuad;
for ( idxQuad = 0 ; idxQuad < 8 ; ++idxQuad )
{
if ( ip[idxQuad*2] || ip[idxQuad*2 + 1] )
{
nCurrentRun = 0;
}
else
{
++nCurrentRun;
if ( nCurrentRun > nLongestRun )
{
nLongestRun = nCurrentRun;
idxLongestRunStart = idxQuad - nCurrentRun + 1;
}
}
}
char *p = pszOutText;
idxQuad = 0;
bool bNeedColon = false;
while ( idxQuad < 8 )
{
if ( idxQuad == idxLongestRunStart )
{
*(p++) = ':';
*(p++) = ':';
bNeedColon = false;
idxQuad += nLongestRun;
}
else
{
if ( bNeedColon )
*(p++) = ':';
bNeedColon = true;
unsigned quad = ( (unsigned)ip[idxQuad*2] << 8U ) | ip[idxQuad*2 + 1];
static const char hexdigits[] = "0123456789abcdef";
if ( quad >= 0x0010 )
{
if ( quad >= 0x0100 )
{
if ( quad >= 0x1000 )
*(p++) = hexdigits[ quad >> 12U ];
*(p++) = hexdigits[ ( quad >> 8U ) & 0xf ];
}
*(p++) = hexdigits[ ( quad >> 4U ) & 0xf ];
}
*(p++) = hexdigits[ quad & 0xf ];
++idxQuad;
}
}
*p = '\0';
}
void IPv6AddrToString( char *pszOutText, const unsigned char *ip, uint16_t port, uint32_t scope )
{
char *p = pszOutText;
*(p++) = '[';
IPv6IPToString( p, ip );
while (*p)
++p;
if ( scope )
{
snprintf( p, 12, "%%%d", scope );
while (*p)
++p;
}
snprintf( p, 8, "]:%u", (unsigned int)port );
}
static inline int ParseIPv6Addr_HexDigitVal( char c )
{
if ( c >= '0' && c <= '9' ) return c - '0';
if ( c >= 'a' && c <= 'f' ) return c - ('a' - 0xa);
if ( c >= 'A' && c <= 'F' ) return c - ('A' - 0xa);
return -1;
}
static inline int ParseIPv6Addr_DecimalDigitVal( char c )
{
if ( c >= '0' && c <= '9' ) return c - '0';
return -1;
}
bool ParseIPv6Addr_IsSpace( char c )
{
return c == ' ' || c == '\t';
}
bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, uint32_t *pOutScope )
{
while ( ParseIPv6Addr_IsSpace( *pszText ) )
++pszText;
const char *s = pszText;
if ( *s == '[' )
{
++s;
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
}
bool bQuadMustFollow = true;
unsigned char *d = pOutIP;
unsigned char *pZeroFill = NULL;
unsigned char *pEndIP = pOutIP + 16;
if ( s[0] == ':' && s[1] == ':' )
{
pZeroFill = d;
s += 2;
bQuadMustFollow = false;
}
for (;;)
{
int quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit < 0 )
{
if ( bQuadMustFollow )
return false;
break;
}
if ( d >= pEndIP )
return false;
const char *pszStartQuad = s;
++s;
int quad = quadDigit;
quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit >= 0 )
{
quad = ( quad << 4 ) | quadDigit;
++s;
quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit >= 0 )
{
quad = ( quad << 4 ) | quadDigit;
++s;
quadDigit = ParseIPv6Addr_HexDigitVal( *s );
if ( quadDigit >= 0 )
{
quad = ( quad << 4 ) | quadDigit;
++s;
}
}
}
if ( *s == '.' )
{
unsigned char *pEndDottedDecimal = d+4;
if ( pEndDottedDecimal > pEndIP )
return false;
s = pszStartQuad;
for (;;)
{
int octet = ParseIPv6Addr_DecimalDigitVal( *s );
if ( octet < 0 )
return false;
++s;
int dig = ParseIPv6Addr_DecimalDigitVal( *s );
if ( dig >= 0 )
{
++s;
octet = octet*10 + dig;
dig = ParseIPv6Addr_DecimalDigitVal( *s );
if ( dig >= 0 )
{
++s;
octet = octet*10 + dig;
if ( octet > 255 )
return false;
}
}
*(d++) = (unsigned char)octet;
if ( d >= pEndDottedDecimal )
break;
if ( *s != '.' )
return false;
++s;
}
break;
}
*(d++) = (unsigned char)( quad >> 8 );
*(d++) = (unsigned char)quad;
if ( *s != ':' )
break;
if ( s[1] == ':' )
{
s += 2;
if ( pZeroFill )
return false;
pZeroFill = d;
bQuadMustFollow = false;
}
else
{
if ( !pZeroFill && d >= pEndIP )
break;
++s;
bQuadMustFollow = true;
}
}
if ( pZeroFill )
{
intptr_t nZeros = pEndIP - d;
if ( nZeros <= 0 )
return false;
memmove( pZeroFill+nZeros, pZeroFill, d-pZeroFill );
memset( pZeroFill, 0, nZeros );
}
else
{
if ( d != pEndIP )
return false;
}
if ( *s == '%' )
{
++s;
uint32_t unScope = 0;
int nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s );
if ( nScopeDigit < 0 )
return false;
unScope = (uint32_t)nScopeDigit;
for (;;)
{
++s;
if ( *s == '\0' || *s == ']' || ParseIPv6Addr_IsSpace( *s ) )
break;
nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s );
if ( nScopeDigit < 0 )
return false;
unScope = unScope * 10 + nScopeDigit;
}
if ( pOutScope )
*pOutScope = unScope;
}
else
{
if ( pOutScope )
*pOutScope = 0;
}
if ( *pszText == '[' )
{
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
if ( *s != ']' )
return false;
++s;
}
if ( *s == ':' || *s == '#' || *s == '.' || *s == 'p' || *s == 'P' )
{
++s;
}
else
{
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
if ( *s == '\0' )
{
if ( pOutPort )
*pOutPort = -1;
return true;
}
if ( strncmp( s, "port", 4 ) == 0 )
{
s += 4;
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
}
else
{
return false;
}
}
if ( !pOutPort )
return false;
int nPort = ParseIPv6Addr_DecimalDigitVal( *s );
if ( nPort < 0 )
return false;
for (;;)
{
++s;
if ( *s == '\0' || ParseIPv6Addr_IsSpace( *s ) )
break;
int portDigit = ParseIPv6Addr_DecimalDigitVal( *s );
if ( portDigit < 0 )
return false;
nPort = nPort * 10 + portDigit;
if ( nPort > 0xffff )
return false;
}
while ( ParseIPv6Addr_IsSpace( *s ) )
++s;
if ( *s != '\0' )
return false;
*pOutPort = nPort;
return true;
}