| 1 | /* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. |
| 2 | Copyright (c) 2014 MariaDB Foundation |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | #include "mariadb.h" |
| 18 | #include "item_inetfunc.h" |
| 19 | |
| 20 | #include "my_net.h" |
| 21 | |
| 22 | /////////////////////////////////////////////////////////////////////////// |
| 23 | |
| 24 | static const int IN_ADDR_SIZE= sizeof (in_addr); |
| 25 | static const int IN6_ADDR_SIZE= sizeof (in6_addr); |
| 26 | static const int IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2; |
| 27 | |
| 28 | static const char HEX_DIGITS[]= "0123456789abcdef" ; |
| 29 | |
| 30 | /////////////////////////////////////////////////////////////////////////// |
| 31 | |
| 32 | longlong Item_func_inet_aton::val_int() |
| 33 | { |
| 34 | DBUG_ASSERT(fixed); |
| 35 | |
| 36 | uint byte_result= 0; |
| 37 | ulonglong result= 0; // We are ready for 64 bit addresses |
| 38 | const char *p,* end; |
| 39 | char c= '.'; // we mark c to indicate invalid IP in case length is 0 |
| 40 | int dot_count= 0; |
| 41 | |
| 42 | StringBuffer<36> tmp; |
| 43 | String *s= args[0]->val_str_ascii(&tmp); |
| 44 | |
| 45 | if (!s) // If null value |
| 46 | goto err; |
| 47 | |
| 48 | null_value= 0; |
| 49 | |
| 50 | end= (p = s->ptr()) + s->length(); |
| 51 | while (p < end) |
| 52 | { |
| 53 | c= *p++; |
| 54 | int digit= (int) (c - '0'); |
| 55 | if (digit >= 0 && digit <= 9) |
| 56 | { |
| 57 | if ((byte_result= byte_result * 10 + digit) > 255) |
| 58 | goto err; // Wrong address |
| 59 | } |
| 60 | else if (c == '.') |
| 61 | { |
| 62 | dot_count++; |
| 63 | result= (result << 8) + (ulonglong) byte_result; |
| 64 | byte_result= 0; |
| 65 | } |
| 66 | else |
| 67 | goto err; // Invalid character |
| 68 | } |
| 69 | if (c != '.') // IP number can't end on '.' |
| 70 | { |
| 71 | /* |
| 72 | Attempt to support short forms of IP-addresses. It's however pretty |
| 73 | basic one comparing to the BSD support. |
| 74 | Examples: |
| 75 | 127 -> 0.0.0.127 |
| 76 | 127.255 -> 127.0.0.255 |
| 77 | 127.256 -> NULL (should have been 127.0.1.0) |
| 78 | 127.2.1 -> 127.2.0.1 |
| 79 | */ |
| 80 | switch (dot_count) { |
| 81 | case 1: result<<= 8; /* Fall through */ |
| 82 | case 2: result<<= 8; /* Fall through */ |
| 83 | } |
| 84 | return (result << 8) + (ulonglong) byte_result; |
| 85 | } |
| 86 | |
| 87 | err: |
| 88 | null_value=1; |
| 89 | return 0; |
| 90 | } |
| 91 | |
| 92 | /////////////////////////////////////////////////////////////////////////// |
| 93 | |
| 94 | String* Item_func_inet_ntoa::val_str(String* str) |
| 95 | { |
| 96 | DBUG_ASSERT(fixed); |
| 97 | |
| 98 | ulonglong n= (ulonglong) args[0]->val_int(); |
| 99 | |
| 100 | /* |
| 101 | We do not know if args[0] is NULL until we have called |
| 102 | some val function on it if args[0] is not a constant! |
| 103 | |
| 104 | Also return null if n > 255.255.255.255 |
| 105 | */ |
| 106 | if ((null_value= (args[0]->null_value || n > 0xffffffff))) |
| 107 | return 0; // Null value |
| 108 | |
| 109 | str->set_charset(collation.collation); |
| 110 | str->length(0); |
| 111 | |
| 112 | uchar buf[8]; |
| 113 | int4store(buf, n); |
| 114 | |
| 115 | /* Now we can assume little endian. */ |
| 116 | |
| 117 | char num[4]; |
| 118 | num[3]= '.'; |
| 119 | |
| 120 | for (uchar *p= buf + 4; p-- > buf;) |
| 121 | { |
| 122 | uint c= *p; |
| 123 | uint n1, n2; // Try to avoid divisions |
| 124 | n1= c / 100; // 100 digits |
| 125 | c-= n1 * 100; |
| 126 | n2= c / 10; // 10 digits |
| 127 | c-= n2 * 10; // last digit |
| 128 | num[0]= (char) n1 + '0'; |
| 129 | num[1]= (char) n2 + '0'; |
| 130 | num[2]= (char) c + '0'; |
| 131 | uint length= (n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero |
| 132 | uint dot_length= (p <= buf) ? 1 : 0; |
| 133 | (void) str->append(num + 4 - length, length - dot_length, |
| 134 | &my_charset_latin1); |
| 135 | } |
| 136 | |
| 137 | return str; |
| 138 | } |
| 139 | |
| 140 | /////////////////////////////////////////////////////////////////////////// |
| 141 | |
| 142 | /** |
| 143 | Check the function argument, handle errors properly. |
| 144 | |
| 145 | @return The function value. |
| 146 | */ |
| 147 | |
| 148 | longlong Item_func_inet_bool_base::val_int() |
| 149 | { |
| 150 | DBUG_ASSERT(fixed); |
| 151 | |
| 152 | // String argument expected |
| 153 | if (unlikely(args[0]->result_type() != STRING_RESULT)) |
| 154 | return 0; |
| 155 | |
| 156 | String buffer; |
| 157 | String *arg_str= args[0]->val_str(&buffer); |
| 158 | |
| 159 | if (unlikely(!arg_str)) // Out-of memory happened. error has been reported. |
| 160 | return 0; // Or: the underlying field is NULL |
| 161 | |
| 162 | return calc_value(arg_str) ? 1 : 0; |
| 163 | } |
| 164 | |
| 165 | /////////////////////////////////////////////////////////////////////////// |
| 166 | |
| 167 | /** |
| 168 | Check the function argument, handle errors properly. |
| 169 | |
| 170 | @param [out] buffer Buffer for string operations. |
| 171 | |
| 172 | @return The function value. |
| 173 | */ |
| 174 | |
| 175 | String *Item_func_inet_str_base::val_str_ascii(String *buffer) |
| 176 | { |
| 177 | DBUG_ASSERT(fixed); |
| 178 | |
| 179 | // String argument expected |
| 180 | if (unlikely(args[0]->result_type() != STRING_RESULT)) |
| 181 | { |
| 182 | null_value= true; |
| 183 | return NULL; |
| 184 | } |
| 185 | |
| 186 | StringBuffer<STRING_BUFFER_USUAL_SIZE> tmp; |
| 187 | String *arg_str= args[0]->val_str(&tmp); |
| 188 | if (unlikely(!arg_str)) |
| 189 | { |
| 190 | // Out-of memory happened. error has been reported. |
| 191 | // Or: the underlying field is NULL |
| 192 | null_value= true; |
| 193 | return NULL; |
| 194 | } |
| 195 | |
| 196 | null_value= !calc_value(arg_str, buffer); |
| 197 | |
| 198 | return unlikely(null_value) ? NULL : buffer; |
| 199 | } |
| 200 | |
| 201 | /////////////////////////////////////////////////////////////////////////// |
| 202 | |
| 203 | /** |
| 204 | Tries to convert given string to binary IPv4-address representation. |
| 205 | This is a portable alternative to inet_pton(AF_INET). |
| 206 | |
| 207 | @param str String to convert. |
| 208 | @param str_len String length. |
| 209 | @param[out] ipv4_address Buffer to store IPv4-address. |
| 210 | |
| 211 | @return Completion status. |
| 212 | @retval false Given string does not represent an IPv4-address. |
| 213 | @retval true The string has been converted sucessfully. |
| 214 | |
| 215 | @note The problem with inet_pton() is that it treats leading zeros in |
| 216 | IPv4-part differently on different platforms. |
| 217 | */ |
| 218 | |
| 219 | static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_address) |
| 220 | { |
| 221 | if (str_length < 7) |
| 222 | { |
| 223 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): " |
| 224 | "invalid IPv4 address: too short." , |
| 225 | (int) str_length, str)); |
| 226 | return false; |
| 227 | } |
| 228 | |
| 229 | if (str_length > 15) |
| 230 | { |
| 231 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): " |
| 232 | "invalid IPv4 address: too long." , |
| 233 | (int) str_length, str)); |
| 234 | return false; |
| 235 | } |
| 236 | |
| 237 | unsigned char *ipv4_bytes= (unsigned char *) ipv4_address; |
| 238 | const char *p= str; |
| 239 | int byte_value= 0; |
| 240 | int chars_in_group= 0; |
| 241 | int dot_count= 0; |
| 242 | char c= 0; |
| 243 | |
| 244 | while (((p - str) < (int)str_length) && *p) |
| 245 | { |
| 246 | c= *p++; |
| 247 | |
| 248 | if (my_isdigit(&my_charset_latin1, c)) |
| 249 | { |
| 250 | ++chars_in_group; |
| 251 | |
| 252 | if (chars_in_group > 3) |
| 253 | { |
| 254 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 255 | "too many characters in a group." , |
| 256 | (int) str_length, str)); |
| 257 | return false; |
| 258 | } |
| 259 | |
| 260 | byte_value= byte_value * 10 + (c - '0'); |
| 261 | |
| 262 | if (byte_value > 255) |
| 263 | { |
| 264 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 265 | "invalid byte value." , |
| 266 | (int) str_length, str)); |
| 267 | return false; |
| 268 | } |
| 269 | } |
| 270 | else if (c == '.') |
| 271 | { |
| 272 | if (chars_in_group == 0) |
| 273 | { |
| 274 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 275 | "too few characters in a group." , |
| 276 | (int) str_length, str)); |
| 277 | return false; |
| 278 | } |
| 279 | |
| 280 | ipv4_bytes[dot_count]= (unsigned char) byte_value; |
| 281 | |
| 282 | ++dot_count; |
| 283 | byte_value= 0; |
| 284 | chars_in_group= 0; |
| 285 | |
| 286 | if (dot_count > 3) |
| 287 | { |
| 288 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 289 | "too many dots." , (int) str_length, str)); |
| 290 | return false; |
| 291 | } |
| 292 | } |
| 293 | else |
| 294 | { |
| 295 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 296 | "invalid character at pos %d." , |
| 297 | (int) str_length, str, (int) (p - str))); |
| 298 | return false; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | if (c == '.') |
| 303 | { |
| 304 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 305 | "ending at '.'." , (int) str_length, str)); |
| 306 | return false; |
| 307 | } |
| 308 | |
| 309 | if (dot_count != 3) |
| 310 | { |
| 311 | DBUG_PRINT("error" , ("str_to_ipv4(%.*s): invalid IPv4 address: " |
| 312 | "too few groups." , |
| 313 | (int) str_length, str)); |
| 314 | return false; |
| 315 | } |
| 316 | |
| 317 | ipv4_bytes[3]= (unsigned char) byte_value; |
| 318 | |
| 319 | DBUG_PRINT("info" , ("str_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d" , |
| 320 | (int) str_length, str, |
| 321 | ipv4_bytes[0], ipv4_bytes[1], |
| 322 | ipv4_bytes[2], ipv4_bytes[3])); |
| 323 | return true; |
| 324 | } |
| 325 | |
| 326 | /////////////////////////////////////////////////////////////////////////// |
| 327 | |
| 328 | /** |
| 329 | Tries to convert given string to binary IPv6-address representation. |
| 330 | This is a portable alternative to inet_pton(AF_INET6). |
| 331 | |
| 332 | @param str String to convert. |
| 333 | @param str_len String length. |
| 334 | @param[out] ipv6_address Buffer to store IPv6-address. |
| 335 | |
| 336 | @return Completion status. |
| 337 | @retval false Given string does not represent an IPv6-address. |
| 338 | @retval true The string has been converted sucessfully. |
| 339 | |
| 340 | @note The problem with inet_pton() is that it treats leading zeros in |
| 341 | IPv4-part differently on different platforms. |
| 342 | */ |
| 343 | |
| 344 | static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address) |
| 345 | { |
| 346 | if (str_length < 2) |
| 347 | { |
| 348 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: too short." , |
| 349 | str_length, str)); |
| 350 | return false; |
| 351 | } |
| 352 | |
| 353 | if (str_length > 8 * 4 + 7) |
| 354 | { |
| 355 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: too long." , |
| 356 | str_length, str)); |
| 357 | return false; |
| 358 | } |
| 359 | |
| 360 | memset(ipv6_address, 0, IN6_ADDR_SIZE); |
| 361 | |
| 362 | const char *p= str; |
| 363 | |
| 364 | if (*p == ':') |
| 365 | { |
| 366 | ++p; |
| 367 | |
| 368 | if (*p != ':') |
| 369 | { |
| 370 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 371 | "can not start with ':x'." , str_length, str)); |
| 372 | return false; |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | char *ipv6_bytes= (char *) ipv6_address; |
| 377 | char *ipv6_bytes_end= ipv6_bytes + IN6_ADDR_SIZE; |
| 378 | char *dst= ipv6_bytes; |
| 379 | char *gap_ptr= NULL; |
| 380 | const char *group_start_ptr= p; |
| 381 | int chars_in_group= 0; |
| 382 | int group_value= 0; |
| 383 | |
| 384 | while (((p - str) < str_length) && *p) |
| 385 | { |
| 386 | char c= *p++; |
| 387 | |
| 388 | if (c == ':') |
| 389 | { |
| 390 | group_start_ptr= p; |
| 391 | |
| 392 | if (!chars_in_group) |
| 393 | { |
| 394 | if (gap_ptr) |
| 395 | { |
| 396 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 397 | "too many gaps(::)." , str_length, str)); |
| 398 | return false; |
| 399 | } |
| 400 | |
| 401 | gap_ptr= dst; |
| 402 | continue; |
| 403 | } |
| 404 | |
| 405 | if (!*p || ((p - str) >= str_length)) |
| 406 | { |
| 407 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 408 | "ending at ':'." , str_length, str)); |
| 409 | return false; |
| 410 | } |
| 411 | |
| 412 | if (dst + 2 > ipv6_bytes_end) |
| 413 | { |
| 414 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 415 | "too many groups (1)." , str_length, str)); |
| 416 | return false; |
| 417 | } |
| 418 | |
| 419 | dst[0]= (unsigned char) (group_value >> 8) & 0xff; |
| 420 | dst[1]= (unsigned char) group_value & 0xff; |
| 421 | dst += 2; |
| 422 | |
| 423 | chars_in_group= 0; |
| 424 | group_value= 0; |
| 425 | } |
| 426 | else if (c == '.') |
| 427 | { |
| 428 | if (dst + IN_ADDR_SIZE > ipv6_bytes_end) |
| 429 | { |
| 430 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 431 | "unexpected IPv4-part." , str_length, str)); |
| 432 | return false; |
| 433 | } |
| 434 | |
| 435 | if (!str_to_ipv4(group_start_ptr, |
| 436 | str + str_length - group_start_ptr, |
| 437 | (in_addr *) dst)) |
| 438 | { |
| 439 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 440 | "invalid IPv4-part." , str_length, str)); |
| 441 | return false; |
| 442 | } |
| 443 | |
| 444 | dst += IN_ADDR_SIZE; |
| 445 | chars_in_group= 0; |
| 446 | |
| 447 | break; |
| 448 | } |
| 449 | else |
| 450 | { |
| 451 | const char *hdp= strchr(HEX_DIGITS, my_tolower(&my_charset_latin1, c)); |
| 452 | |
| 453 | if (!hdp) |
| 454 | { |
| 455 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 456 | "invalid character at pos %d." , |
| 457 | str_length, str, (int) (p - str))); |
| 458 | return false; |
| 459 | } |
| 460 | |
| 461 | if (chars_in_group >= 4) |
| 462 | { |
| 463 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 464 | "too many digits in group." , |
| 465 | str_length, str)); |
| 466 | return false; |
| 467 | } |
| 468 | |
| 469 | group_value <<= 4; |
| 470 | group_value |= hdp - HEX_DIGITS; |
| 471 | |
| 472 | DBUG_ASSERT(group_value <= 0xffff); |
| 473 | |
| 474 | ++chars_in_group; |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | if (chars_in_group > 0) |
| 479 | { |
| 480 | if (dst + 2 > ipv6_bytes_end) |
| 481 | { |
| 482 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 483 | "too many groups (2)." , str_length, str)); |
| 484 | return false; |
| 485 | } |
| 486 | |
| 487 | dst[0]= (unsigned char) (group_value >> 8) & 0xff; |
| 488 | dst[1]= (unsigned char) group_value & 0xff; |
| 489 | dst += 2; |
| 490 | } |
| 491 | |
| 492 | if (gap_ptr) |
| 493 | { |
| 494 | if (dst == ipv6_bytes_end) |
| 495 | { |
| 496 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 497 | "no room for a gap (::)." , str_length, str)); |
| 498 | return false; |
| 499 | } |
| 500 | |
| 501 | int bytes_to_move= (int)(dst - gap_ptr); |
| 502 | |
| 503 | for (int i= 1; i <= bytes_to_move; ++i) |
| 504 | { |
| 505 | ipv6_bytes_end[-i]= gap_ptr[bytes_to_move - i]; |
| 506 | gap_ptr[bytes_to_move - i]= 0; |
| 507 | } |
| 508 | |
| 509 | dst= ipv6_bytes_end; |
| 510 | } |
| 511 | |
| 512 | if (dst < ipv6_bytes_end) |
| 513 | { |
| 514 | DBUG_PRINT("error" , ("str_to_ipv6(%.*s): invalid IPv6 address: " |
| 515 | "too few groups." , str_length, str)); |
| 516 | return false; |
| 517 | } |
| 518 | |
| 519 | return true; |
| 520 | } |
| 521 | |
| 522 | /////////////////////////////////////////////////////////////////////////// |
| 523 | |
| 524 | /** |
| 525 | Converts IPv4-binary-address to a string. This function is a portable |
| 526 | alternative to inet_ntop(AF_INET). |
| 527 | |
| 528 | @param[in] ipv4 IPv4-address data (byte array) |
| 529 | @param[out] str A buffer to store string representation of IPv4-address. |
| 530 | It must be at least of INET_ADDRSTRLEN. |
| 531 | |
| 532 | @note The problem with inet_ntop() is that it is available starting from |
| 533 | Windows Vista, but the minimum supported version is Windows 2000. |
| 534 | */ |
| 535 | |
| 536 | static void ipv4_to_str(const in_addr *ipv4, char *str) |
| 537 | { |
| 538 | const unsigned char *ipv4_bytes= (const unsigned char *) ipv4; |
| 539 | |
| 540 | sprintf(str, "%d.%d.%d.%d" , |
| 541 | ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3]); |
| 542 | } |
| 543 | /////////////////////////////////////////////////////////////////////////// |
| 544 | |
| 545 | /** |
| 546 | Converts IPv6-binary-address to a string. This function is a portable |
| 547 | alternative to inet_ntop(AF_INET6). |
| 548 | |
| 549 | @param[in] ipv6 IPv6-address data (byte array) |
| 550 | @param[out] str A buffer to store string representation of IPv6-address. |
| 551 | It must be at least of INET6_ADDRSTRLEN. |
| 552 | |
| 553 | @note The problem with inet_ntop() is that it is available starting from |
| 554 | Windows Vista, but out the minimum supported version is Windows 2000. |
| 555 | */ |
| 556 | |
| 557 | static void ipv6_to_str(const in6_addr *ipv6, char *str) |
| 558 | { |
| 559 | struct Region |
| 560 | { |
| 561 | int pos; |
| 562 | int length; |
| 563 | }; |
| 564 | |
| 565 | const unsigned char *ipv6_bytes= (const unsigned char *) ipv6; |
| 566 | |
| 567 | // 1. Translate IPv6-address bytes to words. |
| 568 | // We can't just cast to short, because it's not guaranteed |
| 569 | // that sizeof (short) == 2. So, we have to make a copy. |
| 570 | |
| 571 | uint16 ipv6_words[IN6_ADDR_NUM_WORDS]; |
| 572 | |
| 573 | for (int i= 0; i < IN6_ADDR_NUM_WORDS; ++i) |
| 574 | ipv6_words[i]= (ipv6_bytes[2 * i] << 8) + ipv6_bytes[2 * i + 1]; |
| 575 | |
| 576 | // 2. Find "the gap" -- longest sequence of zeros in IPv6-address. |
| 577 | |
| 578 | Region gap= { -1, -1 }; |
| 579 | |
| 580 | { |
| 581 | Region rg= { -1, -1 }; |
| 582 | |
| 583 | for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i) |
| 584 | { |
| 585 | if (ipv6_words[i] != 0) |
| 586 | { |
| 587 | if (rg.pos >= 0) |
| 588 | { |
| 589 | if (rg.length > gap.length) |
| 590 | gap= rg; |
| 591 | |
| 592 | rg.pos= -1; |
| 593 | rg.length= -1; |
| 594 | } |
| 595 | } |
| 596 | else |
| 597 | { |
| 598 | if (rg.pos >= 0) |
| 599 | { |
| 600 | ++rg.length; |
| 601 | } |
| 602 | else |
| 603 | { |
| 604 | rg.pos= i; |
| 605 | rg.length= 1; |
| 606 | } |
| 607 | } |
| 608 | } |
| 609 | |
| 610 | if (rg.pos >= 0) |
| 611 | { |
| 612 | if (rg.length > gap.length) |
| 613 | gap= rg; |
| 614 | } |
| 615 | } |
| 616 | |
| 617 | // 3. Convert binary data to string. |
| 618 | |
| 619 | char *p= str; |
| 620 | |
| 621 | for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i) |
| 622 | { |
| 623 | if (i == gap.pos) |
| 624 | { |
| 625 | // We're at the gap position. We should put trailing ':' and jump to |
| 626 | // the end of the gap. |
| 627 | |
| 628 | if (i == 0) |
| 629 | { |
| 630 | // The gap starts from the beginning of the data -- leading ':' |
| 631 | // should be put additionally. |
| 632 | |
| 633 | *p= ':'; |
| 634 | ++p; |
| 635 | } |
| 636 | |
| 637 | *p= ':'; |
| 638 | ++p; |
| 639 | |
| 640 | i += gap.length - 1; |
| 641 | } |
| 642 | else if (i == 6 && gap.pos == 0 && |
| 643 | (gap.length == 6 || // IPv4-compatible |
| 644 | (gap.length == 5 && ipv6_words[5] == 0xffff) // IPv4-mapped |
| 645 | )) |
| 646 | { |
| 647 | // The data represents either IPv4-compatible or IPv4-mapped address. |
| 648 | // The IPv6-part (zeros or zeros + ffff) has been already put into |
| 649 | // the string (str). Now it's time to dump IPv4-part. |
| 650 | |
| 651 | ipv4_to_str((const in_addr *) (ipv6_bytes + 12), p); |
| 652 | return; |
| 653 | } |
| 654 | else |
| 655 | { |
| 656 | // Usual IPv6-address-field. Print it out using lower-case |
| 657 | // hex-letters without leading zeros (recommended IPv6-format). |
| 658 | // |
| 659 | // If it is not the last field, append closing ':'. |
| 660 | |
| 661 | p += sprintf(p, "%x" , ipv6_words[i]); |
| 662 | |
| 663 | if (i != IN6_ADDR_NUM_WORDS - 1) |
| 664 | { |
| 665 | *p= ':'; |
| 666 | ++p; |
| 667 | } |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | *p= 0; |
| 672 | } |
| 673 | |
| 674 | /////////////////////////////////////////////////////////////////////////// |
| 675 | |
| 676 | /** |
| 677 | Converts IP-address-string to IP-address-data. |
| 678 | |
| 679 | @param arg IP-address-string. |
| 680 | @param [out] buffer Buffer to store IP-address-data. |
| 681 | |
| 682 | @return Completion status. |
| 683 | @retval false Given string does not represent an IP-address. |
| 684 | @retval true The string has been converted sucessfully. |
| 685 | */ |
| 686 | |
| 687 | bool Item_func_inet6_aton::calc_value(const String *arg, String *buffer) |
| 688 | { |
| 689 | // ipv4-string -> varbinary(4) |
| 690 | // ipv6-string -> varbinary(16) |
| 691 | |
| 692 | in_addr ipv4_address; |
| 693 | in6_addr ipv6_address; |
| 694 | |
| 695 | if (str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address)) |
| 696 | { |
| 697 | buffer->length(0); |
| 698 | buffer->append((char *) &ipv4_address, sizeof (in_addr), &my_charset_bin); |
| 699 | |
| 700 | return true; |
| 701 | } |
| 702 | |
| 703 | if (str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address)) |
| 704 | { |
| 705 | buffer->length(0); |
| 706 | buffer->append((char *) &ipv6_address, sizeof (in6_addr), &my_charset_bin); |
| 707 | |
| 708 | return true; |
| 709 | } |
| 710 | |
| 711 | return false; |
| 712 | } |
| 713 | |
| 714 | /////////////////////////////////////////////////////////////////////////// |
| 715 | |
| 716 | /** |
| 717 | Converts IP-address-data to IP-address-string. |
| 718 | |
| 719 | @param arg IP-address-data. |
| 720 | @param [out] buffer Buffer to store IP-address-string. |
| 721 | |
| 722 | @return Completion status. |
| 723 | @retval false The argument does not correspond to IP-address. |
| 724 | @retval true The string has been converted sucessfully. |
| 725 | */ |
| 726 | |
| 727 | bool Item_func_inet6_ntoa::calc_value(const String *arg, String *buffer) |
| 728 | { |
| 729 | if (arg->charset() != &my_charset_bin) |
| 730 | return false; |
| 731 | |
| 732 | if ((int) arg->length() == IN_ADDR_SIZE) |
| 733 | { |
| 734 | char str[INET_ADDRSTRLEN]; |
| 735 | |
| 736 | ipv4_to_str((const in_addr *) arg->ptr(), str); |
| 737 | |
| 738 | buffer->length(0); |
| 739 | buffer->append(str, (uint32) strlen(str), &my_charset_latin1); |
| 740 | |
| 741 | return true; |
| 742 | } |
| 743 | else if ((int) arg->length() == IN6_ADDR_SIZE) |
| 744 | { |
| 745 | char str[INET6_ADDRSTRLEN]; |
| 746 | |
| 747 | ipv6_to_str((const in6_addr *) arg->ptr(), str); |
| 748 | |
| 749 | buffer->length(0); |
| 750 | buffer->append(str, (uint32) strlen(str), &my_charset_latin1); |
| 751 | |
| 752 | return true; |
| 753 | } |
| 754 | |
| 755 | DBUG_PRINT("info" , |
| 756 | ("INET6_NTOA(): varbinary(4) or varbinary(16) expected." )); |
| 757 | return false; |
| 758 | } |
| 759 | |
| 760 | /////////////////////////////////////////////////////////////////////////// |
| 761 | |
| 762 | /** |
| 763 | Checks if the passed string represents an IPv4-address. |
| 764 | |
| 765 | @param arg The string to check. |
| 766 | |
| 767 | @return Check status. |
| 768 | @retval false The passed string does not represent an IPv4-address. |
| 769 | @retval true The passed string represents an IPv4-address. |
| 770 | */ |
| 771 | |
| 772 | bool Item_func_is_ipv4::calc_value(const String *arg) |
| 773 | { |
| 774 | in_addr ipv4_address; |
| 775 | |
| 776 | return str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address); |
| 777 | } |
| 778 | |
| 779 | /////////////////////////////////////////////////////////////////////////// |
| 780 | |
| 781 | /** |
| 782 | Checks if the passed string represents an IPv6-address. |
| 783 | |
| 784 | @param arg The string to check. |
| 785 | |
| 786 | @return Check status. |
| 787 | @retval false The passed string does not represent an IPv6-address. |
| 788 | @retval true The passed string represents an IPv6-address. |
| 789 | */ |
| 790 | |
| 791 | bool Item_func_is_ipv6::calc_value(const String *arg) |
| 792 | { |
| 793 | in6_addr ipv6_address; |
| 794 | |
| 795 | return str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address); |
| 796 | } |
| 797 | |
| 798 | /////////////////////////////////////////////////////////////////////////// |
| 799 | |
| 800 | /** |
| 801 | Checks if the passed IPv6-address is an IPv4-compat IPv6-address. |
| 802 | |
| 803 | @param arg The IPv6-address to check. |
| 804 | |
| 805 | @return Check status. |
| 806 | @retval false The passed IPv6-address is not an IPv4-compatible IPv6-address. |
| 807 | @retval true The passed IPv6-address is an IPv4-compatible IPv6-address. |
| 808 | */ |
| 809 | |
| 810 | bool Item_func_is_ipv4_compat::calc_value(const String *arg) |
| 811 | { |
| 812 | if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin) |
| 813 | return false; |
| 814 | |
| 815 | return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) arg->ptr()); |
| 816 | } |
| 817 | |
| 818 | /////////////////////////////////////////////////////////////////////////// |
| 819 | |
| 820 | /** |
| 821 | Checks if the passed IPv6-address is an IPv4-mapped IPv6-address. |
| 822 | |
| 823 | @param arg The IPv6-address to check. |
| 824 | |
| 825 | @return Check status. |
| 826 | @retval false The passed IPv6-address is not an IPv4-mapped IPv6-address. |
| 827 | @retval true The passed IPv6-address is an IPv4-mapped IPv6-address. |
| 828 | */ |
| 829 | |
| 830 | bool Item_func_is_ipv4_mapped::calc_value(const String *arg) |
| 831 | { |
| 832 | if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin) |
| 833 | return false; |
| 834 | |
| 835 | return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) arg->ptr()); |
| 836 | } |
| 837 | |