Commit 375256ba authored by Alexander Barkov's avatar Alexander Barkov

MDEV-18010 Add classes Inet4 and Inet6

parent 3b8c868a
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
static const int IN_ADDR_SIZE= sizeof (in_addr); static const size_t IN_ADDR_SIZE= 4;
static const int IN6_ADDR_SIZE= sizeof (in6_addr); static const size_t IN6_ADDR_SIZE= 16;
static const int IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2; static const size_t IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2;
static const char HEX_DIGITS[]= "0123456789abcdef"; static const char HEX_DIGITS[]= "0123456789abcdef";
...@@ -89,7 +89,6 @@ longlong Item_func_inet_aton::val_int() ...@@ -89,7 +89,6 @@ longlong Item_func_inet_aton::val_int()
return 0; return 0;
} }
///////////////////////////////////////////////////////////////////////////
String* Item_func_inet_ntoa::val_str(String* str) String* Item_func_inet_ntoa::val_str(String* str)
{ {
...@@ -139,56 +138,220 @@ String* Item_func_inet_ntoa::val_str(String* str) ...@@ -139,56 +138,220 @@ String* Item_func_inet_ntoa::val_str(String* str)
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/**
Check the function argument, handle errors properly.
@return The function value. class Inet4
*/ {
char m_buffer[IN_ADDR_SIZE];
protected:
bool str_to_ipv4(const char *str, size_t length, CHARSET_INFO *cs);
bool binary_to_ipv4(const char *str, size_t length)
{
if (length != sizeof(m_buffer))
return true;
memcpy(m_buffer, str, length);
return false;
}
// Non-initializing constructor
Inet4() { }
public:
void to_binary(char *dst, size_t dstsize) const
{
DBUG_ASSERT(dstsize >= sizeof(m_buffer));
memcpy(dst, m_buffer, sizeof(m_buffer));
}
bool to_binary(String *to) const
{
return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
}
size_t to_string(char *dst, size_t dstsize) const;
bool to_string(String *to) const
{
to->set_charset(&my_charset_latin1);
if (to->alloc(INET_ADDRSTRLEN))
return true;
to->length((uint32) to_string((char*) to->ptr(), INET_ADDRSTRLEN));
return false;
}
};
longlong Item_func_inet_bool_base::val_int() class Inet4_null: public Inet4, public Null_flag
{ {
DBUG_ASSERT(fixed); public:
// Initialize from a text representation
Inet4_null(const char *str, size_t length, CHARSET_INFO *cs)
:Null_flag(str_to_ipv4(str, length, cs))
{ }
Inet4_null(const String &str)
:Inet4_null(str.ptr(), str.length(), str.charset())
{ }
// Initialize from a binary representation
Inet4_null(const char *str, size_t length)
:Null_flag(binary_to_ipv4(str, length))
{ }
Inet4_null(const Binary_string &str)
:Inet4_null(str.ptr(), str.length())
{ }
public:
const Inet4& to_inet4() const
{
DBUG_ASSERT(!is_null());
return *this;
}
void to_binary(char *dst, size_t dstsize) const
{
to_inet4().to_binary(dst, dstsize);
}
bool to_binary(String *to) const
{
return to_inet4().to_binary(to);
}
size_t to_string(char *dst, size_t dstsize) const
{
return to_inet4().to_string(dst, dstsize);
}
bool to_string(String *to) const
{
return to_inet4().to_string(to);
}
};
// String argument expected class Inet6
if (unlikely(args[0]->result_type() != STRING_RESULT)) {
return 0; char m_buffer[IN6_ADDR_SIZE];
protected:
bool make_from_item(Item *item);
bool str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs);
bool binary_to_ipv6(const char *str, size_t length)
{
if (length != sizeof(m_buffer))
return true;
memcpy(m_buffer, str, length);
return false;
}
// Non-initializing constructor
Inet6() { }
public:
bool to_binary(String *to) const
{
return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin);
}
size_t to_string(char *dst, size_t dstsize) const;
bool to_string(String *to) const
{
to->set_charset(&my_charset_latin1);
if (to->alloc(INET6_ADDRSTRLEN))
return true;
to->length((uint32) to_string((char*) to->ptr(), INET6_ADDRSTRLEN));
return false;
}
bool is_v4compat() const
{
static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer);
}
bool is_v4mapped() const
{
static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size");
return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer);
}
};
String buffer;
String *arg_str= args[0]->val_str(&buffer);
if (unlikely(!arg_str)) // Out-of memory happened. error has been reported. class Inet6_null: public Inet6, public Null_flag
return 0; // Or: the underlying field is NULL {
public:
// Initialize from a text representation
Inet6_null(const char *str, size_t length, CHARSET_INFO *cs)
:Null_flag(str_to_ipv6(str, length, cs))
{ }
Inet6_null(const String &str)
:Inet6_null(str.ptr(), str.length(), str.charset())
{ }
// Initialize from a binary representation
Inet6_null(const char *str, size_t length)
:Null_flag(binary_to_ipv6(str, length))
{ }
Inet6_null(const Binary_string &str)
:Inet6_null(str.ptr(), str.length())
{ }
// Initialize from an Item
Inet6_null(Item *item)
:Null_flag(make_from_item(item))
{ }
public:
const Inet6& to_inet6() const
{
DBUG_ASSERT(!is_null());
return *this;
}
bool to_binary(String *to) const
{
DBUG_ASSERT(!is_null());
return to_inet6().to_binary(to);
}
size_t to_string(char *dst, size_t dstsize) const
{
return to_inet6().to_string(dst, dstsize);
}
bool to_string(String *to) const
{
return to_inet6().to_string(to);
}
bool is_v4compat() const
{
return to_inet6().is_v4compat();
}
bool is_v4mapped() const
{
return to_inet6().is_v4mapped();
}
};
return calc_value(arg_str) ? 1 : 0;
}
bool Inet6::make_from_item(Item *item)
{
String tmp(m_buffer, sizeof(m_buffer), &my_charset_bin);
String *str= item->val_str(&tmp);
/*
Charset could be tested in item->collation.collation before the val_str()
call, but traditionally Inet6 functions still call item->val_str()
for non-binary arguments and therefore execute side effects.
*/
if (!str || str->length() != sizeof(m_buffer) ||
str->charset() != &my_charset_bin)
return true;
if (str->ptr() != m_buffer)
memcpy(m_buffer, str->ptr(), sizeof(m_buffer));
return false;
};
///////////////////////////////////////////////////////////////////////////
/** /**
Tries to convert given string to binary IPv4-address representation. Tries to convert given string to binary IPv4-address representation.
This is a portable alternative to inet_pton(AF_INET). This is a portable alternative to inet_pton(AF_INET).
@param str String to convert. @param str String to convert.
@param str_len String length. @param str_length String length.
@param[out] ipv4_address Buffer to store IPv4-address.
@return Completion status. @return Completion status.
@retval false Given string does not represent an IPv4-address. @retval true - error, the given string does not represent an IPv4-address.
@retval true The string has been converted sucessfully. @retval false - ok, the string has been converted sucessfully.
@note The problem with inet_pton() is that it treats leading zeros in @note The problem with inet_pton() is that it treats leading zeros in
IPv4-part differently on different platforms. IPv4-part differently on different platforms.
*/ */
static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_address) bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs)
{ {
DBUG_ASSERT(cs->mbminlen == 1);
if (str_length < 7) if (str_length < 7)
{ {
DBUG_PRINT("error", ("str_to_ipv4(%.*s): " DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
"invalid IPv4 address: too short.", "invalid IPv4 address: too short.",
(int) str_length, str)); (int) str_length, str));
return false; return true;
} }
if (str_length > 15) if (str_length > 15)
...@@ -196,17 +359,18 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -196,17 +359,18 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
DBUG_PRINT("error", ("str_to_ipv4(%.*s): " DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
"invalid IPv4 address: too long.", "invalid IPv4 address: too long.",
(int) str_length, str)); (int) str_length, str));
return false; return true;
} }
unsigned char *ipv4_bytes= (unsigned char *) ipv4_address; unsigned char *ipv4_bytes= (unsigned char *) &m_buffer;
const char *str_end= str + str_length;
const char *p= str; const char *p= str;
int byte_value= 0; int byte_value= 0;
int chars_in_group= 0; int chars_in_group= 0;
int dot_count= 0; int dot_count= 0;
char c= 0; char c= 0;
while (((p - str) < (int)str_length) && *p) while (p < str_end && *p)
{ {
c= *p++; c= *p++;
...@@ -219,7 +383,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -219,7 +383,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too many characters in a group.", "too many characters in a group.",
(int) str_length, str)); (int) str_length, str));
return false; return true;
} }
byte_value= byte_value * 10 + (c - '0'); byte_value= byte_value * 10 + (c - '0');
...@@ -229,7 +393,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -229,7 +393,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"invalid byte value.", "invalid byte value.",
(int) str_length, str)); (int) str_length, str));
return false; return true;
} }
} }
else if (c == '.') else if (c == '.')
...@@ -239,7 +403,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -239,7 +403,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too few characters in a group.", "too few characters in a group.",
(int) str_length, str)); (int) str_length, str));
return false; return true;
} }
ipv4_bytes[dot_count]= (unsigned char) byte_value; ipv4_bytes[dot_count]= (unsigned char) byte_value;
...@@ -252,7 +416,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -252,7 +416,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{ {
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too many dots.", (int) str_length, str)); "too many dots.", (int) str_length, str));
return false; return true;
} }
} }
else else
...@@ -260,7 +424,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -260,7 +424,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"invalid character at pos %d.", "invalid character at pos %d.",
(int) str_length, str, (int) (p - str))); (int) str_length, str, (int) (p - str)));
return false; return true;
} }
} }
...@@ -268,7 +432,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -268,7 +432,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
{ {
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"ending at '.'.", (int) str_length, str)); "ending at '.'.", (int) str_length, str));
return false; return true;
} }
if (dot_count != 3) if (dot_count != 3)
...@@ -276,7 +440,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -276,7 +440,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
"too few groups.", "too few groups.",
(int) str_length, str)); (int) str_length, str));
return false; return true;
} }
ipv4_bytes[3]= (unsigned char) byte_value; ipv4_bytes[3]= (unsigned char) byte_value;
...@@ -285,44 +449,44 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres ...@@ -285,44 +449,44 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres
(int) str_length, str, (int) str_length, str,
ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[0], ipv4_bytes[1],
ipv4_bytes[2], ipv4_bytes[3])); ipv4_bytes[2], ipv4_bytes[3]));
return true; return false;
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Tries to convert given string to binary IPv6-address representation. Tries to convert given string to binary IPv6-address representation.
This is a portable alternative to inet_pton(AF_INET6). This is a portable alternative to inet_pton(AF_INET6).
@param str String to convert. @param str String to convert.
@param str_len String length. @param str_length String length.
@param[out] ipv6_address Buffer to store IPv6-address.
@return Completion status. @return Completion status.
@retval false Given string does not represent an IPv6-address. @retval true - error, the given string does not represent an IPv6-address.
@retval true The string has been converted sucessfully. @retval false - ok, the string has been converted sucessfully.
@note The problem with inet_pton() is that it treats leading zeros in @note The problem with inet_pton() is that it treats leading zeros in
IPv4-part differently on different platforms. IPv4-part differently on different platforms.
*/ */
static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs)
{ {
DBUG_ASSERT(cs->mbminlen == 1);
if (str_length < 2) if (str_length < 2)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too short.", DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too short.",
str_length, str)); (int) str_length, str));
return false; return true;
} }
if (str_length > 8 * 4 + 7) if (str_length > 8 * 4 + 7)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too long.", DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too long.",
str_length, str)); (int) str_length, str));
return false; return true;
} }
memset(ipv6_address, 0, IN6_ADDR_SIZE); memset(m_buffer, 0, sizeof(m_buffer));
const char *p= str; const char *p= str;
...@@ -333,20 +497,20 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -333,20 +497,20 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (*p != ':') if (*p != ':')
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"can not start with ':x'.", str_length, str)); "can not start with ':x'.", (int) str_length, str));
return false; return true;
} }
} }
char *ipv6_bytes= (char *) ipv6_address; const char *str_end= str + str_length;
char *ipv6_bytes_end= ipv6_bytes + IN6_ADDR_SIZE; char *ipv6_bytes_end= m_buffer + sizeof(m_buffer);
char *dst= ipv6_bytes; char *dst= m_buffer;
char *gap_ptr= NULL; char *gap_ptr= NULL;
const char *group_start_ptr= p; const char *group_start_ptr= p;
int chars_in_group= 0; int chars_in_group= 0;
int group_value= 0; int group_value= 0;
while (((p - str) < str_length) && *p) while (p < str_end && *p)
{ {
char c= *p++; char c= *p++;
...@@ -359,26 +523,26 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -359,26 +523,26 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (gap_ptr) if (gap_ptr)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"too many gaps(::).", str_length, str)); "too many gaps(::).", (int) str_length, str));
return false; return true;
} }
gap_ptr= dst; gap_ptr= dst;
continue; continue;
} }
if (!*p || ((p - str) >= str_length)) if (!*p || p >= str_end)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"ending at ':'.", str_length, str)); "ending at ':'.", (int) str_length, str));
return false; return true;
} }
if (dst + 2 > ipv6_bytes_end) if (dst + 2 > ipv6_bytes_end)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"too many groups (1).", str_length, str)); "too many groups (1).", (int) str_length, str));
return false; return true;
} }
dst[0]= (unsigned char) (group_value >> 8) & 0xff; dst[0]= (unsigned char) (group_value >> 8) & 0xff;
...@@ -393,19 +557,19 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -393,19 +557,19 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (dst + IN_ADDR_SIZE > ipv6_bytes_end) if (dst + IN_ADDR_SIZE > ipv6_bytes_end)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"unexpected IPv4-part.", str_length, str)); "unexpected IPv4-part.", (int) str_length, str));
return false; return true;
} }
if (!str_to_ipv4(group_start_ptr, Inet4_null tmp(group_start_ptr, (size_t) (str_end - group_start_ptr), cs);
str + str_length - group_start_ptr, if (tmp.is_null())
(in_addr *) dst))
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"invalid IPv4-part.", str_length, str)); "invalid IPv4-part.", (int) str_length, str));
return false; return true;
} }
tmp.to_binary(dst, IN_ADDR_SIZE);
dst += IN_ADDR_SIZE; dst += IN_ADDR_SIZE;
chars_in_group= 0; chars_in_group= 0;
...@@ -419,16 +583,16 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -419,16 +583,16 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"invalid character at pos %d.", "invalid character at pos %d.",
str_length, str, (int) (p - str))); (int) str_length, str, (int) (p - str)));
return false; return true;
} }
if (chars_in_group >= 4) if (chars_in_group >= 4)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"too many digits in group.", "too many digits in group.",
str_length, str)); (int) str_length, str));
return false; return true;
} }
group_value <<= 4; group_value <<= 4;
...@@ -445,8 +609,8 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -445,8 +609,8 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (dst + 2 > ipv6_bytes_end) if (dst + 2 > ipv6_bytes_end)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"too many groups (2).", str_length, str)); "too many groups (2).", (int) str_length, str));
return false; return true;
} }
dst[0]= (unsigned char) (group_value >> 8) & 0xff; dst[0]= (unsigned char) (group_value >> 8) & 0xff;
...@@ -459,8 +623,8 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -459,8 +623,8 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (dst == ipv6_bytes_end) if (dst == ipv6_bytes_end)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"no room for a gap (::).", str_length, str)); "no room for a gap (::).", (int) str_length, str));
return false; return true;
} }
int bytes_to_move= (int)(dst - gap_ptr); int bytes_to_move= (int)(dst - gap_ptr);
...@@ -477,49 +641,48 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) ...@@ -477,49 +641,48 @@ static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
if (dst < ipv6_bytes_end) if (dst < ipv6_bytes_end)
{ {
DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
"too few groups.", str_length, str)); "too few groups.", (int) str_length, str));
return false; return true;
} }
return true; return false;
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Converts IPv4-binary-address to a string. This function is a portable Converts IPv4-binary-address to a string. This function is a portable
alternative to inet_ntop(AF_INET). alternative to inet_ntop(AF_INET).
@param[in] ipv4 IPv4-address data (byte array) @param[in] ipv4 IPv4-address data (byte array)
@param[out] str A buffer to store string representation of IPv4-address. @param[out] dst A buffer to store string representation of IPv4-address.
It must be at least of INET_ADDRSTRLEN. @param[in] dstsize Number of bytes avaiable in "dst"
@note The problem with inet_ntop() is that it is available starting from @note The problem with inet_ntop() is that it is available starting from
Windows Vista, but the minimum supported version is Windows 2000. Windows Vista, but the minimum supported version is Windows 2000.
*/ */
static void ipv4_to_str(const in_addr *ipv4, char *str) size_t Inet4::to_string(char *dst, size_t dstsize) const
{ {
const unsigned char *ipv4_bytes= (const unsigned char *) ipv4; return (size_t) my_snprintf(dst, dstsize, "%d.%d.%d.%d",
(uchar) m_buffer[0], (uchar) m_buffer[1],
sprintf(str, "%d.%d.%d.%d", (uchar) m_buffer[2], (uchar) m_buffer[3]);
ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3]);
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Converts IPv6-binary-address to a string. This function is a portable Converts IPv6-binary-address to a string. This function is a portable
alternative to inet_ntop(AF_INET6). alternative to inet_ntop(AF_INET6).
@param[in] ipv6 IPv6-address data (byte array) @param[in] ipv6 IPv6-address data (byte array)
@param[out] str A buffer to store string representation of IPv6-address. @param[out] dst A buffer to store string representation of IPv6-address.
It must be at least of INET6_ADDRSTRLEN. It must be at least of INET6_ADDRSTRLEN.
@param[in] dstsize Number of bytes available dst.
@note The problem with inet_ntop() is that it is available starting from @note The problem with inet_ntop() is that it is available starting from
Windows Vista, but out the minimum supported version is Windows 2000. Windows Vista, but out the minimum supported version is Windows 2000.
*/ */
static void ipv6_to_str(const in6_addr *ipv6, char *str) size_t Inet6::to_string(char *dst, size_t dstsize) const
{ {
struct Region struct Region
{ {
...@@ -527,6 +690,8 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -527,6 +690,8 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
int length; int length;
}; };
const char *ipv6= m_buffer;
char *dstend= dst + dstsize;
const unsigned char *ipv6_bytes= (const unsigned char *) ipv6; const unsigned char *ipv6_bytes= (const unsigned char *) ipv6;
// 1. Translate IPv6-address bytes to words. // 1. Translate IPv6-address bytes to words.
...@@ -535,7 +700,8 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -535,7 +700,8 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
uint16 ipv6_words[IN6_ADDR_NUM_WORDS]; uint16 ipv6_words[IN6_ADDR_NUM_WORDS];
for (int i= 0; i < IN6_ADDR_NUM_WORDS; ++i) DBUG_ASSERT(dstsize > 0); // Need a space at least for the trailing '\0'
for (size_t i= 0; i < IN6_ADDR_NUM_WORDS; ++i)
ipv6_words[i]= (ipv6_bytes[2 * i] << 8) + ipv6_bytes[2 * i + 1]; ipv6_words[i]= (ipv6_bytes[2 * i] << 8) + ipv6_bytes[2 * i + 1];
// 2. Find "the gap" -- longest sequence of zeros in IPv6-address. // 2. Find "the gap" -- longest sequence of zeros in IPv6-address.
...@@ -545,7 +711,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -545,7 +711,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
{ {
Region rg= { -1, -1 }; Region rg= { -1, -1 };
for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i) for (size_t i= 0; i < IN6_ADDR_NUM_WORDS; ++i)
{ {
if (ipv6_words[i] != 0) if (ipv6_words[i] != 0)
{ {
...@@ -566,7 +732,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -566,7 +732,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
} }
else else
{ {
rg.pos= i; rg.pos= (int) i;
rg.length= 1; rg.length= 1;
} }
} }
...@@ -581,10 +747,14 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -581,10 +747,14 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
// 3. Convert binary data to string. // 3. Convert binary data to string.
char *p= str; char *p= dst;
for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i) for (int i= 0; i < (int) IN6_ADDR_NUM_WORDS; ++i)
{ {
DBUG_ASSERT(dstend >= p);
size_t dstsize_available= dstend - p;
if (dstsize_available < 5)
break;
if (i == gap.pos) if (i == gap.pos)
{ {
// We're at the gap position. We should put trailing ':' and jump to // We're at the gap position. We should put trailing ':' and jump to
...@@ -611,10 +781,11 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -611,10 +781,11 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
{ {
// The data represents either IPv4-compatible or IPv4-mapped address. // The data represents either IPv4-compatible or IPv4-mapped address.
// The IPv6-part (zeros or zeros + ffff) has been already put into // The IPv6-part (zeros or zeros + ffff) has been already put into
// the string (str). Now it's time to dump IPv4-part. // the string (dst). Now it's time to dump IPv4-part.
ipv4_to_str((const in_addr *) (ipv6_bytes + 12), p); return (size_t) (p - dst) +
return; Inet4_null((const char *) (ipv6_bytes + 12), 4).
to_string(p, dstsize_available);
} }
else else
{ {
...@@ -625,7 +796,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -625,7 +796,7 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
p += sprintf(p, "%x", ipv6_words[i]); p += sprintf(p, "%x", ipv6_words[i]);
if (i != IN6_ADDR_NUM_WORDS - 1) if (i + 1 != IN6_ADDR_NUM_WORDS)
{ {
*p= ':'; *p= ':';
++p; ++p;
...@@ -634,73 +805,51 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str) ...@@ -634,73 +805,51 @@ static void ipv6_to_str(const in6_addr *ipv6, char *str)
} }
*p= 0; *p= 0;
return (size_t) (p - dst);
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
String *Item_func_inet6_aton::val_str(String *buffer)
{
DBUG_ASSERT(fixed);
// String argument expected
if (unlikely(args[0]->result_type() != STRING_RESULT))
{
null_value= true;
return NULL;
}
Ascii_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
null_value= tmp.is_null() || !calc_value(tmp.string(), buffer);
return unlikely(null_value) ? NULL : buffer;
}
/** /**
Converts IP-address-string to IP-address-data. Converts IP-address-string to IP-address-data.
@param arg IP-address-string. ipv4-string -> varbinary(4)
@param [out] buffer Buffer to store IP-address-data. ipv6-string -> varbinary(16)
@return Completion status. @return Completion status.
@retval false Given string does not represent an IP-address. @retval NULL Given string does not represent an IP-address.
@retval true The string has been converted sucessfully. @retval !NULL The string has been converted sucessfully.
*/ */
bool Item_func_inet6_aton::calc_value(const String *arg, String *buffer) String *Item_func_inet6_aton::val_str(String *buffer)
{ {
// ipv4-string -> varbinary(4) DBUG_ASSERT(fixed);
// ipv6-string -> varbinary(16)
in_addr ipv4_address; Ascii_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
in6_addr ipv6_address; if ((null_value= tmp.is_null()))
return NULL;
if (str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address)) Inet4_null ipv4(*tmp.string());
if (!ipv4.is_null())
{ {
buffer->length(0); ipv4.to_binary(buffer);
buffer->append((char *) &ipv4_address, sizeof (in_addr), &my_charset_bin); return buffer;
return true;
} }
if (str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address)) Inet6_null ipv6(*tmp.string());
if (!ipv6.is_null())
{ {
buffer->length(0); ipv6.to_binary(buffer);
buffer->append((char *) &ipv6_address, sizeof (in6_addr), &my_charset_bin); return buffer;
return true;
} }
return false; null_value= true;
return NULL;
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Check the function argument, handle errors properly. Converts IP-address-data to IP-address-string.
@param [out] buffer Buffer for string operations.
@return The function value.
*/ */
String *Item_func_inet6_ntoa::val_str_ascii(String *buffer) String *Item_func_inet6_ntoa::val_str_ascii(String *buffer)
...@@ -716,126 +865,70 @@ String *Item_func_inet6_ntoa::val_str_ascii(String *buffer) ...@@ -716,126 +865,70 @@ String *Item_func_inet6_ntoa::val_str_ascii(String *buffer)
} }
String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]); String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
null_value= tmp.is_null() || !calc_value(tmp.string(), buffer); if ((null_value= tmp.is_null()))
return unlikely(null_value) ? NULL : buffer; return NULL;
}
/**
Converts IP-address-data to IP-address-string.
@param arg IP-address-data.
@param [out] buffer Buffer to store IP-address-string.
@return Completion status.
@retval false The argument does not correspond to IP-address.
@retval true The string has been converted sucessfully.
*/
bool Item_func_inet6_ntoa::calc_value(const Binary_string *arg, String *buffer) Inet4_null ipv4(static_cast<const Binary_string&>(*tmp.string()));
{ if (!ipv4.is_null())
if ((int) arg->length() == IN_ADDR_SIZE)
{ {
char str[INET_ADDRSTRLEN]; ipv4.to_string(buffer);
return buffer;
ipv4_to_str((const in_addr *) arg->ptr(), str);
buffer->length(0);
buffer->append(str, (uint32) strlen(str), &my_charset_latin1);
return true;
} }
else if ((int) arg->length() == IN6_ADDR_SIZE)
{
char str[INET6_ADDRSTRLEN];
ipv6_to_str((const in6_addr *) arg->ptr(), str);
buffer->length(0);
buffer->append(str, (uint32) strlen(str), &my_charset_latin1);
return true; Inet6_null ipv6(static_cast<const Binary_string&>(*tmp.string()));
if (!ipv6.is_null())
{
ipv6.to_string(buffer);
return buffer;
} }
DBUG_PRINT("info", DBUG_PRINT("info", ("INET6_NTOA(): varbinary(4) or varbinary(16) expected."));
("INET6_NTOA(): varbinary(4) or varbinary(16) expected.")); null_value= true;
return false; return NULL;
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Checks if the passed string represents an IPv4-address. Checks if the passed string represents an IPv4-address.
@param arg The string to check.
@return Check status.
@retval false The passed string does not represent an IPv4-address.
@retval true The passed string represents an IPv4-address.
*/ */
bool Item_func_is_ipv4::calc_value(const String *arg) longlong Item_func_is_ipv4::val_int()
{ {
in_addr ipv4_address; DBUG_ASSERT(fixed);
String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
return str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address); return !tmp.is_null() && !Inet4_null(*tmp.string()).is_null();
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Checks if the passed string represents an IPv6-address. Checks if the passed string represents an IPv6-address.
@param arg The string to check.
@return Check status.
@retval false The passed string does not represent an IPv6-address.
@retval true The passed string represents an IPv6-address.
*/ */
bool Item_func_is_ipv6::calc_value(const String *arg) longlong Item_func_is_ipv6::val_int()
{ {
in6_addr ipv6_address; DBUG_ASSERT(fixed);
String_ptr_and_buffer<STRING_BUFFER_USUAL_SIZE> tmp(args[0]);
return str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address); return !tmp.is_null() && !Inet6_null(*tmp.string()).is_null();
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Checks if the passed IPv6-address is an IPv4-compat IPv6-address. Checks if the passed IPv6-address is an IPv4-compat IPv6-address.
@param arg The IPv6-address to check.
@return Check status.
@retval false The passed IPv6-address is not an IPv4-compatible IPv6-address.
@retval true The passed IPv6-address is an IPv4-compatible IPv6-address.
*/ */
bool Item_func_is_ipv4_compat::calc_value(const String *arg) longlong Item_func_is_ipv4_compat::val_int()
{ {
if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin) Inet6_null ip6(args[0]);
return false; return !ip6.is_null() && ip6.is_v4compat();
return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) arg->ptr());
} }
///////////////////////////////////////////////////////////////////////////
/** /**
Checks if the passed IPv6-address is an IPv4-mapped IPv6-address. Checks if the passed IPv6-address is an IPv4-mapped IPv6-address.
@param arg The IPv6-address to check.
@return Check status.
@retval false The passed IPv6-address is not an IPv4-mapped IPv6-address.
@retval true The passed IPv6-address is an IPv4-mapped IPv6-address.
*/ */
bool Item_func_is_ipv4_mapped::calc_value(const String *arg) longlong Item_func_is_ipv4_mapped::val_int()
{ {
if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin) Inet6_null ip6(args[0]);
return false; return !ip6.is_null() && ip6.is_v4mapped();
return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) arg->ptr());
} }
...@@ -81,13 +81,7 @@ class Item_func_inet_bool_base : public Item_bool_func ...@@ -81,13 +81,7 @@ class Item_func_inet_bool_base : public Item_bool_func
{ {
null_value= false; null_value= false;
} }
public:
virtual longlong val_int();
bool need_parentheses_in_default() { return false; } bool need_parentheses_in_default() { return false; }
protected:
virtual bool calc_value(const String *arg) = 0;
}; };
...@@ -117,9 +111,6 @@ class Item_func_inet6_aton : public Item_str_func ...@@ -117,9 +111,6 @@ class Item_func_inet6_aton : public Item_str_func
{ return get_item_copy<Item_func_inet6_aton>(thd, this); } { return get_item_copy<Item_func_inet6_aton>(thd, this); }
String *val_str(String *to); String *val_str(String *to);
protected:
bool calc_value(const String *arg, String *buffer);
}; };
...@@ -153,9 +144,6 @@ class Item_func_inet6_ntoa : public Item_str_ascii_func ...@@ -153,9 +144,6 @@ class Item_func_inet6_ntoa : public Item_str_ascii_func
String *val_str_ascii(String *to); String *val_str_ascii(String *to);
Item *get_copy(THD *thd) Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_inet6_ntoa>(thd, this); } { return get_item_copy<Item_func_inet6_ntoa>(thd, this); }
protected:
bool calc_value(const Binary_string *arg, String *buffer);
}; };
...@@ -176,8 +164,7 @@ class Item_func_is_ipv4 : public Item_func_inet_bool_base ...@@ -176,8 +164,7 @@ class Item_func_is_ipv4 : public Item_func_inet_bool_base
Item *get_copy(THD *thd) Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv4>(thd, this); } { return get_item_copy<Item_func_is_ipv4>(thd, this); }
protected: longlong val_int();
virtual bool calc_value(const String *arg);
}; };
...@@ -192,14 +179,12 @@ class Item_func_is_ipv6 : public Item_func_inet_bool_base ...@@ -192,14 +179,12 @@ class Item_func_is_ipv6 : public Item_func_inet_bool_base
Item_func_inet_bool_base(thd, ip_addr) Item_func_inet_bool_base(thd, ip_addr)
{ } { }
public:
virtual const char *func_name() const virtual const char *func_name() const
{ return "is_ipv6"; } { return "is_ipv6"; }
Item *get_copy(THD *thd) Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv6>(thd, this); } { return get_item_copy<Item_func_is_ipv6>(thd, this); }
protected: longlong val_int();
virtual bool calc_value(const String *arg);
}; };
...@@ -213,15 +198,11 @@ class Item_func_is_ipv4_compat : public Item_func_inet_bool_base ...@@ -213,15 +198,11 @@ class Item_func_is_ipv4_compat : public Item_func_inet_bool_base
inline Item_func_is_ipv4_compat(THD *thd, Item *ip_addr): inline Item_func_is_ipv4_compat(THD *thd, Item *ip_addr):
Item_func_inet_bool_base(thd, ip_addr) Item_func_inet_bool_base(thd, ip_addr)
{ } { }
public:
virtual const char *func_name() const virtual const char *func_name() const
{ return "is_ipv4_compat"; } { return "is_ipv4_compat"; }
Item *get_copy(THD *thd) Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv4_compat>(thd, this); } { return get_item_copy<Item_func_is_ipv4_compat>(thd, this); }
longlong val_int();
protected:
virtual bool calc_value(const String *arg);
}; };
...@@ -235,15 +216,11 @@ class Item_func_is_ipv4_mapped : public Item_func_inet_bool_base ...@@ -235,15 +216,11 @@ class Item_func_is_ipv4_mapped : public Item_func_inet_bool_base
inline Item_func_is_ipv4_mapped(THD *thd, Item *ip_addr): inline Item_func_is_ipv4_mapped(THD *thd, Item *ip_addr):
Item_func_inet_bool_base(thd, ip_addr) Item_func_inet_bool_base(thd, ip_addr)
{ } { }
public:
virtual const char *func_name() const virtual const char *func_name() const
{ return "is_ipv4_mapped"; } { return "is_ipv4_mapped"; }
Item *get_copy(THD *thd) Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); } { return get_item_copy<Item_func_is_ipv4_mapped>(thd, this); }
longlong val_int();
protected:
virtual bool calc_value(const String *arg);
}; };
#endif // ITEM_INETFUNC_INCLUDED #endif // ITEM_INETFUNC_INCLUDED
...@@ -119,16 +119,10 @@ class String_ptr ...@@ -119,16 +119,10 @@ class String_ptr
:m_string_ptr(str) :m_string_ptr(str)
{ } { }
String_ptr(Item *item, String *buffer); String_ptr(Item *item, String *buffer);
const String *string() const { return m_string_ptr; } const String *string() const
const char *ptr() const
{ {
DBUG_ASSERT(m_string_ptr); DBUG_ASSERT(m_string_ptr);
return m_string_ptr->ptr(); return m_string_ptr;
}
uint32 length() const
{
DBUG_ASSERT(m_string_ptr);
return m_string_ptr->length();
} }
bool is_null() const { return m_string_ptr == NULL; } bool is_null() const { return m_string_ptr == NULL; }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment