Net-IP-XS-0.22/0000755000175000017500000000000014363577433011540 5ustar tomhtomhNet-IP-XS-0.22/inet_pton.c0000644000175000017500000001603714363576625013714 0ustar tomhtomh/* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form Modifications to inet_pton6 allowing IPv4 addresses to appear throughout, and miscellaneous modifications to inet_pton4, by Tom Harrison (Copyright (C) 2010). Copyright (C) 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Copyright (c) 1996,1999 by Internet Software Consortium. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #define NS_INADDRSZ 4 #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 int inet_pton4 (const char *src, unsigned char *dst); int inet_pton6 (const char *src, unsigned char *dst); /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal, octal (with the * exception of 0) and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. * (Some modifications by Tom Harrison, 2010.) */ int inet_pton4 (const char *src, unsigned char *dst) { int saw_digit, octets, ch; unsigned char tmp[NS_INADDRSZ], *tp; memset(tmp, 0, NS_INADDRSZ); saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { if (ch >= '0' && ch <= '9') { unsigned n = *tp * 10 + (ch - '0'); if (saw_digit && *tp == 0) { return 0; } if (n > 255) { return 0; } *tp = n; if (!saw_digit) { ++octets; saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) { return 0; } ++tp; saw_digit = 0; } else { return 0; } } memcpy(dst, tmp, NS_INADDRSZ); return 1; } /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid IPv6 address (may contain IPv4 addresses * in any position), else 0. * notice: * (1) does not touch `dst' unless it's returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. * (Modifications allowing IPv4 addresses to appear throughout * by Tom Harrison, 2010.) */ int inet_pton6 (const char *src, unsigned char *dst) { static const char xdigits[] = "0123456789abcdef"; unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; const char *ipv4_endp; char ipv4[16]; int diff; const char *curtok; int ch, saw_xdigit; unsigned val; tp = (unsigned char *) memset (tmp, '\0', NS_IN6ADDRSZ); endp = tp + NS_IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') { if (*++src != ':') { return 0; } } curtok = src; saw_xdigit = 0; val = 0; while ((ch = tolower (*src++)) != '\0') { const char *pch; pch = strchr (xdigits, ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (val > 0xffff) { return 0; } saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) { return 0; } colonp = tp; continue; } else if (*src == '\0') { return 0; } if (tp + NS_INT16SZ > endp) { return 0; } *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp)) { /* Find the next : from curtok, copy from curtok to that * point to ipv4, if it's IPv4 all is good. If the : is not found, * it terminates, so check it directly. */ ipv4_endp = strchr(curtok, ':'); if (ipv4_endp) { diff = ipv4_endp - curtok; if (diff > 15) { return 0; } memcpy(ipv4, curtok, diff); ipv4[diff] = '\0'; diff = inet_pton4(ipv4, tp); } else { diff = inet_pton4(curtok, tp); } if (diff) { val = (tp[2] << 8) | tp[3]; tp += 2; saw_xdigit=1; if (ipv4_endp) { src = ipv4_endp; continue; } else { saw_xdigit=0; tp += 2; break; } } } return 0; } if (saw_xdigit) { if (tp + NS_INT16SZ > endp) { return 0; } *tp++ = (unsigned char) (val >> 8) & 0xff; *tp++ = (unsigned char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; int i; if (tp == endp) { return 0; } for (i = 1; i <= n; i++) { endp[-i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } while (tp < endp) { *(tp++) = 0; } memcpy(dst, tmp, NS_IN6ADDRSZ); return 1; } Net-IP-XS-0.22/META.json0000644000175000017500000000217514363577433013166 0ustar tomhtomh{ "abstract" : "IPv4/IPv6 address library", "author" : [ "Tom Harrison " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010", "license" : [ "open_source" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Net-IP-XS", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "IO::Capture::Stderr" : "0", "Math::BigInt" : "0", "Test::More" : "0", "Tie::Simple" : "0" } } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "https://github.com/tomhrr/p5-Net-IP-XS" } }, "version" : "0.22", "x_serialization_backend" : "JSON::PP version 4.06" } Net-IP-XS-0.22/Makefile.PL0000644000175000017500000000146214363576625013517 0ustar tomhtomhuse strict; use warnings; use ExtUtils::MakeMaker; use ExtUtils::MakeMaker::Config; my $output = WriteMakefile( NAME => 'Net::IP::XS', AUTHOR => 'Tom Harrison ', LICENSE => 'gpl', VERSION_FROM => 'lib/Net/IP/XS.pm', ABSTRACT_FROM => 'lib/Net/IP/XS.pm', META_MERGE => { resources => { repository => 'https://github.com/tomhrr/p5-Net-IP-XS' } }, PERL_MALLOC_OK => 1, OBJECT => '$(O_FILES)', clean => { FILES => '$(O_FILES)' }, PREREQ_PM => { 'Tie::Simple' => 0, 'Math::BigInt' => 0, # Test prereqs. 'Test::More' => 0, 'IO::Capture::Stderr' => 0 }, ); 1; Net-IP-XS-0.22/LICENSE0000644000175000017500000004325414363576625012557 0ustar tomhtomh GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. Net-IP-XS-0.22/README0000644000175000017500000000307114363577176012425 0ustar tomhtomhNet-IP-XS - IPv4/IPv6 address library INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install SUPPORT AND DOCUMENTATION After installing, you can find documentation for this module with the perldoc command. perldoc Net::IP::XS You can also look for information at: RT, CPAN's request tracker http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-IP-XS AnnoCPAN, Annotated CPAN documentation http://annocpan.org/dist/Net-IP-XS CPAN Ratings http://cpanratings.perl.org/d/Net-IP-XS Search CPAN http://search.cpan.org/dist/Net-IP-XS COPYRIGHT AND LICENCE Copyright (C) 2010-2023 Tom Harrison . Original inet_pton4 and inet_pton6 functions are copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Net-IP-XS-0.22/META.yml0000644000175000017500000000125514363577433013014 0ustar tomhtomh--- abstract: 'IPv4/IPv6 address library' author: - 'Tom Harrison ' build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.62, CPAN::Meta::Converter version 2.150010' license: open_source meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Net-IP-XS no_index: directory: - t - inc requires: IO::Capture::Stderr: '0' Math::BigInt: '0' Test::More: '0' Tie::Simple: '0' resources: repository: https://github.com/tomhrr/p5-Net-IP-XS version: '0.22' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Net-IP-XS-0.22/n128.c0000644000175000017500000004270214363576625012403 0ustar tomhtomh/* n128.c - 128-bit integer. Copyright (C) 2012-2014 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include "n128.h" #ifdef __cplusplus extern "C" { #endif static char *power_strings[] = { "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", "65536", "131072", "262144", "524288", "1048576", "2097152", "4194304", "8388608", "16777216", "33554432", "67108864", "134217728", "268435456", "536870912", "1073741824", "2147483648", "4294967296", "8589934592", "17179869184", "34359738368", "68719476736", "137438953472", "274877906944", "549755813888", "1099511627776", "2199023255552", "4398046511104", "8796093022208", "17592186044416", "35184372088832", "70368744177664", "140737488355328", "281474976710656", "562949953421312", "1125899906842624", "2251799813685248", "4503599627370496", "9007199254740992", "18014398509481984", "36028797018963968", "72057594037927936", "144115188075855872", "288230376151711744", "576460752303423488", "1152921504606846976", "2305843009213693952", "4611686018427387904", "9223372036854775808", "18446744073709551616", "36893488147419103232", "73786976294838206464", "147573952589676412928", "295147905179352825856", "590295810358705651712", "1180591620717411303424", "2361183241434822606848", "4722366482869645213696", "9444732965739290427392", "18889465931478580854784", "37778931862957161709568", "75557863725914323419136", "151115727451828646838272", "302231454903657293676544", "604462909807314587353088", "1208925819614629174706176", "2417851639229258349412352", "4835703278458516698824704", "9671406556917033397649408", "19342813113834066795298816", "38685626227668133590597632", "77371252455336267181195264", "154742504910672534362390528", "309485009821345068724781056", "618970019642690137449562112", "1237940039285380274899124224", "2475880078570760549798248448", "4951760157141521099596496896", "9903520314283042199192993792", "19807040628566084398385987584", "39614081257132168796771975168", "79228162514264337593543950336", "158456325028528675187087900672", "316912650057057350374175801344", "633825300114114700748351602688", "1267650600228229401496703205376", "2535301200456458802993406410752", "5070602400912917605986812821504", "10141204801825835211973625643008", "20282409603651670423947251286016", "40564819207303340847894502572032", "81129638414606681695789005144064", "162259276829213363391578010288128", "324518553658426726783156020576256", "649037107316853453566312041152512", "1298074214633706907132624082305024", "2596148429267413814265248164610048", "5192296858534827628530496329220096", "10384593717069655257060992658440192", "20769187434139310514121985316880384", "41538374868278621028243970633760768", "83076749736557242056487941267521536", "166153499473114484112975882535043072", "332306998946228968225951765070086144", "664613997892457936451903530140172288", "1329227995784915872903807060280344576", "2658455991569831745807614120560689152", "5316911983139663491615228241121378304", "10633823966279326983230456482242756608", "21267647932558653966460912964485513216", "42535295865117307932921825928971026432", "85070591730234615865843651857942052864", "170141183460469231731687303715884105728", "340282366920938463463374607431768211456", }; /* * n128_set(): copy N128 value from other N128 value. * @dst: destination N128 object. * @src: source N128 object. */ void n128_set(n128_t *dst, n128_t *src) { memcpy(dst, src, sizeof(*dst)); return; } /** * n128_set_ui(): set N128 value based on unsigned integer. * @n: the N128 object to be set. * @ui: the value to set. */ void n128_set_ui(n128_t *n, unsigned int ui) { n->nums[0] = 0; n->nums[1] = 0; n->nums[2] = 0; n->nums[3] = ui; return; } /** * n128_cmp_ui(): compare N128 value against unsigned integer. * @n: the N128 object. * @ui: unsigned integer. * * Returns 1 if @n is more than @ui, 0 if @n is equal to @ui and -1 if * @n is less than @ui. */ int n128_cmp_ui(n128_t *n, unsigned int ui) { return (n->nums[0] || n->nums[1] || n->nums[2] || (n->nums[3] > ui)) ? 1 : (n->nums[3] == ui) ? 0 : -1; } /** * n128_blsft(): left-shift (circular) N128 value. * @n: the N128 object to shift. * @sft: the number of places by which the object should be shifted. */ void n128_blsft(n128_t *n, int sft) { n128_t copy; int i; uint32_t mask; int diff; diff = sft - 31; if (diff >= 0) { sft = 31; } for (i = 0; i < 4; ++i) { copy.nums[i] = n->nums[i]; } for (i = 0; i < 4; ++i) { n->nums[i] <<= sft; } for (i = 0; i < 4; ++i) { mask = ((1 << sft) - 1) << (32 - sft); mask &= copy.nums[(i + 1) % 4]; mask >>= (32 - sft); n->nums[i] |= mask; } if (diff >= 0) { n128_blsft(n, diff); } return; } /** * n128_brsft(): right-shift (circular) N128 value. * @n: the N128 object to shift. * @sft: the number of places by which the object should be shifted. */ void n128_brsft(n128_t *n, int sft) { n128_t copy; int i; uint32_t mask; int diff; diff = sft - 31; if (diff >= 0) { sft = 31; } for (i = 0; i < 4; ++i) { copy.nums[i] = n->nums[i]; } for (i = 0; i < 4; ++i) { n->nums[i] >>= sft; } for (i = 0; i < 4; ++i) { mask = ((1 << sft) - 1); mask &= copy.nums[(i + 3) % 4]; mask <<= (32 - sft); n->nums[i] |= mask; } if (diff >= 0) { n128_brsft(n, diff); } return; } /** * n128_and(): bitwise AND two N128 values. * @n1: first N128 object. * @n2: second N128 object. * * The result is stored in the first argument. */ void n128_and(n128_t *n1, n128_t *n2) { int i; for (i = 0; i < 4; i++) { n1->nums[i] &= n2->nums[i]; } return; } /** * n128_ior(): bitwise OR two N128 values. * @n1: first N128 object. * @n2: second N128 object. * * The result is stored in the first argument. */ void n128_ior(n128_t *n1, n128_t *n2) { int i; for (i = 0; i < 4; i++) { n1->nums[i] |= n2->nums[i]; } return; } /** * n128_xor(): bitwise XOR two N128 values. * @n1: first N128 object. * @n2: second N128 object. * * The result is stored in the first argument. */ void n128_xor(n128_t *n1, n128_t *n2) { int i; for (i = 0; i < 4; i++) { n1->nums[i] ^= n2->nums[i]; } return; } /** * n128_add(): add two N128 values. * @n1: first N128 object. * @n2: second N128 object. * * The result is stored in the first argument. Overflow is as per * an unsigned integer. */ int n128_add(n128_t *n1, n128_t *n2) { int i; int j; for (i = 0; i < 4; i++) { n1->nums[i] += n2->nums[i]; } for (i = 1; i < 4; i++) { j = i - 1; if (n1->nums[i] < n2->nums[i]) { n1->nums[j]++; while (n1->nums[j] == 0 && j--) { n1->nums[j]++; } } } return 1; } /** * n128_com(): take the complement of an N128 value. * @n: N128 object. * * The result is stored in the argument. */ void n128_com(n128_t *n) { int i; for (i = 0; i < 4; i++) { n->nums[i] = ~(n->nums[i]); } return; } /** * n128_add_ui(): add an unsigned integer value to an N128 value. * @n: N128 object. * * The result is stored in the first argument. Overflow is as per an * unsigned integer. */ int n128_add_ui(n128_t *n, unsigned int ui) { n128_t n2; n128_set_ui(&n2, ui); n128_add(n, &n2); return 1; } /** * n128_sub(): subtract an N128 value from another. * @n1: N128 object (minuend). * @n2: N128 object (subtrahend). * * The result is stored in the first argument. Overflow is not * handled: if @n2 is greater than @n1, the result will be zero. */ int n128_sub(n128_t *n1, n128_t *n2) { int res; n128_t n2c; n128_t *n2cp = &n2c; res = n128_cmp(n1, n2); if (res < 0) { return 0; } if (res == 0) { n128_set_ui(n1, 0); return 1; } n128_set(n2cp, n2); n128_com(n2cp); n128_add_ui(n2cp, 1); n128_add(n1, n2cp); return 1; } /** * n128_tstbit(): test whether a bit is set in an N128 value. * @n: N128 object. * @bit: the bit to test. * * Returns 1 if the bit is set, and zero if it is not. Bits begin at * zero and are ordered from least to most significant. */ int n128_tstbit(n128_t *n, int bit) { return (n->nums[3 - (bit / 32)] >> (bit % 32)) & 1; } /** * n128_setbit(): set a particular bit in an N128 value. * @n: N128 object. * @bit: the bit to set. * * See n128_tstbit(). */ void n128_setbit(n128_t *n, int bit) { n->nums[3 - (bit / 32)] |= (1 << (bit % 32)); } /** * n128_clrbit(): clear a particular bit in an N128 value. * @n: N128 object. * @bit: the bit to clear. * * See n128_tstbit(). */ void n128_clrbit(n128_t *n, int bit) { n->nums[3 - (bit / 32)] &= ~(1 << (bit % 32)); } /** * n128_cmp(): compare N128 value against another. * @n1: first N128 object. * @n2: second N128 object. * * Returns 1 if @n1 is more than @n2, 0 if @n1 is equal to @n2 and -1 if * @n1 is less than @n2. */ int n128_cmp(n128_t *n1, n128_t *n2) { return (n1->nums[0] > n2->nums[0]) ? 1 : (n1->nums[0] < n2->nums[0]) ? -1 : (n1->nums[1] > n2->nums[1]) ? 1 : (n1->nums[1] < n2->nums[1]) ? -1 : (n1->nums[2] > n2->nums[2]) ? 1 : (n1->nums[2] < n2->nums[2]) ? -1 : (n1->nums[3] > n2->nums[3]) ? 1 : (n1->nums[3] < n2->nums[3]) ? -1 : 0; } /** * n128_set_str_binary(): set N128 value based on bitstring. * @n: destination N128 object. * @bitstr: the bitstring. * @len: the length of the bitstring. * * The bitstring's bits must be ordered from most to * least significant. Any character in the bitstring that is not the * character '0' will be treated as the character '1'. */ void n128_set_str_binary(n128_t *n, const char *bitstr, int len) { int i; int j; int mylen; memset(n, 0, 4 * sizeof(uint32_t)); mylen = (len > 128) ? 128 : len; if (mylen < 128) { for (i = 0; i < (128 - mylen); i++) { n128_clrbit(n, (127 - i)); } } else { i = 0; } for (j = 0; i < 128; i++, j++) { if (bitstr[j] != '0') { n128_setbit(n, (127 - i)); } } return; } static void str_subtract(char *buf, int buflen, char *operand, int oplen) { int i; int j; int carry = 0; int diff; for (i = buflen - 1, j = oplen - 1; i >= 0 && j >= 0; --i, --j) { diff = (buf[i] - (operand[j] + carry)); if (diff >= 0) { buf[i] = diff + '0'; carry = 0; } else { buf[i] = diff + '0' + 10; carry = 1; } } if (carry == 1) { buf[i]--; } return; } /** * n128_set_str_decimal(): set N128 value based on decimal string. * @n: destination N128 object. * @bitstr: the decimal string. * @len: the length of the decimal string. */ int n128_set_str_decimal(n128_t *n, const char *str, int len) { int i; char *ps; int ps_len; char buf[40]; char *bufp; if (len > 39) { return 0; } bufp = buf; strncpy(bufp, str, len); buf[len] = '\0'; n128_set_ui(n, 0); for (i = 0; i < len; i++) { if (!isdigit(str[i])) { return 0; } } if (power_strings[127][0] > str[0]) { return 0; } for (i = 127; i >= 0; i--) { if (!len) { break; } ps = power_strings[i]; ps_len = strlen(ps); if (ps_len > len) { continue; } else { if (ps_len == len) { if (strcmp(bufp, ps) < 0) { continue; } } str_subtract(bufp, len, ps, ps_len); while (*bufp == '0') { ++bufp; --len; } n128_setbit(n, i); } } if (len) { return 0; } return 1; } /** * n128_scan0(): return the index of the least significant cleared bit. * @n: N128 object. */ int n128_scan0(n128_t *n) { int i; for (i = 0; i < 128; i++) { if (!n128_tstbit(n, i)) { return i; } } return INT_MAX; } /** * n128_scan1(): return the index of the least significant set bit. * @n: N128 object. */ int n128_scan1(n128_t *n) { int i; for (i = 0; i < 128; i++) { if (n128_tstbit(n, i)) { return i; } } return INT_MAX; } /** * n128_rscan1(): return the index of the most significant set bit. * @n: N128 object. */ static int n128_rscan1(n128_t *n) { int i; for (i = 127; i >= 0; i--) { if (n128_tstbit(n, i)) { return i; } } return INT_MAX; } /** * n128_print_bin(): write an N128 value as a bitstring. * @n: N128 object. * @buf: bitstring buffer. * @ui_only: a boolean indicating whether only the first 32 bits * should be written. * * The buffer is null-terminated on completion, so it must have either * 129 or 33 characters' capacity, depending on the value of @ui_only. */ void n128_print_bin(n128_t *n, char *buf, int ui_only) { int i; int j; j = (ui_only) ? 0 : 3; for (; j >= 0; j--) { for (i = 31; i >= 0; i--) { *buf = (n128_tstbit(n, (j * 32) + i) ? '1' : '0'); ++buf; } } *buf = '\0'; return; } /** * n128_print_hex(): write an N128 value as a hexadecimal string. * @n: N128 object. * @buf: hexadecimal string buffer. * * The buffer is null-terminated on completion. It must have at least * 33 characters' capacity to handle all possible cases. */ void n128_print_hex(n128_t *n, char *buf) { int byte; int i; static const char *lookup = "0123456789abcdef"; for (i = 0; i < 16; i++) { byte = (n->nums[i / 4] >> ((3 - (i % 4)) * 8)) & 0xFF; if (byte) { break; } } *buf++ = '0'; *buf++ = 'x'; if (i == 16) { *buf++ = '0'; } else { for (; i < 16; i++) { byte = (n->nums[i / 4] >> ((3 - (i % 4)) * 8)) & 0xFF; *buf++ = lookup[(byte >> 4) & 0xF]; *buf++ = lookup[byte & 0xF]; } } *buf = '\0'; return; } static int n128_divmod_10(n128_t *n, n128_t *qp, n128_t *rp) { n128_t ten; n128_t *tenp; n128_t t; n128_t *tp; n128_t na; n128_t *np; int shift; int shift1; int shift2; tenp = &ten; tp = &t; np = &na; n128_set(np, n); n128_set_ui(qp, 0); n128_set_ui(tenp, 10); shift1 = n128_rscan1(np); shift2 = n128_rscan1(tenp); shift = shift1 - shift2; if (shift < 0) { /* Divisor is larger than dividend. */ n128_set_ui(qp, 0); n128_set(rp, np); return 1; } n128_blsft(tenp, shift); while (1) { n128_set(tp, np); if (n128_cmp(tp, tenp) >= 0) { n128_sub(tp, tenp); n128_setbit(qp, 0); n128_set(np, tp); } if (n128_cmp_ui(tenp, 10) == 0) { n128_set(rp, np); return 1; } n128_brsft(tenp, 1); n128_blsft(qp, 1); } } /** * n128_print_dec(): write an N128 value as a decimal string. * @n: N128 object. * @buf: decimal string buffer. * * The buffer is null-terminated on completion. It must have at least * 40 characters' capacity to handle all possible cases. */ void n128_print_dec(n128_t *n, char *buf) { int i = 0; int nums[50]; int nc = 0; n128_t na; n128_t *np = &na; n128_t q; n128_t *qp = &q; n128_t r; n128_t *rp = &r; n128_set(np, n); n128_set(qp, np); if (n128_cmp_ui(qp, 0) == 0) { *buf++ = '0'; *buf = '\0'; return; } while (n128_cmp_ui(qp, 0) != 0) { n128_set(np, qp); n128_divmod_10(np, qp, rp); nums[nc++] = rp->nums[3]; } --nc; for (i = nc; i >= 0; i--) { *buf++ = '0' + nums[i]; } *buf = '\0'; return; } #ifdef __cplusplus } #endif Net-IP-XS-0.22/t/0000755000175000017500000000000014363577433012003 5ustar tomhtomhNet-IP-XS-0.22/t/51-rt-102155-2.t0000644000175000017500000000075014363576625013736 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Config; use if $Config{'useithreads'}, 'threads'; use Test::More; BEGIN { if (not $Config{'useithreads'}) { plan skip_all => "Perl not compiled with 'useithreads'"; } else { plan tests => 1; } }; use Net::IP::XS; my $i = Net::IP::XS::N128::new('10000'); for my $j (1..10) { async {}; } for my $thread (threads->list()) { $thread->join(); } ok(1, "Completed thread test successfully"); 1; Net-IP-XS-0.22/t/54-rt-122438.t0000644000175000017500000000041414363576625013605 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 2; use Net::IP::XS; my $ip = Net::IP::XS->new('1.0.0.0-1.255.255.255'); ok($ip, 'Got new object'); is($ip->last_bin(), '00000001111111111111111111111111', 'Got correct last_bin value'); 1; Net-IP-XS-0.22/t/48-rt-73232.t0000644000175000017500000000052414363576625013527 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Net::IP::XS; use Test::More tests => 3; my $ip = Net::IP::XS->new('1:2:3:4:5:6:7'); ok((not $ip), 'Got no object where IPv6 address too short'); is($Net::IP::XS::ERROR, 'Invalid number of octets 1:2:3:4:5:6:7', 'Correct error'); is($Net::IP::XS::ERRNO, 112, 'Correct errno'); 1; Net-IP-XS-0.22/t/50-rt-102155.t0000644000175000017500000000074014363576625013575 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Config; use if $Config{'useithreads'}, 'threads'; use Test::More; BEGIN { if (not $Config{'useithreads'}) { plan skip_all => "Perl not compiled with 'useithreads'"; } else { plan tests => 1; } }; use Net::IP::XS; my $i = Net::IP::XS->new('::1'); for my $j (1..10) { async {}; } for my $thread (threads->list()) { $thread->join(); } ok(1, "Completed thread test successfully"); 1; Net-IP-XS-0.22/t/19-get-mask.t0000644000175000017500000000153414363576625014134 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 12; use Net::IP::XS qw(ip_get_mask Error Errno); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); $c->start(); my $res = ip_get_mask(undef, 0); $c->stop(); is($res, undef, 'Got undef on no version'); is(Error(), "Cannot determine IP version", 'Got correct error'); is(Errno(), 101, 'Got correct errno'); my @data = ( [ -10, 4, '0' x 32 ], [ 0, 4, '0' x 32 ], [ 1, 4, '1'.('0' x 31) ], [ 32, 4, '1' x 32 ], [ 50, 4, '1' x 32 ], [ 16, 4, ('1' x 16).('0' x 16) ], [ 0, 6, ('0' x 128) ], [ 32, 6, ('1' x 32).('0' x 96) ], [ 150, 6, ('1' x 128) ], ); for (@data) { my ($length, $version, $res_exp) = @{$_}; my $res = ip_get_mask($length, $version); is($res, $res_exp, "$length - $version"); } 1; Net-IP-XS-0.22/t/49-rt-92211.t0000644000175000017500000000057614363576625013535 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Net::IP::XS; use Test::More tests => 2; my $ip = Net::IP::XS->new('2001:db8:3:4:5:6:7::/112'); ok($ip, 'IPv6 address is considered valid'); if (not $ip) { diag $Net::IP::XS::ERROR; diag $Net::IP::XS::ERRNO; } is($ip->prefix(), '2001:0db8:0003:0004:0005:0006:0007:0000/112', 'IPv6 object stringified correctly'); 1; Net-IP-XS-0.22/t/40-objects.t0000644000175000017500000004007714363576625014054 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Net::IP::XS qw($IP_NO_OVERLAP $IP_PARTIAL_OVERLAP $IP_A_IN_B_OVERLAP $IP_B_IN_A_OVERLAP $IP_IDENTICAL); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); use Test::More tests => 154; use Scalar::Util qw(blessed); my $ip = Net::IP::XS->new('1.2.3.4', 4); ok((blessed $ip and $ip->isa('Net::IP::XS')), 'IP object is blessed and has correct package'); # Make sure that deleting the unsigned ints storing the start # and end addresses doesn't cause segfaults. delete $ip->{'xs_v4_ip0'}; delete $ip->{'xs_v4_ip1'}; my $res = $ip->size(); $ip = Net::IP::XS->new('1.2.3.4', 4); ok((blessed $ip and $ip->isa('Net::IP::XS')), 'IP object is blessed and has correct package'); $ip = Net::IP::XS->new('ZXCV', 4); ok((not $ip), 'Got no object on bad arguments'); $ip = Net::IP::XS->new('2.0.0.0 - 1.0.0.0', 4); ok((not $ip), 'Got no object where start address is more than end address'); is($Net::IP::XS::ERROR, 'Begin address is greater than End address 2.0.0.0 - 1.0.0.0', 'Correct error'); is($Net::IP::XS::ERRNO, 202, 'Correct errno'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); ok((blessed $ip and $ip->isa('Net::IP::XS')), 'IP object is blessed and has correct package'); is($ip->intip(), 16777216, 'Got correct intip for IPv4 range'); is($ip->binip(), '00000001000000000000000000000000', 'binip is correct'); is($ip->prefixlen(), 8, 'prefixlen is correct'); ok($ip->is_prefix(), 'is_prefix is correct'); is($ip->ip(), '1.0.0.0', 'ip is correct'); is($ip->version(), 4, 'ipversion is correct'); is($ip->binmask(), '11111111000000000000000000000000', 'binmask is correct'); is($ip->last_bin(), '00000001111111111111111111111111', 'last_bin is correct'); is($ip->print(), '1/8', 'Got correct printed value'); is($ip->size(), 16777216, 'Got correct size'); is($ip->intip(), '16777216', 'Got correct intip for IPv4 range'); is($ip->hexip(), '0x1000000', 'Got correct hexip for IPv4 range'); is($ip->hexmask(), '0xff000000', 'Got correct hexmask for IPv4 range'); is($ip->prefix(), '1.0.0.0/8', 'Got correct prefix for IPv4 range'); is($ip->mask(), '255.0.0.0', 'Got correct mask for IPv4 range'); is($ip->iptype(), 'PUBLIC', 'Got correct iptype for IPv4 range'); is($ip->reverse_ip(), '1.in-addr.arpa.', 'Got correct reverse_ip for IPv4 range'); is($ip->last_bin(), ('0' x 7).('1' x 25), 'Got correct last_bin for IPv4 range'); is($ip->last_int(), 0x1FFFFFF, 'Got correct last_int for IPv4 range'); is($ip->last_ip(), '1.255.255.255', 'Got correct last_ip for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->intip(), 16777216, 'Got correct intip for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->binip(), '00000001000000000000000000000000', 'binip is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->prefixlen(), 8, 'prefixlen is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); ok($ip->is_prefix(), 'is_prefix is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->ip(), '1.0.0.0', 'ip is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->version(), 4, 'ipversion is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->binmask(), '11111111000000000000000000000000', 'binmask is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->last_bin(), '00000001111111111111111111111111', 'last_bin is correct'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->print(), '1/8', 'Got correct printed value'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->size(), 16777216, 'Got correct size'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->intip(), '16777216', 'Got correct intip for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->hexip(), '0x1000000', 'Got correct hexip for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->hexmask(), '0xff000000', 'Got correct hexmask for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->prefix(), '1.0.0.0/8', 'Got correct prefix for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->mask(), '255.0.0.0', 'Got correct mask for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->iptype(), 'PUBLIC', 'Got correct iptype for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->reverse_ip(), '1.in-addr.arpa.', 'Got correct reverse_ip for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->last_bin(), ('0' x 7).('1' x 25), 'Got correct last_bin for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->last_int(), 0x1FFFFFF, 'Got correct last_int for IPv4 range'); $ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255', 4); is($ip->last_ip(), '1.255.255.255', 'Got correct last_ip for IPv4 range'); my $ip2 = Net::IP::XS->new('2/8'); ok($ip->bincomp('lt', $ip2), "Range's first address is smaller than other range's"); my $ip3 = $ip->binadd($ip2); ok($ip3, 'Got result on binadd of two IP addresses'); is($ip3->ip(), '3.0.0.0', 'Newly created IP is correct'); my $agg1_ip = Net::IP::XS->new('0.0.0.0 - 0.255.255.255'); my $agg2_ip = Net::IP::XS->new('1.0.0.0 - 1.255.255.255'); my $agg3_ip = $agg1_ip->aggregate($agg2_ip); ok($agg3_ip, 'Got result on aggregate of two IP ranges'); is($agg3_ip->ip(), '0.0.0.0', 'Starting IP is correct'); is($agg3_ip->last_ip(), '1.255.255.255', 'Ending IP is correct'); is($agg3_ip->prefixlen(), 7, 'Ending IP is correct'); is($agg1_ip->overlaps($agg2_ip), $IP_NO_OVERLAP, 'Ranges do not overlap'); is($agg2_ip->overlaps($agg1_ip), $IP_NO_OVERLAP, 'Ranges do not overlap (2)'); is($agg1_ip->overlaps($agg3_ip), $IP_A_IN_B_OVERLAP, 'Range A is in range B'); is($agg3_ip->overlaps($agg1_ip), $IP_B_IN_A_OVERLAP, 'Range B is in range A'); is($agg3_ip->overlaps($agg3_ip), $IP_IDENTICAL, 'Range is identical to range'); $agg1_ip = Net::IP::XS->new('0.0.0.0 - 0.255.255.255'); $agg2_ip = Net::IP::XS->new('2.0.0.0 - 1.255.255.255'); $agg3_ip = $agg1_ip->aggregate($agg2_ip); is($agg3_ip, undef, 'aggregate failed'); $ip = Net::IP::XS->new('0000:0000:0000:0000:0000:0000:0000:0000 - FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 6); ok((blessed $ip and $ip->isa('Net::IP::XS')), 'IP object is blessed and has correct package'); is($ip->size(), '340282366920938463463374607431768211456', 'Got correct size for IPv6'); is($ip->size(), '340282366920938463463374607431768211456', 'Got correct size for IPv6 (2)'); $ip = Net::IP::XS->new('0000:: - FFFF::', 6); ok((blessed $ip and $ip->isa('Net::IP::XS')), 'IP object is blessed and has correct package'); $ip = Net::IP::XS->new('::/0', 6); ok((blessed $ip and $ip->isa('Net::IP::XS')), 'IP object is blessed and has correct package'); $ip = Net::IP::XS->new('::/0', 6); is($ip->size(), '340282366920938463463374607431768211456', 'Got correct size for IPv6'); is($ip->intip(), 0, 'Got correct intip for IPv6 range'); is($ip->hexip(), '0x0', 'Got correct hexip for IPv6 range'); is($ip->hexmask(), '0x0', 'Got correct hexmask for IPv6 range'); is($ip->prefix(), '0000:0000:0000:0000:0000:0000:0000:0000/0', 'Got correct prefix for IPv6 range'); is($ip->mask(), '0000:0000:0000:0000:0000:0000:0000:0000', 'Got correct mask for IPv6 range'); is($ip->iptype(), 'UNSPECIFIED', 'Got correct iptype for range'); is($ip->reverse_ip(), 'ip6.arpa.', 'Got correct iptype for range'); is($ip->last_bin(), '1' x 128, 'Got correct lastbin for range'); is($ip->last_int(), '340282366920938463463374607431768211455', 'Got correct last_int for range'); is($ip->last_ip(), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'Got correct last_ip for range'); is($ip->short(), '::', 'Got correct short for IPv6 range'); $ip->{'last_bin'} = '1' x 256; delete $ip->{'last_ip'}; is($ip->last_ip(), undef, 'Got undef on last_ip for bad last_bin'); $ip = Net::IP::XS->new('::/0', 6); $ip->{'is_prefix'} = 0; delete $ip->{'ip'}; is($ip->print(), undef, 'Got undef on print for bad object'); $ip = Net::IP::XS->new('::/0', 6); $ip->{'is_prefix'} = 0; delete $ip->{'ip'}; is($ip->mask(), undef, 'Got undef on mask for bad object'); $ip = Net::IP::XS->new('::/0', 6); $ip->{'binmask'} = '1' x 256; is($ip->mask(), undef, 'Got undef on mask for bad object'); $ip = Net::IP::XS->new('::/0', 6); delete $ip->{'last_bin'}; is($ip->last_bin(), '1' x 128, 'Got correct last_bin value after '. 'deleting the original (1)'); $ip = Net::IP::XS->new('::/0', 6); delete $ip->{'last_bin'}; $ip->{'is_prefix'} = 0; is($ip->last_bin(), '1' x 128, 'Got correct last_bin value after '. 'deleting the original (2)'); $ip = Net::IP::XS->new('::/0', 6); delete $ip->{'last_bin'}; delete $ip->{'last_ip'}; $ip->{'is_prefix'} = 0; is($ip->last_bin(), undef, 'Got undef last_bin on bad object (1)'); $ip = Net::IP::XS->new('::/0', 6); delete $ip->{'last_bin'}; $ip->{'last_ip'} = 'ZXCV'; $ip->{'is_prefix'} = 0; is($ip->last_bin(), undef, 'Got undef last_bin on bad object (2)'); # (Making sure that each method works if it is called immediately # after new is called. (Repeated calls are for checking that the # values were cached.)) $ip = Net::IP::XS->new('::/0', 6); is($ip->size(), '340282366920938463463374607431768211456', 'Got correct size for IPv6'); $ip = Net::IP::XS->new('::/0', 6); is($ip->intip(), 0, 'Got correct intip for IPv6 range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->hexip(), '0x0', 'Got correct hexip for IPv6 range'); is($ip->hexip(), '0x0', 'Got correct hexip for IPv6 range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->hexmask(), '0x0', 'Got correct hexmask for IPv6 range'); is($ip->hexmask(), '0x0', 'Got correct hexmask for IPv6 range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->prefix(), '0000:0000:0000:0000:0000:0000:0000:0000/0', 'Got correct prefix for IPv6 range'); is($ip->prefix(), '0000:0000:0000:0000:0000:0000:0000:0000/0', 'Got correct prefix for IPv6 range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->mask(), '0000:0000:0000:0000:0000:0000:0000:0000', 'Got correct mask for IPv6 range'); is($ip->mask(), '0000:0000:0000:0000:0000:0000:0000:0000', 'Got correct mask for IPv6 range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->iptype(), 'UNSPECIFIED', 'Got correct iptype for range'); is($ip->iptype(), 'UNSPECIFIED', 'Got correct iptype for range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->reverse_ip(), 'ip6.arpa.', 'Got correct iptype for range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->last_bin(), '1' x 128, 'Got correct lastbin for range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->last_int(), '340282366920938463463374607431768211455', 'Got correct last_int for range'); is($ip->last_int(), '340282366920938463463374607431768211455', 'Got correct last_int for range'); $ip = Net::IP::XS->new('::/0', 6); is($ip->last_ip(), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'Got correct last_ip for range'); $ip2 = Net::IP::XS->new('2000::/16'); ok($ip->bincomp('lt', $ip2), "Range's first address is smaller than other range's"); $ip3 = $ip->binadd($ip2); ok($ip3, 'Got result on binadd of two IP addresses'); is($ip3->ip(), '2000:0000:0000:0000:0000:0000:0000:0000', 'Newly created IP is correct'); $agg1_ip = Net::IP::XS->new('0000::/16'); $agg2_ip = Net::IP::XS->new('0001::/16'); $agg3_ip = $agg1_ip->aggregate($agg2_ip); ok($agg3_ip, 'Got result on aggregate of two IP ranges'); is($agg3_ip->ip(), '0000:0000:0000:0000:0000:0000:0000:0000', 'Starting IP is correct'); is($agg3_ip->last_ip(), '0001:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'Ending IP is correct'); is($agg3_ip->prefixlen(), 15, 'Prefix length is correct'); is($agg1_ip->overlaps($agg2_ip), $IP_NO_OVERLAP, 'Ranges do not overlap'); is($agg2_ip->overlaps($agg1_ip), $IP_NO_OVERLAP, 'Ranges do not overlap (2)'); is($agg1_ip->overlaps($agg3_ip), $IP_A_IN_B_OVERLAP, 'Range A is in range B'); is($agg3_ip->overlaps($agg1_ip), $IP_B_IN_A_OVERLAP, 'Range B is in range A'); is($agg3_ip->overlaps($agg3_ip), $IP_IDENTICAL, 'Range is identical to range'); $ip = Net::IP::XS->new('0/0'); is($ip->intip(), '0', 'Got correct intip for 0/0'); $ip = Net::IP::XS->new('61-217-102-8.hinet-ip.hinet.net'); ok((not $ip), 'Failed on bad IP address'); $ip = Net::IP::XS->new('123.0.0.1/a'); ok((not $ip), 'Failed on bad IP address'); $ip = Net::IP::XS->new('123.0.0.1/a'); ok((not $ip), 'Failed on bad IP address'); $ip = Net::IP::XS->new('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF'); ok($ip, 'Got new IP object'); eval { require IP::Authority }; my $has_ip_authority = (not $@); SKIP: { skip "IP::Authority not available", 5 unless $has_ip_authority; $ip = Net::IP::XS->new('202/8'); is($ip->auth(), 'AP', 'Got correct auth information'); is($ip->auth(), 'AP', 'Got correct auth information (cached)'); $ip = Net::IP::XS->new('2000::'); is($ip->auth(), undef, 'Got undef for auth for IPv6 address'); is($ip->error(), 'Cannot get auth information: Not an IPv4 address', 'Got correct error'); is($ip->errno(), 308, 'Got correct errno'); }; $ip->{'ipversion'} = 0; my @prefixes = $ip->find_prefixes(); is_deeply(\@prefixes, [], 'No prefixes where version is zero'); $ip = Net::IP::XS->new('127.0.0.1', 8); is($ip, undef, "Got undef IP object on bad version"); my $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); delete $ip1->{'binip'}; delete $ip2->{'binip'}; is($ip1->bincomp('lt', $ip2), 0, 'bincomp depends on binip'); $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); is($ip1->bincomp('asdf', $ip2), undef, 'bincomp failed on bad operator'); $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); delete $ip1->{'binip'}; delete $ip2->{'binip'}; $ip3 = $ip1->binadd($ip2); is($ip3->binip(), '0' x 128, 'binadd depends on binip'); $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); $ip1->{'binip'} = '0'; $ip2->{'binip'} = '01'; $ip3 = $ip1->binadd($ip2); is($ip3, undef, 'binadd depends on binip (2)'); $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); delete $ip1->{'ipversion'}; is($ip1->overlaps($ip2), undef, "overlaps depends on first ". "object's version"); $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); delete $ip1->{'ipversion'}; is($ip1->aggregate($ip2), undef, "aggregate depends on first ". "object's version"); $ip1 = Net::IP::XS->new('0000::'); $ip2 = Net::IP::XS->new('1111::'); is($ip1->aggregate($ip2), undef, "aggregate failed"); $ip1 = Net::IP::XS->new('::1'); delete $ip1->{'last_int'}; is($ip1->last_int(), 1, 'Got correct last_int where cached value deleted'); # Use an empty hashref for the object, make sure all methods bar mask # and last_ip return a false value (mask and last_ip should return the # zero IPv6 address). $ip = bless {}, 'Net::IP::XS'; for (qw(binip prefixlen is_prefix ip version binmask last_bin print size intip hexip hexmask prefix iptype reverse_ip last_int)) { ok((not $ip->$_()), "Got false value for $_"); } for (qw(mask last_ip)) { is($ip->$_(), (join ':', ('0000') x 8), "Got zero IPv6 address for $_"); } # Replace all fields with garbage, make sure it doesn't segfault. $ip = Net::IP::XS->new('::/0'); for (keys %{$ip}) { $ip->{$_} = join '', map { chr(rand(256)) } (0..10000); } $c->start(); for (qw(binip prefixlen is_prefix ip version binmask last_bin print size intip hexip hexmask prefix mask iptype reverse_ip last_int last_ip)) { $ip->$_(); } $c->stop(); ok(1, "Called all methods successfully"); # Call all object methods with a non-Net::IP::XS object, confirm no # segfaults or similar. my $str = 'asdf'; my $object = bless \$str, 'not_net_ip_xs'; $c->start(); for (qw(binip prefixlen is_prefix ip version binmask last_bin print size intip hexip hexmask prefix mask iptype reverse_ip last_int last_ip ip_add_num)) { my $fn = "Net::IP::XS::$_"; my $res = eval { $fn->($object) }; } for (qw(bincomp binadd aggregate overlaps)) { my $fn = "Net::IP::XS::$_"; my $res = eval { $fn->($object, $object) }; } $c->stop(); ok(1, 'Called all methods on non-Net::IP::XS object'); 1; Net-IP-XS-0.22/t/45-short.t0000644000175000017500000000124414363576625013560 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 33; use Net::IP::XS; for my $len (0..8) { my $ip = Net::IP::XS->new("0.0.0.0/$len"); is($ip->short(), "0", "Correct short form (prefix length $len)"); } for my $len (9..16) { my $ip = Net::IP::XS->new("0.0.0.0/$len"); is($ip->short(), "0.0", "Correct short form (prefix length $len)"); } for my $len (17..24) { my $ip = Net::IP::XS->new("0.0.0.0/$len"); is($ip->short(), "0.0.0", "Correct short form (prefix length $len)"); } for my $len (25..32) { my $ip = Net::IP::XS->new("0.0.0.0/$len"); is($ip->short(), "0.0.0.0", "Correct short form (prefix length $len)"); } 1; Net-IP-XS-0.22/t/47-object-size.t0000644000175000017500000000136614363576625014646 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Net::IP::XS; use Test::More tests => 6; my $ip = Net::IP::XS->new('0.0.0.0/0'); is($ip->size()->bstr(), '4294967296', 'Got correct size for 0/0'); $ip = Net::IP::XS->new('0.0.0.0/32'); is($ip->size()->bstr(), '1', 'Got correct size for 0/32'); $ip = Net::IP::XS->new('0.0.0.0/16'); is($ip->size()->bstr(), '65536', 'Got correct size for 0/16'); $ip = Net::IP::XS->new('0.0.0.0/8'); is($ip->size()->bstr(), '16777216', 'Got correct size for 0/8'); $ip = Net::IP::XS->new('::/0'); is($ip->size()->bstr(), '340282366920938463463374607431768211456', 'Got correct size for ::/0'); $ip = Net::IP::XS->new('::/128'); is($ip->size()->bstr(), '1', 'Got correct size for ::/128'); 1; Net-IP-XS-0.22/t/14-bintoint.t0000644000175000017500000000204314363576625014241 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 13; use Net::IP::XS qw(ip_bintoint); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); my $res = ip_bintoint('1'); isa_ok($res, 'Math::BigInt'); is($res, 1, 'ip_bintoint (single bit)'); $res = ip_bintoint('10101010'); is($res, 170, 'ip_bintoint 2'); $res = ip_bintoint('1' x 128); is($res, '340282366920938463463374607431768211455', 'ip_bintoint 3'); $res = ip_bintoint('0' x 32); isa_ok($res, 'Math::BigInt'); is($res, 0, 'ip_bintoint 4'); $res = ip_bintoint('1' x 32); is($res, '4294967295', 'ip_bintoint 5'); $res = ip_bintoint('0' x 128); isa_ok($res, 'Math::BigInt'); is($res, 0, 'ip_bintoint 6'); $res = ip_bintoint('0A0B0C0D'); is($res, 85, 'Non-zero treated as 1'); $res = ip_bintoint('1' x 1024); is($res, '340282366920938463463374607431768211455', 'More than 128 bits in bitstring yields the largest 128-bit number'); $c->start(); is(ip_bintoint(undef), 0, 'Got zero on undef'); $c->stop(); is(ip_bintoint(''), 0, 'Got zero on empty string'); 1; Net-IP-XS-0.22/t/31-get-embedded-ipv4.t0000644000175000017500000000134514363576625015604 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 8; use Net::IP::XS qw(ip_get_embedded_ipv4); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); my @data = ( [ ], [ '', undef ], [ qw(:127.0.0.1 127.0.0.1) ], [ qw(::::::::::) ], [ qw(::::::::::;:127.0.0.1 127.0.0.1) ], [ qw(ASDF:ASDF:ASDF:ASDF:ASDF:ASDF:ASDF:127.0.0.1 127.0.0.1) ], [ qw(127.0.0.1 127.0.0.1) ], [ qw(:123123123123123123123123123123123) ], ); for (@data) { my ($input, $res_exp) = @{$_}; $c->start(); my $res = ip_get_embedded_ipv4($input); $c->stop(); is($res, $res_exp, $input); } 1; Net-IP-XS-0.22/t/21-split-prefix.t0000644000175000017500000000160314363576625015040 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 12; use Net::IP::XS qw(ip_splitprefix); my @data = ( [ '' => undef ], [ '/' => undef ], [ '1/' => undef ], [ '1/abcd' => undef ], [ '1/-1' => undef ], [ '100' => undef ], [ '127/32' => [ '127', '32' ] ], [ '127.0.0.1/32' => [ '127.0.0.1', '32' ] ], [ (join ':', ('0000') x 8).'/128' => [ (join ':', ('0000') x 8), 128 ] ], [ ('1' x 256).'/500' => [] ], [ (join ':', ('255.255.255.255') x 4).'/128' => [ (join ':', ('255.255.255.255') x 4), 128 ] ], [ (join ':', ('255.255.255.255') x 4).'a/128' => [] ], ); for (@data) { my ($input, $res_exp) = @{$_}; my @res = ip_splitprefix($input); if (ref $res_exp) { is_deeply(\@res, $res_exp, "Split prefix: $input"); } else { is($res[0], $res_exp, "Split prefix: $input"); } } 1; Net-IP-XS-0.22/t/39-auth.t0000644000175000017500000000166414363576625013373 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More; BEGIN { eval { require IP::Authority; }; if (my $error = $@) { plan skip_all => "IP::Authority not available"; } else { plan tests => 8; } }; use Net::IP::XS qw(ip_auth Error Errno); my $res = ip_auth('', 0); is($res, undef, 'Got undef on no version'); is(Error(), 'Cannot determine IP version for ', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); $res = ip_auth('', 8); is($res, undef, 'Got undef on non-IPv4 address'); is(Error(), 'Cannot get auth information: Not an IPv4 address', 'Got correct error'); is(Errno(), 308, 'Got correct errno'); my @data = ( [['203.0.0.0', 4] => 'AP'], [['202.0.0.0', 4] => 'AP'], ); for my $entry (@data) { my ($arg, $res) = @{$entry}; my ($ip, $version) = @{$arg}; my $res_t = ip_auth($ip, $version); is($res_t, $res, "Got correct auth for $ip ($version)"); } 1; Net-IP-XS-0.22/t/32-aggregate.t0000644000175000017500000000701214363576625014342 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 22; use Net::IP::XS qw(ip_aggregate ip_iptobin Error Errno); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); $c->start(); my $res = ip_aggregate(undef, undef, undef, undef, 0); $c->stop(); is($res, undef, 'Got undef on no version'); is(Error(), 'Cannot determine IP version for ', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); $res = ip_aggregate('0' . 1024, '1' x 1024, '1'.('0' x 1024), '1' x 1025, 4); is($res, undef, 'Got undef where bitstrings too large (IPv4)'); $res = ip_aggregate('0' . 1024, '1' x 1024, '1'.('0' x 1024), '1' x 1025, 6); is($res, undef, 'Got undef where bitstrings too large (IPv6)'); my $addr = ip_iptobin('127.0.0.0', 4); $res = ip_aggregate($addr, $addr, $addr, $addr, 4); is($res, undef, 'Got undef on non-contiguous ranges'); is(Error(), "Ranges not contiguous - $addr - $addr", 'Got correct error'); is(Errno(), 160, 'Got correct errno'); $addr = ip_iptobin((join ':', ('0000') x 8), 6); $res = ip_aggregate($addr, $addr, $addr, $addr, 6); is($res, undef, 'Got undef on non-contiguous ranges'); is(Error(), "Ranges not contiguous - $addr - $addr", 'Got correct error'); is(Errno(), 160, 'Got correct errno'); my $addr1 = ip_iptobin('0.0.0.1', 4); my $addr2 = ip_iptobin('127.255.255.255', 4); my $addr3 = ip_iptobin('128.0.0.0', 4); my $addr4 = ip_iptobin('255.255.255.255', 4); $res = ip_aggregate($addr1, $addr2, $addr3, $addr4, 4); is($res, undef, 'Got undef on multiple prefixes'); is(Error(), "$addr1 - $addr4 is not a single prefix", 'Got correct error'); is(Errno(), 161, 'Got correct errno'); my @data = ( [ qw(127.0.0.0 127.0.0.255 127.0.1.0 124.0.0.255 4 undef) ], [ qw(127.0.0.0 127.0.0.255 127.0.1.0 127.0.3.255 4 127.0.0.0/22) ], [ qw(127.0.0.0 127.0.0.1 127.0.0.2 127.0.0.3 4 127.0.0.0/30) ], [ qw(0.0.0.0 127.255.255.255 128.0.0.0 255.255.255.255 4 0.0.0.0/0) ], [ '0000:0000:0000:0000:0000:0000:1234:0000', '0000:0000:0000:0000:0000:0000:1234:00FF', '0000:0000:0000:0000:0000:0000:1234:0100', '0000:0000:0000:0000:0000:0000:1234:01FF', 6, '0000:0000:0000:0000:0000:0000:1234:0000/119' ], [ '0000:0000:0000:0000:0000:0000:0000:0000', '0000:0000:0000:0000:0000:0000:0000:0001', '0000:0000:0000:0000:0000:0000:0000:0002', '0000:0000:0000:0000:0000:0000:0000:0003', 6, '0000:0000:0000:0000:0000:0000:0000:0000/126' ], [ '0000:0000:0000:0000:0000:0000:0000:0000', '7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', '8000:0000:0000:0000:0000:0000:0000:0000', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 6, '0000:0000:0000:0000:0000:0000:0000:0000/0' ], [ '0000:0000:0000:0000:0000:0000:0000:0000', '0000:ffff:ffff:ffff:ffff:ffff:ffff:ffff', '0001:0000:0000:0000:0000:0000:0000:0000', '0001:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 6, '0000:0000:0000:0000:0000:0000:0000:0000/15' ], ); for (@data) { my ($b1, $e1, $b2, $e2, $version, $res_exp) = @{$_}; if ($res_exp eq 'undef') { $res_exp = undef; } my $res = ip_aggregate(ip_iptobin($b1, $version), ip_iptobin($e1, $version), ip_iptobin($b2, $version), ip_iptobin($e2, $version), $version); is($res, $res_exp, "$b1 - $e1, $b2 - $e2"); } 1; Net-IP-XS-0.22/t/27-compress-v4.t0000644000175000017500000000237714363576625014613 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 16; use Net::IP::XS qw(ip_compress_v4_prefix); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); my $res; my @res; my @data = ( [ '', '', '' ], [ undef, undef, '' ], [ 'abcd', 'ef', 'abcd' ], [ '255.255.255.255.255', 'ef', undef ], [ '...................', 'ef', undef ], [ qw(127.0.0.1 -100), undef ], [ qw(127.0.0.1 100), undef ], [ qw(127.0.0.1 0 127) ], [ qw(127.0.0.1 1 127) ], [ qw(127.0.0.1 8 127) ], [ qw(127.0.0.1 9 127.0) ], [ qw(127.0.0.1 16 127.0) ], [ qw(127.0.0.1 24 127.0.0) ], [ qw(127.0.0.1 31 127.0.0.1) ], [ qw(127.0.0.1 32 127.0.0.1) ], [ qw(127.0.0.1/32 32 127.0.0.1/32) ], ); for (@data) { my ($addr, $length, $res_exp) = @{$_}; $c->start(); my $res = ip_compress_v4_prefix($addr, $length); $c->stop(); for ($addr, $length) { defined $_ or $_ = 'undef'; } is($res, $res_exp, "$addr - $length"); } 1; Net-IP-XS-0.22/t/46-wrong-object.t0000644000175000017500000000173414363576625015026 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 36; use Net::IP::XS; my $str = "asdf"; my $obj = bless \$str, "Temporary"; for (qw(print size_str intip_str hexip hexmask prefix mask iptype reverse_ip last_bin last_int_str last_ip short find_prefixes)) { my $fn_name = "Net::IP::XS::$_"; is(eval("$fn_name(\$obj)"), undef, "Got undef on calling $_ on non-Net-IP-XS object"); ok((not $@), "Did not die on calling $_"); diag $@ if $@; } for (qw(binadd aggregate overlaps)) { my $fn_name = "Net::IP::XS::$_"; is(eval("$fn_name(\$obj, \$obj)"), undef, "Got undef on calling $_ on non-Net-IP-XS objects"); ok((not $@), "Did not die on calling $_"); diag $@ if $@; } is(Net::IP::XS::ip_add_num($obj, 100, undef), undef, 'Got undef on calling ip_add_num on non-Net-IP-XS object'); is(Net::IP::XS::bincomp($obj, 'lt', $obj), undef, 'Got undef on calling bincomp on non-Net-IP-XS objects'); 1; Net-IP-XS-0.22/t/22-is-valid-mask.t0000644000175000017500000000244414363576625015060 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 16; use Net::IP::XS qw(ip_is_valid_mask Error Errno); my $res; my @res; $res = ip_is_valid_mask('', 0); is($res, undef, 'No IP address version with empty mask'); is(Error(), 'Cannot determine IP version for ', 'Got correct error'); is(Errno(), 101, 'Got correct error'); $res = ip_is_valid_mask('', 8); is($res, 1, 'Bad IP address version with empty mask'); $res = ip_is_valid_mask('1'.('0' x 31), 4); is($res, 1, 'IPv4 mask (1)'); $res = ip_is_valid_mask('1' x 32, 4); is($res, 1, 'IPv4 mask (2)'); $res = ip_is_valid_mask('0' x 32, 4); is($res, 1, 'IPv4 mask (3)'); $res = ip_is_valid_mask('asdf', 4); is($res, undef, 'Invalid mask (contains letters, too short)'); is(Error(), "Invalid mask length for asdf", 'Got correct error'); is(Errno(), 150, 'Got correct errno'); $res = ip_is_valid_mask('asdf' x 8, 4); is($res, undef, 'Invalid mask (contains letters, correct length)'); is(Error(), "Invalid mask ".('asdf' x 8), 'Got correct error'); is(Errno(), 151, 'Got correct errno'); $res = ip_is_valid_mask('asdf' x 500, 4); is($res, undef, 'Invalid mask (contains letters, too long)'); is(Errno(), 150, 'Got correct errno'); $res = ip_is_valid_mask('0'.('1' x 31), 4); is($res, undef, 'Invalid mask (0 then series of 1s)'); 1; Net-IP-XS-0.22/t/26-get-prefix-length.t0000644000175000017500000000251414363576625015752 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 17; use Net::IP::XS qw(ip_get_prefix_length ip_iptobin Error Errno); my $res = ip_get_prefix_length('1', '11'); is($res, undef, 'Got undef on different lengths'); is(Error(), 'IP addresses of different length', 'Got correct error'); is(Errno(), 130, 'Got correct errno'); my @data = ( [ qw(127.0.0.0 127.0.0.0 4 0) ], [ qw(127.0.0.0 127.0.0.1 4 1) ], [ qw(127.0.0.0 127.0.0.2 4 0) ], [ qw(127.0.0.0 127.0.0.3 4 2) ], [ qw(127.0.0.0 127.0.0.4 4 0) ], [ qw(127.0.0.0 127.0.0.5 4 1) ], [ qw(127.0.0.0 127.0.0.6 4 0) ], [ qw(127.0.0.0 127.0.0.7 4 3) ], [ qw(127.0.0.0 127.0.0.255 4 8) ], [ qw(127.0.0.0 127.0.255.255 4 16) ], [ qw(127.0.0.0 127.255.255.255 4 24) ], [ qw(0.0.0.0 255.255.255.255 4 32) ], [ (join ':', ('0000') x 8), (join ':', (('0000') x 7, 'ffff')), 6, 16 ], [ (join ':', ('0000') x 8), (join ':', ('ffff') x 8), 6, 128 ], ); for (@data) { my ($addr1, $addr2, $version, $len_exp) = @{$_}; my $len = ip_get_prefix_length(ip_iptobin($addr1, $version), ip_iptobin($addr2, $version)); is($len, $len_exp, "$addr1 - $addr2"); } 1; Net-IP-XS-0.22/t/15-inttobin.t0000644000175000017500000000332514363576625014246 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 15; use Net::IP::XS qw(ip_inttobin Error Errno); my $res = ip_inttobin('1', 0); is($res, undef, 'ip_inttobin invalid'); is(Error(), 'Cannot determine IP version for 1', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); $res = ip_inttobin('1', 6); is($res, ('0' x 127).'1', 'ip_inttobin 1'); $res = ip_inttobin('170', 6); is($res, ('0' x 120).'10101010', 'ip_inttobin 2'); $res = ip_inttobin('1', 4); is($res, ('0' x 31).'1', 'ip_inttobin 3'); $res = ip_inttobin('170', 4); is($res, ('0' x 24).'10101010', 'ip_inttobin 4'); $res = ip_inttobin('1', 8); is($res, ('0' x 127).1, 'ip_inttobin 5'); # The 'real' bitstring here is # 101011110010001011100111001011110011110110101010001011. The return # value is the lower 32 bits of this value, because of the version # number argument. use Math::BigInt; $res = ip_inttobin(Math::BigInt->new("12324124312431243"), 4); is($res, '11001011110011110110101010001011', 'ip_inttobin bigint 1'); $res = ip_inttobin(Math::BigInt->new("4294967295"), 4); is($res, '1' x 32, 'ip_inttobin bigint 2'); $res = ip_inttobin( Math::BigInt->new("340282366920938463463374607431768211455"), 4 ); is($res, '1' x 32, 'ip_inttobin bigint 3'); $res = ip_inttobin( Math::BigInt->new("340282366920938463463374607431768211455"), 6 ); is($res, '1' x 128, 'ip_inttobin bigint 3'); $res = ip_inttobin( Math::BigInt->new("340282366920938463463374607431768211456"), 6 ); is($res, undef, 'Got undef on a number that was too large'); $res = ip_inttobin('ABCD', 4); is($res, ('0' x 32), 'Got zero address on non-integer'); $res = ip_inttobin('ABCD', 6); is($res, ('0' x 128), 'Got zero address on non-integer (IPv6)') Net-IP-XS-0.22/t/38-type.t0000644000175000017500000000213714363576625013406 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 10; use Net::IP::XS qw(ip_iptype ip_iptobin ip_expand_address); $Net::IP::XS::IPv4ranges{'1' x 32} = 'A' x 1024; sub tbea { ip_iptobin(ip_expand_address($_[0], $_[1]), $_[1]) } my @data = ( [[tbea('1.2.3.4', 4), 4] => 'PUBLIC'], [[tbea('0.255.255.255', 4), 4] => 'PRIVATE'], [[tbea('127.0.0.1', 4), 4] => 'PRIVATE'], [[tbea('192.0.2.128', 4), 4], => 'RESERVED'], [[tbea('248.0.0.0', 4), 4] => 'RESERVED'], [[tbea('0100::', 6), 6] => 'RESERVED'], [[tbea('ff00::1234', 6), 6], => 'MULTICAST'], [[tbea('::1', 6), 6], => 'LOOPBACK'], [['1' x 32, 4] => 'A' x 255], ); for my $entry (@data) { my ($arg, $res) = @{$entry}; my ($ip, $version) = @{$arg}; my $res_t = ip_iptype($ip, $version); is($res_t, $res, "Got correct type for $ip ($version)"); } for (keys %Net::IP::XS::IPv6ranges) { delete $Net::IP::XS::IPv6ranges{$_}; } my $res = ip_iptype(tbea('4000::', 6), 6); is($res, undef, "Got undef on IPv6 address with no type"); 1; Net-IP-XS-0.22/t/28-overlap.t0000644000175000017500000000766714363576625014111 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 33; use Net::IP::XS qw(ip_is_overlap ip_iptobin Error Errno $IP_NO_OVERLAP $IP_PARTIAL_OVERLAP $IP_A_IN_B_OVERLAP $IP_B_IN_A_OVERLAP $IP_IDENTICAL); my $c = 1; for my $det ([qw(1 12 123 1234)], [qw(1 1 123 1234)], [qw(1 1 12 12)]) { my ($b1, $e1, $b2, $e2) = @{$det}; my $res = ip_is_overlap($b1, $e1, $b2, $e2); is($res, undef, "Got no result on strings of different lengths ($c)"); is(Error(), 'IP addresses of different length', 'Got correct error'); is(Errno(), 130, 'Got correct errno'); } my $res = ip_is_overlap('1', '0', '0', '0'); is($res, undef, 'Got no result on bad range (1)'); is(Error(), 'Invalid range 1 - 0', 'Got correct error'); is(Errno(), 140, 'Got correct errno'); $res = ip_is_overlap('0', '0', '1', '0'); is($res, undef, 'Got no result on bad range (2)'); is(Error(), 'Invalid range 1 - 0', 'Got correct error'); is(Errno(), 140, 'Got correct errno'); my @data = ( [ qw(127.0.0.1 127.0.0.255 128.0.0.0 128.0.0.255 4), $IP_NO_OVERLAP ], [ qw(127.0.0.1 128.0.0.0 128.0.0.0 128.0.0.255 4), $IP_PARTIAL_OVERLAP ], [ qw(127.0.0.1 128.0.0.0 127.0.0.0 127.255.255.255 4), $IP_PARTIAL_OVERLAP ], [ qw(127.0.0.1 129.0.0.0 128.0.0.0 129.0.0.0 4), $IP_B_IN_A_OVERLAP ], [ qw(128.0.0.1 129.0.0.0 127.0.0.0 129.0.0.0 4), $IP_A_IN_B_OVERLAP ], [ qw(127.0.0.1 127.0.0.255 126.0.0.0 128.0.0.255 4), $IP_A_IN_B_OVERLAP ], [ qw(0.0.0.0 255.255.255.255 126.0.0.0 128.0.0.255 4), $IP_B_IN_A_OVERLAP ], [ qw(0.0.0.0 255.255.255.255 0.0.0.0 255.255.255.255 4), $IP_IDENTICAL ], [ (join ':', ('0000') x 8), (join ':', ('1111') x 8), (join ':', ('2222') x 8), (join ':', ('3333') x 8), 6, $IP_NO_OVERLAP ], [ (join ':', ('0000') x 8), (join ':', ('1111') x 8), (join ':', ('1111') x 8), (join ':', ('3333') x 8), 6, $IP_PARTIAL_OVERLAP ], [ (join ':', ('0000') x 8), (join ':', ('5555') x 8), (join ':', ('1111') x 8), (join ':', ('3333') x 8), 6, $IP_B_IN_A_OVERLAP ], [ (join ':', ('1111') x 8), (join ':', ('3333') x 8), (join ':', ('0000') x 8), (join ':', ('1111') x 8), 6, $IP_PARTIAL_OVERLAP ], [ (join ':', ('1111') x 8), (join ':', ('3333') x 8), (join ':', ('1111') x 8), (join ':', ('5555') x 8), 6, $IP_A_IN_B_OVERLAP ], [ (join ':', ('1111') x 8), (join ':', ('3333') x 8), (join ':', ('0000') x 8), (join ':', ('3333') x 8), 6, $IP_A_IN_B_OVERLAP ], [ (join ':', ('0000') x 8), (join ':', ('3333') x 8), (join ':', ('1111') x 8), (join ':', ('3333') x 8), 6, $IP_B_IN_A_OVERLAP ], [ (join ':', ('0000') x 8), (join ':', ('3333') x 8), (join ':', ('1111') x 8), (join ':', ('4444') x 8), 6, $IP_PARTIAL_OVERLAP ], [ (join ':', ('1111') x 8), (join ':', ('3333') x 8), (join ':', ('0000') x 8), (join ':', ('4444') x 8), 6, $IP_A_IN_B_OVERLAP ], [ (join ':', ('0000') x 8), (join ':', ('ffff') x 8), (join ':', ('0000') x 8), (join ':', ('ffff') x 8), 6, $IP_IDENTICAL ], ); for (@data) { my ($b1, $e1, $b2, $e2, $version, $res_exp) = @{$_}; my $res = ip_is_overlap(ip_iptobin($b1, $version), ip_iptobin($e1, $version), ip_iptobin($b2, $version), ip_iptobin($e2, $version)); is($res, $res_exp, "$b1 - $e1, $b2 - $e2"); } 1; Net-IP-XS-0.22/t/16-is-ipv4.t0000644000175000017500000000364214363576625013716 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 47; use Net::IP::XS qw(ip_is_ipv4 Error Errno); use IO::Capture::Stderr; my @data = ( [ '' => 0, 107, 'Invalid chars in IP ' ], [ undef, 0, 107, 'Invalid chars in IP ' ], [ '127' => 1 ], [ '127.0' => 1 ], [ '127.0.0' => 1 ], [ '127.0.0.0' => 1 ], [ '0' => 1 ], [ '255.255.255.255' => 1 ], [ '1.2.3.4' => 1 ], [ '192.168.0.1' => 1 ], [ '127.256' => 0, 107, 'Invalid quad in IP address 127.256 - 256' ], [ '127.127.256' => 0, 107, 'Invalid quad in IP address 127.127.256 - 256' ], [ '127.127.127.256' => 0, 107, 'Invalid quad in IP address 127.127.127.256 - 256' ], [ '1.1.1.256' => 0, 107, 'Invalid quad in IP address 1.1.1.256 - 256' ], [ '123459125' => 0, 107, 'Invalid quad in IP address 123459125 - 123459125' ], [ 'ABCD' => 0, 107, 'Invalid chars in IP ABCD' ], [ '.123' => 0, 103, 'Invalid IP .123 - starts with a dot' ], [ '123.' => 0, 104, 'Invalid IP 123. - ends with a dot' ], [ '1.....2' => 0, 105, 'Invalid IP address 1.....2' ], [ '123..123.123' => 0, 106, 'Empty quad in IP address 123..123.123' ], [ '92233720368547758078' => 0, 107, qr/^Invalid quad in IP address 92233720368547758078/ ], ); my $cap = IO::Capture::Stderr->new(); for my $entry (@data) { my ($input, $res, $errno, $error) = @{$entry}; $cap->start(); my $res_t = ip_is_ipv4($input); $cap->stop(); if (not defined $input) { $input = '(undef)'; } is($res_t, $res, "Got correct ip_is_ipv4 result for $input"); if (defined $errno) { is(Errno(), $errno, 'Got correct errno'); } if (defined $error) { if (ref $error) { like(Error(), $error, 'Got correct error'); } else { is(Error(), $error, 'Got correct error'); } } } 1; Net-IP-XS-0.22/t/20-last-address.t0000644000175000017500000000172214363576625015001 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 9; use Net::IP::XS qw(ip_last_address_bin Error Errno); my $res = ip_last_address_bin('0' x 32, 0, 0); is(Error(), 'Cannot determine IP version', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); $res = ip_last_address_bin('0' x 32, 0, 4); is($res, '1' x 32, 'ip_last_address_bin 1'); $res = ip_last_address_bin('0' x 32, 32, 4); is($res, '0' x 32, 'ip_last_address_bin 2'); $res = ip_last_address_bin('0' x 35, 35, 4); is($res, '0' x 32, 'ip_last_address_bin 3'); $res = ip_last_address_bin('0' x 35, -5, 4); is($res, '0' x 32, 'ip_last_address_bin 4'); $res = ip_last_address_bin('11110000111100001111000011110000', 8, 4); is($res, '11110000'.('1' x 24), 'ip_last_address_bin 5'); $res = ip_last_address_bin('0' x 128, 32, 6); is($res, ('0' x 32).('1' x 96), 'ip_last_address_bin 6'); $res = ip_last_address_bin('0' x 500, 500, 6); is($res, ('0' x 128), 'ip_last_address_bin 7'); 1; Net-IP-XS-0.22/t/41-serialise.t0000644000175000017500000000247214363576625014401 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Net::IP::XS; use Storable qw(freeze thaw); use File::Temp qw(tempfile); my (undef, $ft) = tempfile(); use Test::More tests => 6; use Scalar::Util qw(blessed); { my $ip = Net::IP::XS->new('::/0'); is($ip->size(), '340282366920938463463374607431768211456', "Got size for IP address"); my $serial = freeze($ip); open my $fh, '>', $ft or die $!; print $fh $serial; close $fh; undef $ip; undef $serial; open $fh, '<', $ft or die $!; my $con = do { local $/; <$fh> }; close $fh; $ip = thaw($con); is($ip->size(), '340282366920938463463374607431768211456', "Got size for IP address"); undef $ip; ok(1, "Completed serial-deserial process for IPv6 without issues"); } { my $ip = Net::IP::XS->new('0.0.0.0/0'); is($ip->size(), '4294967296', "Got size for IP address"); my $serial = freeze($ip); open my $fh, '>', $ft or die $!; print $fh $serial; close $fh; undef $ip; undef $serial; open $fh, '<', $ft or die $!; my $con = do { local $/; <$fh> }; close $fh; $ip = thaw($con); is($ip->size(), '4294967296', "Got size for IP address"); undef $ip; ok(1, "Completed serial-deserial process for IPv4 without issues"); } 1; Net-IP-XS-0.22/t/11-iptobin.t0000644000175000017500000000405614363576625014062 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 21; use Net::IP::XS qw(ip_iptobin Error Errno ip_bintoip); my $str = ip_iptobin('000.000.000.000', 4); is($str, undef, 'Got undef on bad IPv4 address (1)'); $str = ip_iptobin('300.300.300.300', 4); is($str, undef, 'Got undef on bad IPv4 address (2)'); $str = ip_iptobin('300.300.300.300.300.300', 4); is($str, undef, 'Got undef on bad IPv4 address (3)'); $str = ip_iptobin('0.0.0.0', 4); is($str, '00000000000000000000000000000000', 'ip_iptobin v4 (min)'); $str = ip_iptobin('127.1.2.122', 4); is($str, '01111111000000010000001001111010', 'ip_iptobin v4'); $str = ip_iptobin('255.255.255.255', 4); is($str, '11111111111111111111111111111111', 'ip_iptobin v4 (max)'); $str = ip_iptobin('1.2.3.400', 800); is($str, undef, 'ip_iptobin invalid'); $str = ip_iptobin((join ':', (('0000') x 8)), 6); is($str, '0' x 128, 'ip_iptobin v6 (min)'); $str = ip_iptobin((join ':', (('ffff') x 8)), 6); is($str, '1' x 128, 'ip_iptobin v6 (max)'); $str = ip_iptobin('12341234123412341234123412341234', 6); is($str, '0001001000110100' x 8, 'ip_iptobin v6'); $str = ip_iptobin( 'ff00:0000:0000:0000:0000:0000:0000:1234', 6 ); is($str, '1111111100000000'.('0' x 96).'0001001000110100', 'ip_iptobin v6'); $str = ip_iptobin('1234', 6); is($str, undef, 'ip_iptobin invalid'); is(Error(), 'Bad IP address 1234', 'Correct error'); is(Errno(), 102, 'Correct errno'); $str = ip_iptobin( 'A\CD:E 1; BEGIN { use_ok( 'Net::IP::XS' ); } Net-IP-XS-0.22/t/34-reverse.t0000644000175000017500000000414314363576625014073 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 32; use Net::IP::XS qw(ip_reverse Error Errno); my $res = ip_reverse('ASDF', 0, 0); is($res, undef, 'Got undef on no version and bad address'); is(Error(), 'Cannot determine IP version for ASDF', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); my @data = ( [['A.B.C.D', 6, 4] => undef], [['0.0.0.0', -1, 4] => undef], [['0.0.0.0', 33, 4] => undef], [['0.0.0.0', 32, 4] => '0.0.0.0.in-addr.arpa.'], [['1.2.3.4', 32, 4] => '4.3.2.1.in-addr.arpa.'], [['1.2.3.4', 24, 4] => '3.2.1.in-addr.arpa.'], [['1.2.3.4', 16, 4] => '2.1.in-addr.arpa.'], [['1.2.3.4', 8, 4] => '1.in-addr.arpa.'], [['1.2.3.4', 0, 4] => 'in-addr.arpa.'], [['123.234.234.231', 32, 4] => '231.234.234.123.in-addr.arpa.'], [['255.255.255.255', 32, 4] => '255.255.255.255.in-addr.arpa.'], [['100.0', 32, 4] => '0.0.0.100.in-addr.arpa.'], [['A:B:C:D::', -1, 6] => undef], [['A:B:C:D::', 129, 6] => undef], [['ZXCVCCCCC', 56, 6] => undef], [['A:B:C:D::', 0, 6] => 'ip6.arpa.'], [['A:B:C:D::', 4, 6] => '0.ip6.arpa.'], [['A:B:C:D::', 8, 6] => '0.0.ip6.arpa.'], [['A:B:C:D::', 12, 6] => '0.0.0.ip6.arpa.'], [['A:B:C:D::', 16, 6] => 'a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 20, 6] => '0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 24, 6] => '0.0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 28, 6] => '0.0.0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 32, 6] => 'b.0.0.0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 36, 6] => '0.b.0.0.0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 40, 6] => '0.0.b.0.0.0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 44, 6] => '0.0.0.b.0.0.0.a.0.0.0.ip6.arpa.'], [['A:B:C:D::', 48, 6] => 'c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa.'], [['0::0',128,6] => '0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.'], ); for my $entry (@data) { my ($args, $res) = @{$entry}; my ($ip, $len, $ver) = @{$args}; my $res_t = ip_reverse($ip, $len, $ver); for ($ip, $len, $ver) { defined $_ or $_ = undef; } is($res_t, $res, "Got reverse domain for $ip, $len, $ver"); } 1; Net-IP-XS-0.22/t/12-iplengths.t0000644000175000017500000000067214363576625014414 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 4; use Net::IP::XS qw(ip_iplengths); use IO::Capture::Stderr; my $len = ip_iplengths(4); is($len, 32, 'ip_iplengths 4'); $len = ip_iplengths(6); is($len, 128, 'ip_iplengths 6'); $len = ip_iplengths(8); is($len, undef, 'ip_iplengths invalid'); my $c = IO::Capture::Stderr->new(); $c->start(); $len = ip_iplengths(undef); is($len, undef, 'ip_iplengths invalid'); 1; Net-IP-XS-0.22/t/24-bincomp.t0000644000175000017500000000224614363576625014050 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 27; use Net::IP::XS qw(ip_bincomp Error Errno); my $res = ip_bincomp('11', 'gte', '11'); is(Error(), 'Invalid Operator gte', 'Correct error message'); is(Errno(), 131, 'Correct errno'); $res = ip_bincomp('1', 'ge', '11'); is(Error(), 'IP addresses of different length', 'Correct error message'); is(Errno(), 130, 'Correct errno'); my @data = ( [ qw(1 le 0 0) ], [ qw(0 le 0 1) ], [ qw(0 le 1 1) ], [ qw(1 le 1 1) ], [ qw(1 ge 0 1) ], [ qw(0 ge 0 1) ], [ qw(0 ge 1 0) ], [ qw(1 ge 1 1) ], [ qw(1 lt 0 0) ], [ qw(0 lt 0 0) ], [ qw(0 lt 1 1) ], [ qw(1 lt 1 0) ], [ qw(1 gt 0 1) ], [ qw(0 gt 0 0) ], [ qw(0 gt 1 0) ], [ qw(1 gt 1 0) ], [ qw(1001 ge 1010 0) ], [ qw(01 lt 10 1) ], [ qw(10 gt 01 1) ], [ '0' x 32, 'ge', '1', undef ], [ '1' x 128, 'le', '1' x 128, 1 ], [ ('1' x 127).'0', 'lt', '1' x 128, 1 ], [ ('1' x 511).'0', 'lt', '1' x 512, 1 ], ); for (@data) { my ($first, $op, $second, $res_exp) = @{$_}; my $res = ip_bincomp($first, $op, $second); is($res, $res_exp, "$first $op $second"); } 1; Net-IP-XS-0.22/t/25-binadd.t0000644000175000017500000000304314363576625013637 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 15; use Net::IP::XS qw(ip_binadd Error Errno); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); my $res = ip_binadd('0', '01'); is($res, undef, 'Got no result on different lengths'); is(Error(), 'IP addresses of different length', 'Got correct error'); is(Errno(), 130, 'Got correct errno'); my @data = ( [ undef, undef, '' ], [ '', '', '' ], [ 'abcd', 'edfh', '1110' ], [ qw(0 0 0) ], [ qw(1 1 0) ], [ qw(1010 0101 1111) ], [ qw(11111111 00000001 00000000) ], [ '0000001110111011101110111011101110111011101110111011101110111011'. '1011101110111011101110111011101110111011101110111011101110111011', '0000001110111011101110111011101110111011101110111011101110111011'. '1011101110111011101110111011101110111011101110111011101110111011', '0000011101110111011101110111011101110111011101110111011101110111'. '0111011101110111011101110111011101110111011101110111011101110110', ], [ '1' x 128, '1' x 128, ('1' x 127).'0' ], [ '1' x 129, '1' x 128, undef ], [ '1' x 129, '1' x 129, undef ], [ '1' x 1024, '1' x 1024, undef ], ); for (@data) { my ($first, $second, $res_exp) = @{$_}; if (not defined $first or not defined $second) { $c->start(); } my $res = ip_binadd($first, $second); if (not defined $first or not defined $second) { $c->stop(); } for ($first, $second) { defined $_ or $_ = 'undef'; } is($res, $res_exp, "$first + $second"); } 1; Net-IP-XS-0.22/t/36-normal-range.t0000644000175000017500000000166014363576625015005 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 9; use Net::IP::XS qw(ip_normal_range); my @data = ( ['0.0.0.0' => ['0.0.0.0 - 0.0.0.0']], ['0.0.0.0 + 255' => ['0.0.0.0 - 0.0.0.255']], ['0.0.0.0 + 16777215' => ['0.0.0.0 - 0.255.255.255']], ['0.0.0.0 + 16777216' => ['0.0.0.0 - 1.0.0.0']], ['0.0.0.0 + 4294967295' => ['0.0.0.0 - 255.255.255.255']], ['0.0.0.0 + 4294967296' => [undef]], [':: + 340282366920938463463374607431768211455' => ['0000:0000:0000:0000:0000:0000:0000:0000 - '. 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff']], [':: + 340282366920938463463374607431768211456' => [ undef ] ], [':: + 333333340282366920938463463374607431768211456' => [ undef ] ], ); for my $entry (@data) { my ($arg, $res) = @{$entry}; my @res_t = ip_normal_range($arg); is_deeply(\@res_t, $res, "Got normal range for $arg"); } 1; Net-IP-XS-0.22/t/37-compress-v6.t0000644000175000017500000000331514363576625014607 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 20; use Net::IP::XS qw(ip_compress_address Error Errno); my $res = ip_compress_address('ZZZZ', 0); is($res, undef, 'Got undef on bad IP address'); is(Error(), 'Cannot determine IP version for ZZZZ', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); $res = ip_compress_address('1.2.3.4', 4); is($res, '1.2.3.4', 'Original IP address returned for IPv4'); my @data = ( ['0000:0000:0000:0000:0000:0000:0000:0000' => '::'], ['2001:db8:0:1:1:1:1:1' => '2001:db8:0:1:1:1:1:1'], ['2001:db8:0:0:0:0:2:1' => '2001:db8::2:1'], ['2001:0:0:1:0:0:0:1' => '2001:0:0:1::1'], ['2001:db8:0:0:1:0:0:1' => '2001:db8::1:0:0:1'], ['ABCD:0000:EF01:0000:ABCD:0000:EF01:0000' => 'abcd:0:ef01:0:abcd:0:ef01:0'], ['ABCD:0000:0000:0000:ABCD:0000:EF01:0000' => 'abcd::abcd:0:ef01:0'], ['ABCD:0000:0000:0000::0000:EF01:0000' => 'abcd::ef01:0'], ['ABCD:0000:0000:0000:0000:0000:EF01:0000' => 'abcd::ef01:0'], ['ABCD:0000:EF01:0000:0000:ABCD:0000:EF01' => 'abcd:0:ef01::abcd:0:ef01'], ['000F:000F:FF0F:FFFF:000F:FFFF:FFFF:FFFF' => 'f:f:ff0f:ffff:f:ffff:ffff:ffff'], ['FFFF:0000:0000:0000:0000:0000:0000:FFFF' => 'ffff::ffff'], ['FFFF:0:0000:0000:0:0000:0:FFFF' => 'ffff::ffff'], ['FFFF:0:0000:FFFF:0:0000:0:FFFF' => 'ffff:0:0:ffff::ffff'], ['a:b:c:d:a:b:c:d' => 'a:b:c:d:a:b:c:d'], ['a:b:c:d:a:b:c:d/128' => undef], ); for my $entry (@data) { my ($arg, $res) = @{$entry}; my $res_t = ip_compress_address($arg, 6); is($res_t, $res, "Got compressed address for $arg"); } 1; Net-IP-XS-0.22/t/53-rt-118593.t0000644000175000017500000000075714363576625013625 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 4; use Net::IP::XS qw(ip_normalize); my @res = ip_normalize('1.2.3.0/24k'); is($Net::IP::XS::ERRNO, 172, "Got correct error number"); is($Net::IP::XS::ERROR, "Invalid prefix length /24k", "Got correct error message"); @res = ip_normalize('2001:2000::/33k'); is($Net::IP::XS::ERRNO, 172, "Got correct error number"); is($Net::IP::XS::ERROR, "Invalid prefix length /33k", "Got correct error message"); 1; Net-IP-XS-0.22/t/10-n128.t0000644000175000017500000001722514363576625013107 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 95; use Math::BigInt; use Net::IP::XS; my $nt = "Net::IP::XS::N128"; my $max_128_minus_1 = '340282366920938463463374607431768211454'; my $max_128 = '340282366920938463463374607431768211455'; # Unsigned integer operations. { my $n1 = $nt->new(); ok($n1, 'Got new N128 object'); $n1->set_ui(0); is($n1->cmp_ui(0), 0, '0 cmp 0 equals 0'); $n1->set_ui(1); is($n1->cmp_ui(0), 1, '1 cmp 0 equals 1'); $n1->set_ui(0); is($n1->cmp_ui(1), -1, '0 cmp 1 equals -1'); $n1->set_ui(1); $n1->blsft(1); is($n1->cmp_ui(2), 0, '1 << 1 equals 2'); $n1->set_ui(1); $n1->blsft(8); is($n1->cmp_ui(256), 0, '1 << 8 equals 256'); for my $i (9, 10, 11, 32, 64, 127) { $n1->set_ui(1); $n1->blsft($i); is($n1->cmp_ui(256), 1, "(1 << $i) cmp 256 equals 1"); } for my $i (1, 2, 3, 31, 32, 33, 64, 127) { $n1->set_ui(1); $n1->blsft($i); $n1->brsft($i); is($n1->cmp_ui(1), 0, "1 << $i >> $i equals 1"); } my $n2 = $nt->new(); $n2->set_ui(0); my @ui_tests = ( [ 'band', 1, 0, 0 ], [ 'band', 1, 1, 1 ], [ 'bior', 1, 0, 1 ], [ 'bior', 1, 1, 1 ], [ 'bior', 0, 0, 0 ], [ 'bxor', 1, 0, 1 ], [ 'bxor', 1, 1, 0 ], [ 'bxor', 0, 0, 0 ], [ 'badd', 0, 0, 0 ], [ 'badd', 1, 1, 2 ], [ 'badd', 100, 100, 200 ], [ 'badd', 256, 256, 512 ], [ 'bsub', 0, 0, 0 ], [ 'bsub', 0, 1, 0 ], [ 'bsub', 1, 0, 1 ], [ 'bsub', 1, 1, 0 ], [ 'bsub', 4294967295, 4294967294, 1 ], [ 'bsub', 4294967295, 1234567890, 3060399405 ], ); for my $data (@ui_tests) { my ($fn, $n1v, $n2v, $res) = @{$data}; $n1->set_ui($n1v); $n2->set_ui($n2v); $n1->$fn($n2); is($n1->cmp_ui($res), 0, "$n1v $fn $n2v equals $res"); } my @add_ui_tests = ( [ 0, 0, 0 ], [ 0, 1, 1 ], [ 1, 0, 1 ], [ 1, 1, 2 ], [ 100, 100, 200 ], [ 256, 256, 512 ], ); for my $data (@add_ui_tests) { my ($a, $b, $c) = @{$data}; $n1->set_ui($a); $n1->badd_ui($b); is($n1->cmp_ui($c), 0, "$a + $b equals $c (add unsigned integer)"); } } # Remaining operations. { my $n1 = $nt->new(); $n1->set_ui(0); my @set; for (my $i = 0; $i < 128; $i++) { if ($n1->tstbit($i)) { push @set, $i; } } ok((not @set), 'No bits set in number set to zero'); $n1->bnot(); my @not_set; for (my $i = 0; $i < 128; $i++) { if (not $n1->tstbit($i)) { push @not_set, $i; } } ok((not @not_set), 'All bits set in complement of zero'); for (my $i = 0; $i < 128; $i++) { $n1->clrbit($i); } @set = (); for (my $i = 0; $i < 128; $i++) { if ($n1->tstbit($i)) { push @set, $i; } } ok((not @set), 'Cleared all bits, no bits set'); for (my $i = 0; $i < 128; $i++) { $n1->setbit($i); } @not_set = (); for (my $i = 0; $i < 128; $i++) { if (not $n1->tstbit($i)) { push @not_set, $i; } } ok((not @not_set), 'Set all bits, all bits set'); $n1->set_ui(0); $n1->setbit(0); is($n1->cmp_ui(1), 0, 'Bit index begins from LSB (set)'); $n1->set_ui(0); $n1->clrbit(0); is($n1->cmp_ui(0), 0, 'Bit index begins from LSB (clear)'); $n1->setbit(100); ok($n1->tstbit(100), 'Bit index begins from LSB (test)'); $n1->set_ui(0); for (my $i = 0; $i < 8; $i++) { $n1->setbit($i); } is($n1->cmp_ui(255), 0, 'Setting lowest eight bits worked correctly'); my $n2 = $nt->new(); $n1->set_ui(0); $n2->set_ui(0); $n1->setbit(120); $n2->setbit(121); is($n1->cmp($n2), -1, 'Object comparison - less than'); is($n1->cmp($n1), 0, 'Object comparison - equality'); is($n2->cmp($n1), 1, 'Object comparison - more than'); $n1->set_ui(0); $n2->set_ui(0); $n1->setbit(90); $n2->setbit(91); is($n1->cmp($n2), -1, 'Object comparison - less than (2)'); is($n1->cmp($n1), 0, 'Object comparison - equality (2)'); is($n2->cmp($n1), 1, 'Object comparison - more than (2)'); $n1->set_ui(0); $n2->set_ui(0); $n1->setbit(55); $n2->setbit(56); is($n1->cmp($n2), -1, 'Object comparison - less than (3)'); is($n1->cmp($n1), 0, 'Object comparison - equality (3)'); is($n2->cmp($n1), 1, 'Object comparison - more than (3)'); $n1->set_ui(0); $n2->set_ui(0); $n1->setbit(27); $n2->setbit(28); is($n1->cmp($n2), -1, 'Object comparison - less than (4)'); is($n1->cmp($n1), 0, 'Object comparison - equality (4)'); is($n2->cmp($n1), 1, 'Object comparison - more than (4)'); $n1->set_binstr('1'); $n2->set_ui(1); is($n1->cmp($n2), 0, 'Set number based on bitstring (1)'); $n1->set_binstr('1' x 2); $n2->set_ui(3); is($n1->cmp($n2), 0, 'Set number based on bitstring (3)'); $n1->set_binstr('101'); $n2->set_ui(5); is($n1->cmp($n2), 0, 'Set number based on bitstring (5)'); $n1->set_binstr(('0' x 127) . '1'); $n2->set_ui(1); is($n1->cmp($n2), 0, 'Set number based on bitstring (1)'); $n1->set_binstr(('0' x 126) . '11'); $n2->set_ui(3); is($n1->cmp($n2), 0, 'Set number based on bitstring (3)'); $n1->set_binstr(('0' x 125) . ('1' x 128)); $n2->set_ui(7); is($n1->cmp($n2), 0, 'Set number based on bitstring (7, and too long)'); $n1->set_binstr(('0' x 96) . ('1' x 32)); $n2->set_ui(4294967295); is($n1->cmp($n2), 0, 'Set number based on bitstring ((1 << 32) - 1)'); $n1->set_ui(0); $n1->bnot(); $n2->set_ui(1); $n1->badd($n2); is($n1->cmp_ui(0), 0, 'Addition allows for overflow'); $n1->set_ui(0); $n1->bnot(); $n2->set_ui(100); $n1->badd($n2); is($n1->cmp_ui(99), 0, 'Addition allows for overflow (2)'); $n1->set_ui(0); $n1->bnot(); $n2->set_ui(1); $n2->badd($n1); is($n2->cmp_ui(0), 0, 'Addition allows for overflow (3)'); } # From/to decimal string. { my @nums = ( '0', '1', '2', '3', '1024', '10000', '65535', '65536', '12341234123412341234', $max_128_minus_1, $max_128 ); for my $num (@nums) { my $mb = Math::BigInt->new($num); my $n128 = $nt->new(); $n128->set_decstr($num); my $mbstr = $mb->bstr(); my $n128str = $n128->bstr(); is($n128str, $mbstr, "From/to string ($num)"); } my $n128 = $nt->new(); $n128->set_ui(0); $n128->set_decstr('340282366920938463463374607431768211456'); is($n128->bstr(), $max_128, 'If number is too large, N128 is set to (1 << 128) - 1'); } # From/to decimal string with operations. { my @tests = ( [ 'badd', '4294967295', '1', '4294967296' ], [ 'badd', '9223372036854775807', '4294967297', '9223372041149743104' ], [ 'badd', $max_128, '1', '0' ], [ 'badd', $max_128, '5', '4' ], [ 'badd', $max_128_minus_1, 1, $max_128 ], [ 'badd', $max_128_minus_1, 2, 0 ], [ 'bsub', '4294967296', '1', '4294967295' ], [ 'bsub', '9223372041149743104', '9223372036854775807', '4294967297' ], [ 'bsub', $max_128, 1, $max_128_minus_1 ], ); for my $data (@tests) { my ($fn, $n1v, $n2v, $res) = @{$data}; my $n1 = $nt->new(); my $n2 = $nt->new(); my $n3 = $nt->new(); $n1->set_decstr($n1v); $n2->set_decstr($n2v); $n3->set_decstr($res); $n1->$fn($n2); is($n1->cmp($n3), 0, "$n1v $fn $n2v equals $res"); } } 1; Net-IP-XS-0.22/t/13-bintoip.t0000644000175000017500000000321014363576625014053 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 16; use Net::IP::XS qw(ip_bintoip Error Errno); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); my $res = ip_bintoip('1' x 8, 4); is($res, '0.0.0.255', 'ip_bintoip 4'); $res = ip_bintoip('1' x 32, 4); is($res, '255.255.255.255', 'ip_bintoip 4'); $res = ip_bintoip('1', 4); is($res, '0.0.0.1', 'ip_bintoip 4'); $res = ip_bintoip('1' x 33, 4); is($res, undef, 'ip_bintoip invalid'); is(Error(), 'Invalid IP length for binary IP '.('1' x 33), 'Correct error message'); is(Errno(), 189, 'Correct error number'); $res = ip_bintoip('1', 6); is($res, '0000:0000:0000:0000:0000:0000:0000:0001', 'ip_bintoip 6'); $res = ip_bintoip('1' x 16, 6); is($res, '0000:0000:0000:0000:0000:0000:0000:ffff', 'ip_bintoip 6'); $res = ip_bintoip(('1' x 127).'0', 6); is($res, 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe', 'ip_bintoip 6'); $res = ip_bintoip('1' x 128, 6); is($res, 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'ip_bintoip 6'); $res = ip_bintoip('1' x 129, 6); is($res, undef, 'ip_bintoip invalid'); is(Error(), 'Invalid IP length for binary IP '. ('1' x 129), 'Correct error message'); is(Errno(), 189, 'Correct error number'); $c->start(); $res = ip_bintoip(undef, 0); $c->stop(); is($res, (join ':', ('0000') x 8), 'ip_bintoip returns zero address on undef and bad version'); $res = ip_bintoip('2020', 4); is($res, '0.0.0.0', 'ip_bintoip returns zero address on non-bitstring (4)'); $res = ip_bintoip('2020', 6); is($res, (join ':', ('0000') x 8), 'ip_bintoip returns zero address on non-bitstring (6)'); 1; Net-IP-XS-0.22/t/43-ip-add-num-v4.t0000644000175000017500000000250314363576625014700 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 16; use Net::IP::XS qw(:PROC); my $ip = Net::IP::XS->new('0/0'); is($ip->intip(), 0, 'Got correct intip'); is($ip->last_int(), '4294967295', 'Got correct last_int'); $ip += 16777216; is($ip->intip(), 16777216, 'Got correct intip after addition'); is($ip->last_int(), '4294967295', 'Got correct last_int after addition'); is($ip->print(), '1.0.0.0 - 255.255.255.255', 'Stringification after addition'); $ip = Net::IP::XS->new('0/32'); is($ip->intip(), 0, 'Got correct intip'); is($ip->last_int(), 0, 'Got correct last_int'); $ip += 1; is($ip, undef, 'Got undef on addition outside bounds'); $ip = Net::IP::XS->new('0/31'); is($ip->intip(), 0, 'Got correct intip'); is($ip->last_int(), 1, 'Got correct last_int'); $ip += 1; is($ip->intip(), 1, 'Got correct intip (+1)'); is($ip->last_int(), 1, 'Got correct last_int (+1)'); $ip += 1; is($ip, undef, 'Got undef on addition outside bounds'); $ip = Net::IP::XS->new('0/0'); my $count = 0; while ($ip = $ip + 10000000) { $count++; } is($count, 429, 'Addition failed at correct point'); $ip = Net::IP::XS->new('0/0'); $ip += '9' x 256; is($ip, undef, 'Got undef on trying to add a number that is too large'); $ip = Net::IP::XS->new('0'); $ip += -1; is($ip, undef, 'Got undef of trying to add negative number'); 1; Net-IP-XS-0.22/t/17-is-ipv6.t0000644000175000017500000000304314363576625013714 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 21; use Net::IP::XS qw(ip_is_ipv6 Error Errno); my $res = ip_is_ipv6('123.123.123.123'); is($res, 0, 'ip_is_ipv6 invalid (IPv4 address)'); $res = ip_is_ipv6(':123.123.123.123'); is($res, 0, 'ip_is_ipv6 invalid (starts with :)'); is(Error(), "Invalid address :123.123.123.123 (starts with :)", 'Got correct error'); is(Errno(), 109, 'Got correct errno'); $res = ip_is_ipv6('0000:'); is($res, 0, 'ip_is_ipv6 invalid (ends with :)'); is(Error(), "Invalid address 0000: (ends with :)", 'Got correct error'); is(Errno(), 110, 'Got correct errno'); $res = ip_is_ipv6('0000::0000::0000'); is($res, 0, 'ip_is_ipv6 invalid (multiple ::)'); is(Error(), "Invalid address 0000::0000::0000 (More than one :: pattern)", 'Got correct error'); is(Errno(), 111, 'Got correct errno'); $res = ip_is_ipv6('ABCDE::12345'); is($res, 0, 'ip_is_ipv6 invalid (parts too long)'); is(Errno(), 108, 'Correct errno'); $res = ip_is_ipv6('GGGG:FFFF'); is($res, 0, 'ip_is_ipv6 invalid (bad characters)'); is(Errno(), 108, 'Correct errno'); $res = ip_is_ipv6('1:2:3:4:5:6:7:8:9'); is($res, 0, 'ip_is_ipv6 invalid (too many colons 1)'); $res = ip_is_ipv6('1:2:3:4:5:6:7:8:9:0:1:2:3'); is($res, 0, 'ip_is_ipv6 invalid (too many colons 2)'); my @data = ( ['1:2:3:4:5:6:7:8' => 1], ['1234::5678' => 1], ['1234:5678:9ABC:DEF0:1234:5678:9ABC:DEF0' => 1], ['::123.123.123.123' => 1], ['::' => 1] ); for (@data) { my ($res, $res_exp) = @{$_}; is(ip_is_ipv6($res), $res_exp, "$res"); } 1; Net-IP-XS-0.22/t/18-get-version.t0000644000175000017500000000062514363576625014665 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 4; use Net::IP::XS qw(ip_get_version); my $res = ip_get_version('127.0.0.1'); is($res, 4, 'ip_get_version 4'); $res = ip_get_version('::'); is($res, 6, 'ip_get_version 6 1'); $res = ip_get_version('2000::'); is($res, 6, 'ip_get_version 6 2'); $res = ip_get_version('2000::.asdf'); is($res, undef, 'ip_get_version invalid'); 1; Net-IP-XS-0.22/t/23-expand-address.t0000644000175000017500000000632414363576625015323 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 45; use Net::IP::XS qw(ip_expand_address); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); $c->start(); is(ip_expand_address(undef, 4), '0.0.0.0', 'Got zero address on undef (IPv4)'); is(ip_expand_address(undef, 6), (join ':', ('0000') x 8), 'Got zero address on undef (IPv6)'); $c->stop(); is(ip_expand_address('', 4), '0.0.0.0', 'Got zero address on empty string (IPv4)'); is(ip_expand_address('', 6), (join ':', ('0000') x 8), 'Got zero address on string (IPv6)'); is(ip_expand_address('ZXCV', 4), undef, 'Got undef on invalid IPv4 address'); is(ip_expand_address('ZXCV', 6), undef, 'Got undef on invalid IPv4 address'); my @data = ( ['0' => '0.0.0.0'], ['0.0' => '0.0.0.0'], ['0.0.0' => '0.0.0.0'], ['0.0.0.0' => '0.0.0.0'], ['0.1.2.3' => '0.1.2.3'], ['255.255.255.255' => '255.255.255.255'], ['1' => '1.0.0.0'], ['1.2' => '1.2.0.0'], ['1.2.3' => '1.2.3.0'], ['1.2.3.4.5' => undef], ); for (@data) { my ($key, $value) = @{$_}; is(ip_expand_address($key, 4), $value, "v4: $key"); } @data = ( ['::' => '0000:0000:0000:0000:0000:0000:0000:0000'], ['::1234' => '0000:0000:0000:0000:0000:0000:0000:1234'], ['1234::' => '1234:0000:0000:0000:0000:0000:0000:0000'], ['1234:5678::' => '1234:5678:0000:0000:0000:0000:0000:0000'], ['1234::5678' => '1234:0000:0000:0000:0000:0000:0000:5678'], ['0::0' => '0000:0000:0000:0000:0000:0000:0000:0000'], ['0:0:0:0:0:0:0:0' => '0000:0000:0000:0000:0000:0000:0000:0000'], ['0000:0000:0000:0000:0000:0000:0000:0000' => '0000:0000:0000:0000:0000:0000:0000:0000'], ['1234:5678::ABCD:EF12' => '1234:5678:0000:0000:0000:0000:abcd:ef12'], ['1234:5678::ABCD:EF12:3456:7890' => '1234:5678:0000:0000:abcd:ef12:3456:7890'], ['1234:5678:ABCD::ABCD:EF12:3456:7890' => '1234:5678:abcd:0000:abcd:ef12:3456:7890'], ['1.2.3.4::' => '0102:0304:0000:0000:0000:0000:0000:0000'], ['1.2.3.4:255.255.255.255::' => '0102:0304:ffff:ffff:0000:0000:0000:0000'], ['255.255.255.255:255.255.255.255::' => 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'], ['255.255.255.255:255.255.255.255:255.255.255.255:255.255.255.255' => 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'], ['255.255.255.255:255.255.255.255:255.255.255.255:'. '255.255.255.255:255.255.255.255' => undef], ['1.2.3.4:100.100.100.100::' => '0102:0304:6464:6464:0000:0000:0000:0000' ], ['A:B:C:D::' => '000a:000b:000c:000d:0000:0000:0000:0000'], ['ff00::1234', => 'ff00:0000:0000:0000:0000:0000:0000:1234'], ['0', '0000:0000:0000:0000:0000:0000:0000:0000'], [':FFFF' => undef], ['FFFFF' => undef], ['FFFF:' => undef], ['FFFF:::' => undef], ['0000:0000:0000:0000:0000:0000:0000:0000::' => undef], ['0000:0000:0000:0000:123.123.123.123.123.123:0000:0000' => undef], ['1:2:3:4:1:2:3:4:1:2:3:4' => undef], ['1:2:3:4:1:2:3:4:1' => undef], ['1111:2222:3333:4444:1111:2222:3333:4444:1111:2222:3333:4444' => undef], ); for my $entry (@data) { my ($key, $value) = @{$entry}; is(ip_expand_address($key, 6), $value, "v6: $key"); } 1; Net-IP-XS-0.22/t/42-tags.t0000644000175000017500000000175514363576625013363 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 36; use Net::IP::XS qw(:PROC); for (qw(Error Errno ip_iptobin ip_bintoip ip_iplengths ip_bintoint ip_inttobin ip_expand_address ip_is_ipv4 ip_is_ipv6 ip_get_version ip_get_mask ip_last_address_bin ip_splitprefix ip_is_valid_mask ip_bincomp ip_binadd ip_get_prefix_length ip_compress_v4_prefix ip_is_overlap ip_check_prefix ip_range_to_prefix ip_get_embedded_ipv4 ip_aggregate ip_prefix_to_range ip_reverse ip_normalize ip_compress_address ip_iptype ip_auth ip_normal_range)) { ok(main->can($_), "Imported function $_ with PROC"); } is($IP_NO_OVERLAP, 0, 'IP_NO_OVERLAP has correct value'); is($IP_PARTIAL_OVERLAP, 1, 'IP_PARTIAL_OVERLAP has correct value'); is($IP_A_IN_B_OVERLAP, -1, 'IP_A_IN_B_OVERLAP has correct value'); is($IP_B_IN_A_OVERLAP, -2, 'IP_B_IN_A_OVERLAP has correct value'); is($IP_IDENTICAL, -3, 'IP_IDENTICAL has correct value'); 1; Net-IP-XS-0.22/t/01-pod.t0000644000175000017500000000036314363576625013174 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More; # Ensure a recent version of Test::Pod my $min_tp = 1.22; eval "use Test::Pod $min_tp"; plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; all_pod_files_ok(); Net-IP-XS-0.22/t/10-basic.t0000644000175000017500000000234114363576625013471 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 11; use Net::IP::XS qw( $IP_NO_OVERLAP $IP_PARTIAL_OVERLAP $IP_A_IN_B_OVERLAP $IP_B_IN_A_OVERLAP $IP_IDENTICAL ); use IO::Capture::Stderr; my $c = IO::Capture::Stderr->new(); $Net::IP::XS::ERROR = 'asdf'; is($Net::IP::XS::ERROR, 'asdf', 'Error stored correctly'); $Net::IP::XS::ERRNO = 1234; is($Net::IP::XS::ERRNO, 1234, 'Errno stored correctly'); $c->start(); $Net::IP::XS::ERROR = undef; $Net::IP::XS::ERRNO = undef; $c->stop(); is($Net::IP::XS::ERROR, '', 'Error is string when set to undef'); is($Net::IP::XS::ERRNO, 0, 'Errno is number when set to undef'); $Net::IP::XS::ERROR = 'e' x 1024; is($Net::IP::XS::ERROR, 'e' x 511, 'Error messaged truncated properly'); $c->start(); $Net::IP::XS::ERRNO = 'not a number'; $c->stop(); is($Net::IP::XS::ERRNO, 0, 'Errno is zero after setting as string'); is($IP_NO_OVERLAP, 0, 'IP_NO_OVERLAP has correct value'); is($IP_PARTIAL_OVERLAP, 1, 'IP_PARTIAL_OVERLAP has correct value'); is($IP_A_IN_B_OVERLAP, -1, 'IP_A_IN_B_OVERLAP has correct value'); is($IP_B_IN_A_OVERLAP, -2, 'IP_B_IN_A_OVERLAP has correct value'); is($IP_IDENTICAL, -3, 'IP_IDENTICAL has correct value'); 1; Net-IP-XS-0.22/t/52-rt-118605.t0000644000175000017500000000047714363576625013615 0ustar tomhtomh#!/usr/bin/perl use warnings; use strict; use Test::More tests => 1; use Config; use Math::BigInt; use Net::IP::XS qw(ip_splitprefix); my $large_len = Math::BigInt->new(1)->blsft(4096)->bmul(-1); my @res = ip_splitprefix("1.2.3.4/$large_len"); is_deeply(\@res, [], 'No result when length unable to be parsed'); 1; Net-IP-XS-0.22/t/33-prefix-to-range.t0000644000175000017500000000400714363576625015425 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 24; use Net::IP::XS qw(ip_prefix_to_range Error Errno); my $res = ip_prefix_to_range('a', 15, 0); is($res, undef, 'Got undef on no version'); is(Error(), 'Cannot determine IP version', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); $res = ip_prefix_to_range('123.123.123.123.123', 16, 4); is($res, undef, 'Got undef when unable to expand first address'); $res = ip_prefix_to_range('1.2.3.4', 16, 4); is($res, undef, 'Got undef when address and prefix do not go together'); my @data = ( [['0.0.0.0', 32, 4] => ['0.0.0.0', '0.0.0.0']], [['0.0.0.0', 31, 4] => ['0.0.0.0', '0.0.0.1']], [['0.0.0.0', 30, 4] => ['0.0.0.0', '0.0.0.3']], [['0.0.0.0', 29, 4] => ['0.0.0.0', '0.0.0.7']], [['0.0.0.0', 28, 4] => ['0.0.0.0', '0.0.0.15']], [['0.0.0.0', 27, 4] => ['0.0.0.0', '0.0.0.31']], [['1.2.3.0', 24, 4] => ['1.2.3.0', '1.2.3.255']], [['1.2.2.0', 23, 4] => ['1.2.2.0', '1.2.3.255']], [['1.2.0.0', 22, 4] => ['1.2.0.0', '1.2.3.255']], [['1.2.4.0', 22, 4] => ['1.2.4.0', '1.2.7.255']], [['1.2.0.0', 21, 4] => ['1.2.0.0', '1.2.7.255']], [['1.2.0.0', 20, 4] => ['1.2.0.0', '1.2.15.255']], [['100.100.16.0', 20, 4] => ['100.100.16.0', '100.100.31.255']], [['0.0.0.0', 1, 4] => ['0.0.0.0', '127.255.255.255']], [['0.0.0.0', 0, 4] => ['0.0.0.0', '255.255.255.255']], [['255.255.255.255', 32, 4] => ['255.255.255.255', '255.255.255.255']], [['1.0.0.0', 8, 4] => ['1.0.0.0', '1.255.255.255']], [[(join ':', ('0000') x 8), 0, 6] => [(join ':', ('0000') x 8), (join ':', ('ffff') x 8)]], [[(join ':', ('0000') x 8), 1, 6] => [(join ':', ('0000') x 8), (join ':', '7fff', ('ffff') x 7)]], ); for my $entry (@data) { my ($args, $res) = @{$entry}; my ($ip, $len, $ver) = @{$args}; my @res_t = ip_prefix_to_range($ip, $len, $ver); is_deeply(\@res_t, $res, "Got correct results for $ip, $len, $ver"); } 1; Net-IP-XS-0.22/t/44-ip-add-num-v6.t0000644000175000017500000000353214363576625014706 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 18; use Net::IP::XS qw(:PROC); my $ip = Net::IP::XS->new('::/0'); is($ip->intip(), 0, 'Got correct intip'); is($ip->last_int(), '340282366920938463463374607431768211455', 'Got correct last_int'); $ip += 16777216; is($ip->intip(), 16777216, 'Got correct intip after addition'); is($ip->last_int(), '340282366920938463463374607431768211455', 'Got correct last_int after addition'); is($ip->print(), '0000:0000:0000:0000:0000:0000:0100:0000 - '. 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'Stringification after addition'); $ip = Net::IP::XS->new('::/128'); is($ip->intip(), 0, 'Got correct intip'); is($ip->last_int(), 0, 'Got correct last_int'); $ip += 1; is($ip, undef, 'Got undef on addition outside bounds'); $ip = Net::IP::XS->new('::/127'); is($ip->intip(), 0, 'Got correct intip'); is($ip->last_int(), 1, 'Got correct last_int'); $ip += 1; is($ip->intip(), 1, 'Got correct intip (+1)'); is($ip->last_int(), 1, 'Got correct last_int (+1)'); $ip += 1; is($ip, undef, 'Got undef on addition outside bounds'); $ip = Net::IP::XS->new('::/0'); my $count = 0; while ($ip = $ip + '1000000000000000000000000000000000000') { $count++; } is($count, 340, 'Addition failed at correct point'); $ip = Net::IP::XS->new('::/0'); $ip += '340282366920938463463374607431768211456'; is($ip, undef, 'Got undef on trying to add a number that is too large (1)'); $ip = Net::IP::XS->new('::/0'); $ip += '9' x 256; is($ip, undef, 'Got undef on trying to add a number that is too large'); $ip = Net::IP::XS->new('::/128'); $ip += -1; is($ip, undef, 'Got undef on trying to add negative number'); $ip = Net::IP::XS->new('::/0'); $ip += '340282366920938463463374607431768211455'; is($ip->ip(), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'IP set correctly (added largest possible integer'); 1; Net-IP-XS-0.22/t/35-normalize.t0000644000175000017500000000613314363576625014422 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 53; use Net::IP::XS qw(ip_normalize); my @data = ( ['1.0.0.1/32' => ['1.0.0.1', '1.0.0.1']], ['0.0.0.0/24' => ['0.0.0.0', '0.0.0.255']], ['0.0.0.0/0' => ['0.0.0.0', '255.255.255.255']], ['1.2/16' => ['1.2.0.0', '1.2.255.255']], ['0::0/0' => ['0000:0000:0000:0000:0000:0000:0000:0000', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff']], ['AAAA::/16' => ['aaaa:0000:0000:0000:0000:0000:0000:0000', 'aaaa:ffff:ffff:ffff:ffff:ffff:ffff:ffff']], ['1:2:3:4::/64' => ['0001:0002:0003:0004:0000:0000:0000:0000', '0001:0002:0003:0004:ffff:ffff:ffff:ffff']], ['0.0.0.0/32,' => ['0.0.0.0', '0.0.0.1']], ['0.0.0.0/32||||' => []], ['2401:4600::/33k' => []], ['1.2.3.4/24asdf' => []], ['0.0.0.0/32,/32,/32' => ['0.0.0.0', '0.0.0.2']], ['0/8,/8,/8' => ['0.0.0.0', '2.255.255.255']], ['0/8,/8,/8,/24' => ['0.0.0.0', '3.0.0.255']], ['asdf/asdf' => []], ['-asdf' => []], ['-' => []], ['255-' => []], ['255- ' => []], ['- ' => []], ['asdf - 128.0.0.1' => []], ['0' => ['0.0.0.0']], ['0::0' => ['0000:0000:0000:0000:0000:0000:0000:0000']], ['0.0.0.0-1.0.0.0' => ['0.0.0.0', '1.0.0.0']], ['0-255' => ['0.0.0.0', '255.0.0.0']], ['0-255.255' => ['0.0.0.0', '255.255.0.0']], ['1.2.3.4 - 5.6.7.8' => ['1.2.3.4', '5.6.7.8']], ['0::0-1::1' => ['0000:0000:0000:0000:0000:0000:0000:0000', '0001:0000:0000:0000:0000:0000:0000:0001']], ['abcd::-abce::' => ['abcd:0000:0000:0000:0000:0000:0000:0000', 'abce:0000:0000:0000:0000:0000:0000:0000']], ['0.0.0.0 + 1' => ['0.0.0.0', '0.0.0.1']], ['0.0.0.0 + 255' => ['0.0.0.0', '0.0.0.255']], ['0.0.0.0 + 256' => ['0.0.0.0', '0.0.1.0']], ['1.0.0.0 + 65535' => ['1.0.0.0', '1.0.255.255']], ['0.0.0.0+1' => ['0.0.0.0', '0.0.0.1']], ['0.0.0.0+255' => ['0.0.0.0', '0.0.0.255']], ['0.0.0.0+256' => ['0.0.0.0', '0.0.1.0']], ['1.0.0.0+65535' => ['1.0.0.0', '1.0.255.255']], ['1.0.0.0+1234567812389123578457847543278547345' => []], ['0::0 + 65536' => ['0000:0000:0000:0000:0000:0000:0000:0000', '0000:0000:0000:0000:0000:0000:0001:0000']], ['0000:: - FFFF::' => ['0000:0000:0000:0000:0000:0000:0000:0000', 'ffff:0000:0000:0000:0000:0000:0000:0000']], ['0::0/0' => ['0000:0000:0000:0000:0000:0000:0000:0000', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff']], ['0::0/128,/128,/128,/128,/128,/128' => [ '0000:0000:0000:0000:0000:0000:0000:0000', '0000:0000:0000:0000:0000:0000:0000:0005']], ['1' x 2500 => []], ['abcd::/ab' => []], ['abcd::/1' => []], ['abcd::/200' => []], ['abcd::/-200' => []], ['1.2.3.4/ab' => []], ['1.2.3.4/100' => []], ['1.2.3.4/-100' => []], ['1.2.3.4/0' => []], ['1.2.3.4 + ABCD' => []], ['ZZZZ' => []], ); for my $entry (@data) { my ($arg, $res) = @{$entry}; my @res_t = ip_normalize($arg); is_deeply(\@res_t, $res, "Got normalized result for $arg"); } 1; Net-IP-XS-0.22/t/30-range-to-prefix.t0000644000175000017500000003017314363576625015425 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 20; use Net::IP::XS qw(ip_range_to_prefix ip_iptobin Error Errno); my $res; my @res; @res = ip_range_to_prefix('1', '1', 0); is_deeply(\@res, [], 'No results on no version'); is(Error(), 'Cannot determine IP version', 'Got correct error'); is(Errno(), 101, 'Got correct errno'); @res = ip_range_to_prefix('1', '1', 8); is_deeply(\@res, [], 'No results on bad version'); @res = ip_range_to_prefix('10', '1', 4); is_deeply(\@res, [], 'No results on different lengths'); is(Error(), 'IP addresses of different length', 'Got correct error'); is(Errno(), 130, 'Got correct errno'); @res = ip_range_to_prefix('0' x 256, '1' x 256, 4); ok(1, 'Call returned where bitstrings too long (IPv4)'); @res = ip_range_to_prefix('0' x 512, '1' x 512, 6); ok(1, 'Call returned where bitstrings too long (IPv6)'); @res = ip_range_to_prefix( ip_iptobin('127.0.0.1', 4), ip_iptobin('127.0.0.255', 4), 4 ); is_deeply( \@res, [ '127.0.0.1/32', '127.0.0.2/31', '127.0.0.4/30', '127.0.0.8/29', '127.0.0.16/28', '127.0.0.32/27', '127.0.0.64/26', '127.0.0.128/25' ], 'ip_range_to_prefix 1' ); @res = ip_range_to_prefix( ip_iptobin('127.0.0.0', 4), ip_iptobin('127.0.0.255', 4), 4 ); is_deeply(\@res, ['127.0.0.0/24'], 'ip_range_to_prefix 2'); @res = ip_range_to_prefix( ip_iptobin('127.0.0.0', 4), ip_iptobin('128.0.0.0', 4), 4 ); is_deeply(\@res, ['127.0.0.0/8', '128.0.0.0/32'], 'ip_range_to_prefix 3'); @res = ip_range_to_prefix( ip_iptobin('0.0.0.0', 4), ip_iptobin('255.255.255.255', 4), 4 ); is_deeply(\@res, ['0.0.0.0/0'], 'ip_range_to_prefix 4'); @res = ip_range_to_prefix( ip_iptobin('0.0.0.1', 4), ip_iptobin('255.255.255.255', 4), 4 ); is_deeply( \@res, [ '0.0.0.1/32', '0.0.0.2/31', '0.0.0.4/30', '0.0.0.8/29', '0.0.0.16/28', '0.0.0.32/27', '0.0.0.64/26', '0.0.0.128/25', '0.0.1.0/24', '0.0.2.0/23', '0.0.4.0/22', '0.0.8.0/21', '0.0.16.0/20', '0.0.32.0/19', '0.0.64.0/18', '0.0.128.0/17', '0.1.0.0/16', '0.2.0.0/15', '0.4.0.0/14', '0.8.0.0/13', '0.16.0.0/12', '0.32.0.0/11', '0.64.0.0/10', '0.128.0.0/9', '1.0.0.0/8', '2.0.0.0/7', '4.0.0.0/6', '8.0.0.0/5', '16.0.0.0/4', '32.0.0.0/3', '64.0.0.0/2', '128.0.0.0/1' ], 'ip_range_to_prefix 5' ); @res = ip_range_to_prefix( ip_iptobin('0.0.0.0', 4), ip_iptobin('255.255.255.254', 4), 4 ); is_deeply( \@res, [ '0.0.0.0/1', '128.0.0.0/2', '192.0.0.0/3', '224.0.0.0/4', '240.0.0.0/5', '248.0.0.0/6', '252.0.0.0/7', '254.0.0.0/8', '255.0.0.0/9', '255.128.0.0/10', '255.192.0.0/11', '255.224.0.0/12', '255.240.0.0/13', '255.248.0.0/14', '255.252.0.0/15', '255.254.0.0/16', '255.255.0.0/17', '255.255.128.0/18', '255.255.192.0/19', '255.255.224.0/20', '255.255.240.0/21', '255.255.248.0/22', '255.255.252.0/23', '255.255.254.0/24', '255.255.255.0/25', '255.255.255.128/26', '255.255.255.192/27', '255.255.255.224/28', '255.255.255.240/29', '255.255.255.248/30', '255.255.255.252/31', '255.255.255.254/32' ], 'ip_range_to_prefix 6' ); # 2000::, 2020:: @res = ip_range_to_prefix( '00100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '00100000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 6 ); is_deeply( \@res, [ '2000:0000:0000:0000:0000:0000:0000:0000/11', '2020:0000:0000:0000:0000:0000:0000:0000/128' ], 'ip_range_to_prefix 7' ); @res = ip_range_to_prefix( ip_iptobin('AAAA:0000:0000:0000:0000:0000:0000:0000', 6), ip_iptobin('AAAA:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 6), 6 ); is_deeply(\@res, ['aaaa:0000:0000:0000:0000:0000:0000:0000/16'], 'ip_range_to_prefix 8'); @res = ip_range_to_prefix( ip_iptobin('0000:0000:0000:0000:0000:0000:0000:0000', 6), ip_iptobin('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 6), 6 ); is_deeply(\@res, ['0000:0000:0000:0000:0000:0000:0000:0000/0'], 'ip_range_to_prefix 9'); @res = ip_range_to_prefix( ip_iptobin('0000:0000:0000:0000:0000:0000:0000:0000', 6), ip_iptobin('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE', 6), 6 ); is_deeply(\@res, [ '0000:0000:0000:0000:0000:0000:0000:0000/1', '8000:0000:0000:0000:0000:0000:0000:0000/2', 'c000:0000:0000:0000:0000:0000:0000:0000/3', 'e000:0000:0000:0000:0000:0000:0000:0000/4', 'f000:0000:0000:0000:0000:0000:0000:0000/5', 'f800:0000:0000:0000:0000:0000:0000:0000/6', 'fc00:0000:0000:0000:0000:0000:0000:0000/7', 'fe00:0000:0000:0000:0000:0000:0000:0000/8', 'ff00:0000:0000:0000:0000:0000:0000:0000/9', 'ff80:0000:0000:0000:0000:0000:0000:0000/10', 'ffc0:0000:0000:0000:0000:0000:0000:0000/11', 'ffe0:0000:0000:0000:0000:0000:0000:0000/12', 'fff0:0000:0000:0000:0000:0000:0000:0000/13', 'fff8:0000:0000:0000:0000:0000:0000:0000/14', 'fffc:0000:0000:0000:0000:0000:0000:0000/15', 'fffe:0000:0000:0000:0000:0000:0000:0000/16', 'ffff:0000:0000:0000:0000:0000:0000:0000/17', 'ffff:8000:0000:0000:0000:0000:0000:0000/18', 'ffff:c000:0000:0000:0000:0000:0000:0000/19', 'ffff:e000:0000:0000:0000:0000:0000:0000/20', 'ffff:f000:0000:0000:0000:0000:0000:0000/21', 'ffff:f800:0000:0000:0000:0000:0000:0000/22', 'ffff:fc00:0000:0000:0000:0000:0000:0000/23', 'ffff:fe00:0000:0000:0000:0000:0000:0000/24', 'ffff:ff00:0000:0000:0000:0000:0000:0000/25', 'ffff:ff80:0000:0000:0000:0000:0000:0000/26', 'ffff:ffc0:0000:0000:0000:0000:0000:0000/27', 'ffff:ffe0:0000:0000:0000:0000:0000:0000/28', 'ffff:fff0:0000:0000:0000:0000:0000:0000/29', 'ffff:fff8:0000:0000:0000:0000:0000:0000/30', 'ffff:fffc:0000:0000:0000:0000:0000:0000/31', 'ffff:fffe:0000:0000:0000:0000:0000:0000/32', 'ffff:ffff:0000:0000:0000:0000:0000:0000/33', 'ffff:ffff:8000:0000:0000:0000:0000:0000/34', 'ffff:ffff:c000:0000:0000:0000:0000:0000/35', 'ffff:ffff:e000:0000:0000:0000:0000:0000/36', 'ffff:ffff:f000:0000:0000:0000:0000:0000/37', 'ffff:ffff:f800:0000:0000:0000:0000:0000/38', 'ffff:ffff:fc00:0000:0000:0000:0000:0000/39', 'ffff:ffff:fe00:0000:0000:0000:0000:0000/40', 'ffff:ffff:ff00:0000:0000:0000:0000:0000/41', 'ffff:ffff:ff80:0000:0000:0000:0000:0000/42', 'ffff:ffff:ffc0:0000:0000:0000:0000:0000/43', 'ffff:ffff:ffe0:0000:0000:0000:0000:0000/44', 'ffff:ffff:fff0:0000:0000:0000:0000:0000/45', 'ffff:ffff:fff8:0000:0000:0000:0000:0000/46', 'ffff:ffff:fffc:0000:0000:0000:0000:0000/47', 'ffff:ffff:fffe:0000:0000:0000:0000:0000/48', 'ffff:ffff:ffff:0000:0000:0000:0000:0000/49', 'ffff:ffff:ffff:8000:0000:0000:0000:0000/50', 'ffff:ffff:ffff:c000:0000:0000:0000:0000/51', 'ffff:ffff:ffff:e000:0000:0000:0000:0000/52', 'ffff:ffff:ffff:f000:0000:0000:0000:0000/53', 'ffff:ffff:ffff:f800:0000:0000:0000:0000/54', 'ffff:ffff:ffff:fc00:0000:0000:0000:0000/55', 'ffff:ffff:ffff:fe00:0000:0000:0000:0000/56', 'ffff:ffff:ffff:ff00:0000:0000:0000:0000/57', 'ffff:ffff:ffff:ff80:0000:0000:0000:0000/58', 'ffff:ffff:ffff:ffc0:0000:0000:0000:0000/59', 'ffff:ffff:ffff:ffe0:0000:0000:0000:0000/60', 'ffff:ffff:ffff:fff0:0000:0000:0000:0000/61', 'ffff:ffff:ffff:fff8:0000:0000:0000:0000/62', 'ffff:ffff:ffff:fffc:0000:0000:0000:0000/63', 'ffff:ffff:ffff:fffe:0000:0000:0000:0000/64', 'ffff:ffff:ffff:ffff:0000:0000:0000:0000/65', 'ffff:ffff:ffff:ffff:8000:0000:0000:0000/66', 'ffff:ffff:ffff:ffff:c000:0000:0000:0000/67', 'ffff:ffff:ffff:ffff:e000:0000:0000:0000/68', 'ffff:ffff:ffff:ffff:f000:0000:0000:0000/69', 'ffff:ffff:ffff:ffff:f800:0000:0000:0000/70', 'ffff:ffff:ffff:ffff:fc00:0000:0000:0000/71', 'ffff:ffff:ffff:ffff:fe00:0000:0000:0000/72', 'ffff:ffff:ffff:ffff:ff00:0000:0000:0000/73', 'ffff:ffff:ffff:ffff:ff80:0000:0000:0000/74', 'ffff:ffff:ffff:ffff:ffc0:0000:0000:0000/75', 'ffff:ffff:ffff:ffff:ffe0:0000:0000:0000/76', 'ffff:ffff:ffff:ffff:fff0:0000:0000:0000/77', 'ffff:ffff:ffff:ffff:fff8:0000:0000:0000/78', 'ffff:ffff:ffff:ffff:fffc:0000:0000:0000/79', 'ffff:ffff:ffff:ffff:fffe:0000:0000:0000/80', 'ffff:ffff:ffff:ffff:ffff:0000:0000:0000/81', 'ffff:ffff:ffff:ffff:ffff:8000:0000:0000/82', 'ffff:ffff:ffff:ffff:ffff:c000:0000:0000/83', 'ffff:ffff:ffff:ffff:ffff:e000:0000:0000/84', 'ffff:ffff:ffff:ffff:ffff:f000:0000:0000/85', 'ffff:ffff:ffff:ffff:ffff:f800:0000:0000/86', 'ffff:ffff:ffff:ffff:ffff:fc00:0000:0000/87', 'ffff:ffff:ffff:ffff:ffff:fe00:0000:0000/88', 'ffff:ffff:ffff:ffff:ffff:ff00:0000:0000/89', 'ffff:ffff:ffff:ffff:ffff:ff80:0000:0000/90', 'ffff:ffff:ffff:ffff:ffff:ffc0:0000:0000/91', 'ffff:ffff:ffff:ffff:ffff:ffe0:0000:0000/92', 'ffff:ffff:ffff:ffff:ffff:fff0:0000:0000/93', 'ffff:ffff:ffff:ffff:ffff:fff8:0000:0000/94', 'ffff:ffff:ffff:ffff:ffff:fffc:0000:0000/95', 'ffff:ffff:ffff:ffff:ffff:fffe:0000:0000/96', 'ffff:ffff:ffff:ffff:ffff:ffff:0000:0000/97', 'ffff:ffff:ffff:ffff:ffff:ffff:8000:0000/98', 'ffff:ffff:ffff:ffff:ffff:ffff:c000:0000/99', 'ffff:ffff:ffff:ffff:ffff:ffff:e000:0000/100', 'ffff:ffff:ffff:ffff:ffff:ffff:f000:0000/101', 'ffff:ffff:ffff:ffff:ffff:ffff:f800:0000/102', 'ffff:ffff:ffff:ffff:ffff:ffff:fc00:0000/103', 'ffff:ffff:ffff:ffff:ffff:ffff:fe00:0000/104', 'ffff:ffff:ffff:ffff:ffff:ffff:ff00:0000/105', 'ffff:ffff:ffff:ffff:ffff:ffff:ff80:0000/106', 'ffff:ffff:ffff:ffff:ffff:ffff:ffc0:0000/107', 'ffff:ffff:ffff:ffff:ffff:ffff:ffe0:0000/108', 'ffff:ffff:ffff:ffff:ffff:ffff:fff0:0000/109', 'ffff:ffff:ffff:ffff:ffff:ffff:fff8:0000/110', 'ffff:ffff:ffff:ffff:ffff:ffff:fffc:0000/111', 'ffff:ffff:ffff:ffff:ffff:ffff:fffe:0000/112', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000/113', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:8000/114', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:c000/115', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:e000/116', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:f000/117', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:f800/118', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00/119', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fe00/120', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00/121', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80/122', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffc0/123', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0/124', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0/125', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8/126', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc/127', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe/128' ], 'ip_range_to_prefix 10'); @res = ip_range_to_prefix( ip_iptobin('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 6), ip_iptobin('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 6), 6 ); is_deeply(\@res, [ 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128' ], 'ip_range_to_prefix 11'); 1; Net-IP-XS-0.22/t/29-check-prefix.t0000644000175000017500000000246214363576625014776 0ustar tomhtomh#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 14; use Net::IP::XS qw(ip_check_prefix Error Errno); use IO::Capture::Stderr; my $c= IO::Capture::Stderr->new(); my $res; $c->start(); $res = ip_check_prefix(undef, undef, undef); $c->stop(); is($res, 1, 'Prefix is correct where all arguments are undef'); $res = ip_check_prefix('1010', '5', 4); is($res, undef, 'Got correct result when prefix length too large'); is(Error(), "Prefix length 5 is longer than IP address (4)", 'Got correct error'); is(Errno(), 170, 'Got correct errno'); $res = ip_check_prefix('1010', '2', 4); is($res, undef, 'Got correct result when prefix part not only zeroes'); is(Error(), 'Invalid prefix 1010/2', 'Got correct error'); is(Errno(), 171, 'Got correct errno'); $res = ip_check_prefix('10000000', '2', 4); is($res, undef, 'Got correct result when IP address too small'); is(Error(), "Invalid prefix length /2", 'Got correct error'); is(Errno(), 172, 'Got correct errno'); $res = ip_check_prefix('1010', -10, 6); is($res, undef, 'Got correct result when negative length provided'); is(Error(), 'Invalid prefix length /-10', 'Got correct error'); is(Errno(), 172, 'Got correct errno'); $res = ip_check_prefix('10000000000000000000000000000000', '1', 4); is($res, 1, 'Valid prefix'); 1; Net-IP-XS-0.22/functions.c0000644000175000017500000021352714363577211013720 0ustar tomhtomh/* functions.c - Core functions for Net::IP::XS. Copyright (C) 2010-2023 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "functions.h" #include "inet_pton.h" #ifdef __cplusplus extern "C" { #endif /* Global error string and error number (tied to $Net::IP::XS::ERROR * and $Net::IP::XS::ERRNO). */ static char netip_Error[512]; static int netip_Errno; /** * NI_hv_get_pv(): get string value from hashref. * @object: reference to a hashref. * @key: hashref key as a string. * @keylen: the length of the hashref key. * * Returns a pointer to the beginning of the string to which @key is * mapped in the hashref. If @key does not exist in the hashref, * returns 0. */ const char* NI_hv_get_pv(SV *object, const char *key, int keylen) { SV **ref; ref = hv_fetch((HV*) SvRV(object), key, keylen, 0); if (!ref) { return NULL; } return SvPV(*ref, PL_na); } /** * NI_hv_get_iv(): get integer value from hashref. * @object: reference to a hashref. * @key: hashref key as a string. * @keylen: the length of the hashref key. * * Returns the integer to which @key is mapped in the hashref. If * @key does not exist in the hashref, returns 0. */ int NI_hv_get_iv(SV *object, const char *key, int keylen) { SV **ref; ref = hv_fetch((HV*) SvRV(object), key, keylen, 0); if (!ref) { return -1; } return SvIV(*ref); } /** * NI_hv_get_uv(): get unsigned integer value from hashref. * @object: reference to a hashref. * @key: hashref key as a string. * @keylen: the length of the hashref key. * * Returns the unsigned integer to which @key is mapped in the * hashref. If @key does not exist in the hashref, returns 0. */ unsigned int NI_hv_get_uv(SV *object, const char *key, int keylen) { SV **ref; ref = hv_fetch((HV*) SvRV(object), key, keylen, 0); if (!ref) { return -1; } return SvUV(*ref); } /** * NI_set_Errno() - set the global error number. * @Errno: the new error number. */ void NI_set_Errno(int Errno) { netip_Errno = Errno; } /** * NI_get_Errno() - get the global error number. */ int NI_get_Errno(void) { return netip_Errno; } /** * NI_set_Error() - set the global error string. * @Error: the new error string. * * If the error string is more than 512 characters in length * (including the ending null), only the first 511 characters will be * used (with a null as the 512th character). */ void NI_set_Error(const char *Error) { int len; len = strlen(Error); if (len > 511) { len = 511; } memcpy(netip_Error, Error, len); netip_Error[len] = '\0'; } /** * NI_get_Error() - get the global error string. */ const char* NI_get_Error(void) { return (const char *) netip_Error; } /** * NI_set_Error_Errno() - set the global error number and string. * @Errno: the new error number. * @Error: the new error string (can include printf modifiers). * @...: format arguments to substitute into @Error. */ void NI_set_Error_Errno(int Errno, const char *Error, ...) { va_list args; va_start(args, Error); vsnprintf(netip_Error, 512, Error, args); netip_Error[511] = '\0'; netip_Errno = Errno; va_end(args); } /** * NI_ip_uchars_to_n128(): make N128 integer from array of chars. * @uchars: array of at least 16 unsigned chars. * @buf: N128 integer buffer. */ void NI_ip_uchars_to_n128(unsigned char uchars[16], n128_t *num) { int i; int j; unsigned long k; for (i = 0; i < 4; i++) { j = i * 4; k = ( (uchars[j + 3]) | (uchars[j + 2] << 8) | (uchars[j + 1] << 16) | (uchars[j] << 24)); num->nums[i] = k; } } /** * NI_ip_uchars_to_ulong(): get whole integer from array of chars. * @uchars: array of at least 4 unsigned chars. */ unsigned long NI_ip_uchars_to_ulong(unsigned char uchars[4]) { return (uchars[3] | (uchars[2] << 8) | (uchars[1] << 16) | (uchars[0] << 24)); } /** * NI_hdtoi(): convert hexadecimal character to integer. * @c: hexadecimal character. * * Returns -1 when the character is not a valid hexadecimal character. */ int NI_hdtoi(char c) { c = tolower(c); return (isdigit(c)) ? c - '0' : ((c >= 'a') && (c <= 'f')) ? 10 + (c - 'a') : -1; } /** * NI_trailing_zeroes(): get trailing zeroes from number treated as binary. * @n: the number. */ int NI_trailing_zeroes(unsigned long n) { int c; if (!n) { return CHAR_BIT * sizeof(n); } n = (n ^ (n - 1)) >> 1; for (c = 0; n; c++) { n >>= 1; } return c; } /** * NI_bintoint(): convert bitstring to integer. * @bitstr: the bitstring. * @len: the number of characters to use from the bitstring. */ unsigned long NI_bintoint(const char *bitstr, int len) { unsigned long res; int i; int help; res = 0; help = len - 1; for (i = 0; i < len; i++) { res += (((unsigned long) (bitstr[i] == '1')) << (help - i)); } return res; } /** * NI_bintoint_nonzero(): convert bitstring to integer. * @bitstr: the bitstring. * @len: the number of characters to use from the bitstring. * * This function treats all non-zero characters in the bitstring as * if they were '1', whereas NI_bintoint() treats all non-one * characters as if they were '0'. */ unsigned long NI_bintoint_nonzero(const char *bitstr, int len) { unsigned long res; int i; int help; res = 0; help = len - 1; for (i = 0; i < len; i++) { res += (((unsigned long) (bitstr[i] != '0')) << (help - i)); } return res; } /** * NI_iplengths() - return length in bits of the version of IP address. * @version: IP version as integer (either 4 or 6). * * Returns 0 if @version is an invalid value. */ int NI_iplengths(int version) { switch (version) { case 4: return 32; case 6: return 128; default: return 0; }; } /** * NI_ip_n128tobin(): make bitstring from N128 integer. * @num: N128 integer. * @len: the number of bits to write to the buffer. * @buf: the bitstring buffer. * * This does not null-terminate the buffer. */ void NI_ip_n128tobin(n128_t *num, int len, char *buf) { int i; if (len == 0) { return; } for (i = 0; i < len; i++) { buf[len - 1 - i] = (n128_tstbit(num, i) ? '1' : '0'); } } /** * NI_ip_inttobin_str() - make bitstring from IP address integer. * @ip_int_str: the IP address integer as a string. * @version: the IP address version. * @buf: the bitstring buffer. * * Returns 1 if the buffer is able to be populated properly. Returns 0 * if the IP version is invalid. This function null-terminates the * buffer. The buffer must have at least 129 characters' capacity. */ int NI_ip_inttobin_str(const char *ip_int_str, int version, char *buf) { n128_t num; int i; int len; int res; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP " "version for %s", ip_int_str); return 0; } len = strlen(ip_int_str); for (i = 0; i < len; i++) { if (!isdigit(ip_int_str[i])) { memset(buf, '0', (version == 4) ? 32 : 128); buf[(version == 4) ? 32 : 128] = '\0'; return 1; } } n128_set_ui(&num, 0); res = n128_set_str_decimal(&num, ip_int_str, strlen(ip_int_str)); if (!res) { return 0; } n128_print_bin(&num, buf, (version == 4)); return 1; } /** * NI_ip_bintoint_str(): convert bitstring to integer string. * @bitstr: the bitstring. * @buf: the integer string buffer. */ int NI_ip_bintoint_str(const char *bitstr, char *buf) { unsigned long num_ulong; n128_t num_n128; int len; len = strlen(bitstr); if (len <= 32) { num_ulong = NI_bintoint_nonzero(bitstr, len); sprintf(buf, "%lu", num_ulong); return 1; } n128_set_ui(&num_n128, 0); n128_set_str_binary(&num_n128, bitstr, len); n128_print_dec(&num_n128, buf); return 1; } /** * NI_ip_is_ipv4(): check whether string is an IPv4 address. * @str: IP address string (null-terminated). */ int NI_ip_is_ipv4(const char *str) { int i; int len; int quads = 0; int quadspots[3]; long current_quad; int cq_index; char *endptr; len = strlen(str); if (!len) { NI_set_Error_Errno(107, "Invalid chars in IP "); return 0; } /* Contains invalid characters. */ for (i = 0; i < len; i++) { if (!isdigit(str[i]) && str[i] != '.') { NI_set_Error_Errno(107, "Invalid chars in IP %s", str); return 0; } } /* Starts or ends with '.'. */ if (str[0] == '.') { NI_set_Error_Errno(103, "Invalid IP %s - starts with a dot", str); return 0; } if (str[len - 1] == '.') { NI_set_Error_Errno(104, "Invalid IP %s - ends with a dot", str); return 0; } /* Contains more than four quads (octets). */ for (i = 0; i < len; i++) { if (str[i] == '.') { if (quads == 3) { NI_set_Error_Errno(105, "Invalid IP address %s", str); return 0; } quadspots[quads] = i + 1; quads++; } } /* Contains an empty quad. */ for (i = 0; i < (len - 1); i++) { if ((str[i] == '.') && (str[i + 1] == '.')) { NI_set_Error_Errno(106, "Empty quad in IP address %s", str); return 0; } } /* Contains an invalid quad value. */ for (cq_index = 0; cq_index <= quads; cq_index++) { i = (cq_index > 0) ? (quadspots[cq_index - 1]) : 0; endptr = NULL; current_quad = strtol(str + i, &endptr, 10); if (STRTOL_FAILED(current_quad, str + i, endptr) || (!(current_quad >= 0 && current_quad < 256))) { NI_set_Error_Errno(107, "Invalid quad in IP address " "%s - %d", str, current_quad); return 0; } } return 1; } /** * NI_ip_is_ipv6(): check whether string is an IPv6 address. * @str: the IP address string. */ int NI_ip_is_ipv6(const char *str) { int i; int len; int octs = 0; int octspots[8]; int oct_index; const char *double_colon; const char *next_oct; const char *cc; int count; int is_hd; int max_colons; len = strlen(str); double_colon = strstr(str, "::"); max_colons = (double_colon == NULL) ? 7 : 8; /* Store a pointer to the next character after each ':' in * octspots. */ for (i = 0; i < len; i++) { if (str[i] == ':') { if (octs == max_colons) { return 0; } octspots[octs++] = i + 1; } } if (!octs) { return 0; } for (oct_index = 0; oct_index <= octs; oct_index++) { i = (oct_index > 0) ? (octspots[oct_index - 1]) : 0; /* Empty octet. */ if (str[i] == ':') { continue; } if (strlen(str + i) == 0) { continue; } /* Last octet can be an IPv4 address. */ cc = str + i; if ((oct_index == octs) && NI_ip_is_ipv4(cc)) { continue; } /* 1-4 hex digits. */ next_oct = strchr(str + i, ':'); if (next_oct == NULL) { next_oct = (str + len); } count = next_oct - cc; is_hd = 1; while (cc != next_oct) { if (!isxdigit(*cc)) { is_hd = 0; break; } cc++; } if (is_hd && (count <= 4)) { continue; } NI_set_Error_Errno(108, "Invalid IP address %s", str); return 0; } /* Starts or ends with ':'. */ if ((str[0] == ':') && (str[1] != ':')) { NI_set_Error_Errno(109, "Invalid address %s " "(starts with :)", str); return 0; } if ((str[len - 1] == ':') && (str[len - 2] != ':')) { NI_set_Error_Errno(110, "Invalid address %s " "(ends with :)", str); return 0; } /* Contains more than one '::'. */ if ((double_colon != NULL) && (strstr(double_colon + 1, "::"))) { NI_set_Error_Errno(111, "Invalid address %s " "(More than one :: pattern)", str); return 0; } /* Doesn't contain '::', though it has fewer than eight segments. */ if ((octs != 7) && (double_colon == NULL)) { NI_set_Error_Errno(112, "Invalid number of octets %s", str); return 0; } return 1; } /** * NI_ip_get_version(): return the version of the IP address string. * @str: the IP address string. * * Returns 0 if the string is neither an IPv4 nor an IPv6 address * string. */ int NI_ip_get_version(const char *str) { if ((!strchr(str, ':')) && NI_ip_is_ipv4(str)) { return 4; } else if (NI_ip_is_ipv6(str)) { return 6; } else { return 0; } } /** * NI_ip_get_mask(): make bitstring network mask. * @len: the mask's prefix length. * @version: the mask's IP address version. * @buf: the bitstring mask buffer. * * If @len is larger than the number of bits for an IP address of the * specified version, then @len is set to equal that number of bits. * So if 48 were specified for @len for an IPv4 address, it would be * set to 32. This function does not null-terminate the buffer. The * buffer must have 32 or 128 characters' capacity for IPv4 and IPv6 * respectively. */ int NI_ip_get_mask(int len, int version, char *buf) { int size; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version"); return 0; } size = NI_iplengths(version); if (len < 0) { len = 0; } else if (len > size) { len = size; } memset(buf, '1', len); memset(buf + len, '0', (size - len)); return 1; } /** * NI_ip_last_address_ipv6(): get last address of prefix. * @ip: the beginning IP address. * @len: the prefix length. * @buf: N128 integer buffer for the last address. */ int NI_ip_last_address_ipv6(n128_t *ip, int len, n128_t *buf) { int i; memcpy(buf, ip, sizeof(*ip)); len = (len == 0) ? 128 : (128 - len); for (i = 0; i < len; i++) { n128_setbit(buf, i); } return 1; } /** * NI_ip_last_address_ipv4(): get last address of prefix. * @ip: the beginning IP address. * @len: the prefix length. */ unsigned long NI_ip_last_address_ipv4(unsigned long ip, int len) { unsigned long mask; mask = (len == 0) ? 0xFFFFFFFF : ((1 << (32 - len)) - 1); return ip | mask; } /** * NI_ip_last_address_bin(): make last address of prefix as a bitstring. * @bitstr: the beginning IP address as a bitstring. * @len: the prefix length. * @version: the IP address version. * @buf: the last address bitstring buffer. * * This function does not null-terminate the buffer. The buffer must * have 32 or 128 characters' capacity for IPv4 and IPv6 respectively. */ int NI_ip_last_address_bin(const char *bitstr, int len, int version, char *buf) { int size; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version"); return 0; } size = NI_iplengths(version); if ((len < 0) || (len > size)) { len = size; } strncpy(buf, bitstr, len); memset(buf + len, '1', (size - len)); return 1; } /** * NI_ip_bincomp(): compare two bitstrings. * @bitstr_1: first bitstring. * @op_str: the comparator as a string. * @bitstr_2: second bitstring. * @result: a pointer to an integer. * * The bitstrings and the comparator must be null-terminated. The * comparator must be one of 'gt', 'ge', 'lt', and 'le'. 'gt' means * 'greater than', 'ge' means 'greater than or equal to', 'lt' means * 'less than', 'le' means 'less than or equal to'. Returns 1 or 0 * depending on whether the strings were able to be compared * successfully. If the comparison was able to be made, the result of * the comparison is stored in @result. This function will not compare * two bitstrings of different lengths. */ int NI_ip_bincomp(const char *bitstr_1, const char *op_str, const char *bitstr_2, int *result) { const char *b; const char *e; int op; int res; op = (!strcmp(op_str, "gt")) ? GT : (!strcmp(op_str, "lt")) ? LT : (!strcmp(op_str, "le")) ? LE : (!strcmp(op_str, "ge")) ? GE : 0; if (!op) { NI_set_Error_Errno(131, "Invalid Operator %s", op_str); return 0; } if ((op == GT) || (op == GE)) { b = bitstr_1; e = bitstr_2; } else { b = bitstr_2; e = bitstr_1; } if (strlen(b) != (strlen(e))) { NI_set_Error_Errno(130, "IP addresses of different length"); return 0; } res = strcmp(b, e); *result = (!res && ((op == GE) || (op == LE))) ? 1 : (res > 0); return 1; } /** * NI_ip_is_overlap_ipv6(): get overlap status of two ranges. * @begin_1: beginning address of first range. * @end_1: ending address of first range. * @begin_2: beginning address of second range. * @end_2: ending address of second range. * @result: a pointer to an integer. */ void NI_ip_is_overlap_ipv6(n128_t *begin_1, n128_t *end_1, n128_t *begin_2, n128_t *end_2, int *result) { int res; if (!n128_cmp(begin_1, begin_2)) { if (!n128_cmp(end_1, end_2)) { *result = IP_IDENTICAL; return; } res = n128_cmp(end_1, end_2); *result = (res < 0) ? IP_A_IN_B_OVERLAP : IP_B_IN_A_OVERLAP; return; } if (!n128_cmp(end_1, end_2)) { res = n128_cmp(begin_1, begin_2); *result = (res < 0) ? IP_B_IN_A_OVERLAP : IP_A_IN_B_OVERLAP; return; } res = n128_cmp(begin_1, begin_2); if (res < 0) { res = n128_cmp(end_1, begin_2); if (res < 0) { *result = IP_NO_OVERLAP; return; } res = n128_cmp(end_1, end_2); *result = (res < 0) ? IP_PARTIAL_OVERLAP : IP_B_IN_A_OVERLAP; return; } res = n128_cmp(end_2, begin_1); if (res < 0) { *result = IP_NO_OVERLAP; return; } res = n128_cmp(end_2, end_1); *result = (res < 0) ? IP_PARTIAL_OVERLAP : IP_A_IN_B_OVERLAP; return; } /** * NI_ip_is_overlap_ipv4(): get overlap status of two ranges. * @begin_1: beginning address of first range. * @end_1: ending address of first range. * @begin_2: beginning address of second range. * @end_2: ending address of second range. * @result: a pointer to an integer. */ void NI_ip_is_overlap_ipv4(unsigned long begin_1, unsigned long end_1, unsigned long begin_2, unsigned long end_2, int *result) { if (begin_1 == begin_2) { if (end_1 == end_2) { *result = IP_IDENTICAL; return; } *result = (end_1 < end_2) ? IP_A_IN_B_OVERLAP : IP_B_IN_A_OVERLAP; return; } if (end_1 == end_2) { *result = (begin_1 < begin_2) ? IP_B_IN_A_OVERLAP : IP_A_IN_B_OVERLAP; return; } if (begin_1 < begin_2) { if (end_1 < begin_2) { *result = IP_NO_OVERLAP; return; } *result = (end_1 < end_2) ? IP_PARTIAL_OVERLAP : IP_B_IN_A_OVERLAP; return; } if (end_2 < begin_1) { *result = IP_NO_OVERLAP; return; } *result = (end_2 < end_1) ? IP_PARTIAL_OVERLAP : IP_A_IN_B_OVERLAP; return; } /** * NI_ip_is_overlap(): get overlap status of two ranges. * @begin_1: beginning bitstring IP address for first range. * @end_1: ending bitstring IP address for first range. * @begin_2: beginning bitstring IP address for second range. * @end_2: ending bitstring IP address for second range. * @result: a pointer to an integer. * * Each bitstring must be null-terminated. Returns 1 or 0 depending * on whether the ranges are able to be compared successfully. If the * overlap status is able to be determined, stores that status in * @result. Returns 0 if the bitstrings are not all of equal length. * The possible overlap statuses are NO_OVERLAP, IP_PARTIAL_OVERLAP, * IP_A_IN_B_OVERLAP (first range completely contained within second * range), IP_B_IN_A_OVERLAP (second range completely contained within * first range) and IP_IDENTICAL. */ int NI_ip_is_overlap(const char *begin_1, const char *end_1, const char *begin_2, const char *end_2, int *result) { int b1_len; int b2_len; int res = 0; n128_t begin_1_n128; n128_t end_1_n128; n128_t begin_2_n128; n128_t end_2_n128; unsigned long begin_1_ulong; unsigned long begin_2_ulong; unsigned long end_1_ulong; unsigned long end_2_ulong; b1_len = strlen(begin_1); b2_len = strlen(begin_2); if (!( (b1_len == (int) strlen(end_1)) && (b2_len == (int) strlen(end_2)) && (b1_len == b2_len))) { NI_set_Error_Errno(130, "IP addresses of different length"); return 0; } NI_ip_bincomp(begin_1, "le", end_1, &res); if (!res) { NI_set_Error_Errno(140, "Invalid range %s - %s", begin_1, end_1); return 0; } NI_ip_bincomp(begin_2, "le", end_2, &res); if (!res) { NI_set_Error_Errno(140, "Invalid range %s - %s", begin_2, end_2); return 0; } /* IPv4-specific version (avoids using N128). */ if (b1_len <= 32) { begin_1_ulong = NI_bintoint(begin_1, b1_len); begin_2_ulong = NI_bintoint(begin_2, b1_len); end_1_ulong = NI_bintoint(end_1, b1_len); end_2_ulong = NI_bintoint(end_2, b1_len); NI_ip_is_overlap_ipv4(begin_1_ulong, end_1_ulong, begin_2_ulong, end_2_ulong, result); return 1; } /* IPv6 version (using N128). */ n128_set_str_binary(&begin_1_n128, begin_1, b1_len); n128_set_str_binary(&begin_2_n128, begin_2, b1_len); n128_set_str_binary(&end_1_n128, end_1, b1_len); n128_set_str_binary(&end_2_n128, end_2, b1_len); NI_ip_is_overlap_ipv6(&begin_1_n128, &end_1_n128, &begin_2_n128, &end_2_n128, result); return 1; } /** * NI_ip_check_prefix_ipv6(): check whether prefix length is valid. * @ip: IP address. * @len: the prefix length. */ int NI_ip_check_prefix_ipv6(n128_t *ip, int len) { n128_t mask; char buf[IPV6_BITSTR_LEN]; int i; if ((len < 0) || (len > 128)) { NI_set_Error_Errno(172, "Invalid prefix length /%d", len); return 0; } n128_set_ui(&mask, 0); for (i = 0; i < (128 - len); i++) { n128_setbit(&mask, i); } n128_and(&mask, ip); if (n128_cmp_ui(&mask, 0)) { NI_ip_n128tobin(ip, len, buf); buf[len] = '\0'; NI_set_Error_Errno(171, "Invalid prefix %s/%d", buf, len); return 0; } return 1; } /** * NI_ip_check_prefix_ipv4(): check whether prefix length is valid. * @ip: IP address. * @len: the prefix length. */ int NI_ip_check_prefix_ipv4(unsigned long ip, int len) { unsigned long mask; if ((len < 0) || (len > 32)) { NI_set_Error_Errno(172, "Invalid prefix length /%d", len); return 0; } mask = (len == 0) ? 0xFFFFFFFF : ((1 << (32 - len)) - 1); if ((ip & mask) != 0) { NI_set_Error_Errno(171, "Invalid prefix %u/%d", ip, len); return 0; } return 1; } /** * NI_ip_check_prefix(): check whether prefix length is valid for address. * @bitstr: the bitstring IP address. * @len: the prefix length. * @version: the IP address version. * * The bitstring must be null-terminated. */ int NI_ip_check_prefix(const char *bitstr, int len, int version) { int iplen; const char *c; if (len < 0) { NI_set_Error_Errno(172, "Invalid prefix length /%d", len); return 0; } iplen = strlen(bitstr); if (len > iplen) { NI_set_Error_Errno(170, "Prefix length %d is longer than " "IP address (%d)", len, iplen); return 0; } c = bitstr + len; while (*c != '\0') { if (*c != '0') { NI_set_Error_Errno(171, "Invalid prefix %s/%d", bitstr, len); return 0; } c++; } if (iplen != NI_iplengths(version)) { NI_set_Error_Errno(172, "Invalid prefix length /%d", len); return 0; } return 1; } /** * NI_ip_get_prefix_length_ipv4(): get prefix length for a given range. * @begin: first IP address. * @end: second IP address. * @bits: number of bits to check. * @len: a pointer to an integer. */ void NI_ip_get_prefix_length_ipv4(unsigned long begin, unsigned long end, int bits, int *len) { int i; int res = 0; for (i = 0; i < bits; i++) { if ((begin & 1) == (end & 1)) { res = bits - i; break; } begin >>= 1; end >>= 1; } *len = res; } /** * NI_ip_get_prefix_length_ipv6(): get prefix length for a given range. * @num1: first IP address as an N128 integer. * @num2: second IP address as an N128 integer. * @bits: number of bits to check * @len: a pointer to an integer. * * Returns 1 or 0 depending on whether the prefix length could be * calculated. Stores the prefix length in @len if it is able to be * calculated. */ void NI_ip_get_prefix_length_ipv6(n128_t *num1, n128_t *num2, int bits, int *len) { int i; int res = 0; for (i = 0; i < bits; i++) { if (n128_tstbit(num1, i) == n128_tstbit(num2, i)) { res = bits - i; break; } } *len = res; } /** * NI_ip_get_prefix_length(): get prefix length for a given range. * @bitstr_1: first IP address as a bitstring. * @bitstr_2: second IP address as a bitstring. * @len: a pointer to an integer. * * Returns 1 or 0 depending on whether the prefix length could be * calculated. Stores the prefix length in @len if it is able to be * calculated. */ int NI_ip_get_prefix_length(const char *bitstr_1, const char *bitstr_2, int *len) { int bin1_len; int bin2_len; int i; int res; bin1_len = strlen(bitstr_1); bin2_len = strlen(bitstr_2); if (bin1_len != bin2_len) { NI_set_Error_Errno(130, "IP addresses of different length"); return 0; } res = bin1_len; for (i = (bin1_len - 1); i >= 0; i--) { if (bitstr_1[i] == bitstr_2[i]) { res = (bin1_len - 1 - i); break; } } *len = res; return 1; } /** * NI_ip_inttoip_ipv4(): make IPv4 address from integer. * @n: the IP address as a number. * @buf: the IP address buffer. */ void NI_ip_inttoip_ipv4(unsigned long n, char *buf) { sprintf(buf, "%lu.%lu.%lu.%lu", (n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, (n >> 0) & 0xFF); } /** * NI_ip_inttoip_ipv6(): make IPv6 address from integers. * @n1: the most significant 32 bits of the address. * @n2: the next-most significant 32 bits of the address. * @n3: the next-most significant 32 bits of the address. * @n4: the least significant 32 bits of the address. * @buf: the IP address buffer. */ void NI_ip_inttoip_ipv6(unsigned long n1, unsigned long n2, unsigned long n3, unsigned long n4, char *buf) { sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", (unsigned int) (n1 >> 16) & 0xFFFF, (unsigned int) (n1 ) & 0xFFFF, (unsigned int) (n2 >> 16) & 0xFFFF, (unsigned int) (n2 ) & 0xFFFF, (unsigned int) (n3 >> 16) & 0xFFFF, (unsigned int) (n3 ) & 0xFFFF, (unsigned int) (n4 >> 16) & 0xFFFF, (unsigned int) (n4 ) & 0xFFFF); } /** * NI_ip_inttoip_n128(): make IPv6 address from N128 integer. * @ip: IP address. * @buf: the IP address buffer. */ void NI_ip_inttoip_n128(n128_t *ip, char *buf) { NI_ip_inttoip_ipv6(ip->nums[0], ip->nums[1], ip->nums[2], ip->nums[3], buf); } /** * NI_ip_bintoip(): make IP address from bitstring. * @bitstr: the IP address as a bitstring. * @version: IP address version as integer. * @buf: the IP address buffer. * * The bitstring must be null-terminated. This function null-terminates the * buffer, so it has to have between eight and sixteen characters' capacity * (inclusive) for IPv4 addresses, depending on the value of the address, and * 40 characters' capacity for IPv6 addresses. Bitstrings that have fewer * characters than there are bits in the relevant version of IP address will be * treated as if they were left-padded with '0' characters until that number of * bits is met: e.g., passing "1" and 4 as the first two arguments to this * function will yield "0.0.0.1" in @buf. */ int NI_ip_bintoip(const char *bitstr, int version, char *buf) { int size; int iplen; int longs; int i; int j; int excess; int bits; unsigned long nums[4]; iplen = NI_iplengths(version); size = strlen(bitstr); if (size > iplen) { NI_set_Error_Errno(189, "Invalid IP length for " "binary IP %s", bitstr); return 0; } if (version == 4) { nums[0] = NI_bintoint(bitstr, size); NI_ip_inttoip_ipv4(nums[0], buf); return 1; } for (i = 0; i < 4; i++) { nums[i] = 0; } excess = size % 32; longs = (size / 32) + (!excess ? 0 : 1); for (i = (4 - longs), j = 0; i < 4; i++, j++) { bits = (i == (4 - longs) && excess) ? excess : 32; nums[i] = NI_bintoint(bitstr + (j * 32), bits); } NI_ip_inttoip_ipv6(nums[0], nums[1], nums[2], nums[3], buf); return 1; } /** * NI_ip_binadd(): add two bitstring IP addresses. * @ip1: first bitstring IP address. * @ip2: second bitstring IP address. * @buf: result buffer. * @maxlen: maximum capacity of buffer. * * Both bitstrings must be null-terminated and of the same length as * each other. The result is stored as a bitstring and * null-terminated. The result will be of the length of the * bitstrings, regardless of the result of the addition. */ int NI_ip_binadd(const char *ip1, const char *ip2, char *buf, int maxlen) { n128_t num1; n128_t num2; int len1; int len2; len1 = strlen(ip1); len2 = strlen(ip2); if (len1 != len2) { NI_set_Error_Errno(130, "IP addresses of different length"); return 0; } if (len1 > (maxlen - 1)) { return 0; } n128_set_str_binary(&num1, ip1, len1); n128_set_str_binary(&num2, ip2, len2); n128_add(&num1, &num2); NI_ip_n128tobin(&num1, len1, buf); buf[len2] = '\0'; return 1; } /** * NI_ip_range_to_prefix_ipv4(): get prefixes contained within range. * @begin: beginning address. * @end: ending address. * @version: IP address version. * @prefixes: prefix strings buffer. * @pcount: prefix count buffer. * * Will write at most 32 prefix strings to @prefixes. */ int NI_ip_range_to_prefix_ipv4(unsigned long begin, unsigned long end, int version, char **prefixes, int *pcount) { unsigned long current; unsigned long mask; unsigned long zeroes; int iplen; int res; int i; int prefix_length; char *new_prefix; char range[4]; current = 0; mask = 0; iplen = NI_iplengths(version); *pcount = 0; while (begin <= end) { /* Calculate the number of zeroes that exist on the right of * 'begin', and create a mask for that number of bits. */ zeroes = NI_trailing_zeroes(begin); mask = 0; for (i = 0; i < (int) zeroes; i++) { mask |= (1 << i); } /* Find the largest range (from 'begin' to 'current') that * does not exceed 'end'. */ do { current = begin; current |= mask; mask >>= 1; } while (current > end); /* Get the prefix length for the range and add the stringified * range to @prefixes. */ NI_ip_get_prefix_length_ipv4(begin, current, iplen, &prefix_length); new_prefix = (char *) malloc(MAX_IPV4_RANGE_STR_LEN); if (!new_prefix) { printf("NI_ip_range_to_prefix: malloc failed!\n"); return 0; } prefixes[(*pcount)++] = new_prefix; NI_ip_inttoip_ipv4(begin, new_prefix); strcat(new_prefix, "/"); res = snprintf(range, 4, "%d", prefix_length); strncat(new_prefix, range, res); begin = current + 1; /* Do not continue getting prefixes if 'current' completely * comprises set bits. */ if (current == 0xFFFFFFFF) { break; } } return 1; } /** * NI_ip_range_to_prefix_ipv6(): get prefixes contained within range. * @begin: beginning address. * @end: ending address. * @version: IP address version. * @prefixes: prefix strings buffer. * @pcount: prefix count buffer. * * Will write at most 128 prefix strings to @prefixes. */ int NI_ip_range_to_prefix_ipv6(n128_t *begin, n128_t *end, int version, char **prefixes, int *pcount) { n128_t current; n128_t mask; unsigned long zeroes; int iplen; unsigned long res; int i; int prefix_length; char *new_prefix; char tempip[IPV6_BITSTR_LEN]; char range[4]; iplen = NI_iplengths(version); tempip[iplen] = '\0'; *pcount = 0; while (n128_cmp(begin, end) <= 0) { if (*pcount == 128) { return 0; } /* Calculate the number of zeroes that exist on the right of * 'begin', and create a mask for that number of bits. */ zeroes = n128_scan1(begin); zeroes = ((zeroes == INT_MAX) ? (unsigned) iplen : zeroes) - 1; n128_set_ui(&mask, 0); for (i = 0; i < ((int) zeroes + 1); i++) { n128_setbit(&mask, i); } /* Find the largest range (from 'begin' to 'current') that * does not exceed 'end'. */ do { n128_set(¤t, begin); n128_ior(¤t, &mask); n128_clrbit(&mask, zeroes); zeroes--; } while (n128_cmp(¤t, end) > 0); /* Get the prefix length for the range and add the stringified * range to @prefixes. */ NI_ip_get_prefix_length_ipv6(begin, ¤t, iplen, &prefix_length); new_prefix = (char *) malloc(MAX_IPV6_RANGE_STR_LEN); if (!new_prefix) { printf("NI_ip_range_to_prefix: malloc failed!\n"); return 0; } prefixes[(*pcount)++] = new_prefix; NI_ip_n128tobin(begin, iplen, tempip); NI_ip_bintoip(tempip, version, new_prefix); strcat(new_prefix, "/"); res = snprintf(range, 4, "%d", prefix_length); strncat(new_prefix, range, res); n128_set(begin, ¤t); n128_add_ui(begin, 1); /* Do not continue getting prefixes if 'current' completely * comprises set bits. */ res = n128_scan0(¤t); if (res == INT_MAX) { break; } } return 1; } /** * NI_ip_range_to_prefix(): get prefixes contained within range. * @begin: first IP address as a bitstring. * @end: second IP address as a bitstring. * @version: IP address version. * @prefixes: prefix strings buffer. * @pcount: prefix count buffer. * * Both bitstrings must be null-terminated. If unsure of the number of * prefixes that will be created, @prefixes must contain space for 128 * character pointers. Returns 1/0 depending on whether it completed * successfully. */ int NI_ip_range_to_prefix(const char *begin, const char *end, int version, char **prefixes, int *pcount) { n128_t begin_n128; n128_t end_n128; unsigned long begin_ulong; unsigned long end_ulong; int iplen; int res; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version"); return 0; } if (strlen(begin) != strlen(end)) { NI_set_Error_Errno(130, "IP addresses of different length"); return 0; } iplen = NI_iplengths(version); if (!iplen) { return 0; } if (version == 4) { begin_ulong = NI_bintoint(begin, 32); end_ulong = NI_bintoint(end, 32); return NI_ip_range_to_prefix_ipv4(begin_ulong, end_ulong, version, prefixes, pcount); } n128_set_str_binary(&begin_n128, begin, strlen(begin)); n128_set_str_binary(&end_n128, end, strlen(end)); res = NI_ip_range_to_prefix_ipv6(&begin_n128, &end_n128, version, prefixes, pcount); return res; } /** * NI_ip_aggregate_tail(): post-processing after version-specific aggregation. * @res: the result of the relevant ip_range_to_prefix function. * @prefixes: prefix strings buffer. * @pcount: prefix count. * @version: IP address version. * @buf: the buffer for the new range. * * If @res is false, then frees the prefixes and returns zero. If no * prefixes were returned, returns zero. If more than one prefix was * returned, frees the prefixes and returns 161. Otherwise, populates * the buffer (null-terminated) with the first range from @prefixes * and returns 1. */ int NI_ip_aggregate_tail(int res, char **prefixes, int pcount, int version, char *buf) { int i; int len; int max; if (!res) { for (i = 0; i < pcount; i++) { free(prefixes[i]); } return 0; } if (pcount == 0) { return 0; } if (pcount > 1) { for (i = 0; i < pcount; i++) { free(prefixes[i]); } return 161; } len = strlen(*prefixes); max = (version == 4) ? MAX_IPV4_RANGE_STR_LEN - 1 : MAX_IPV6_RANGE_STR_LEN - 1; if (len > max) { len = max; } strncpy(buf, *prefixes, len); buf[len] = 0; return 1; } /** * NI_ip_aggregate_ipv6(): get the aggregate range of two ranges as a string. * @begin_1: beginning N128 integer IP address for first range. * @end_1: ending N128 integer IP address for first range. * @begin_2: beginning N128 integer IP address for second range. * @end_2: ending N128 integer IP address for second range. * @version: IP address version. * @buf: the buffer for the new range. * * See NI_ip_aggregate(). */ int NI_ip_aggregate_ipv6(n128_t *b1, n128_t *e1, n128_t *b2, n128_t *e2, int version, char *buf) { char *prefixes[128]; int pcount; int res; n128_add_ui(e1, 1); if (n128_cmp(e1, b2)) { return 160; } pcount = 0; res = NI_ip_range_to_prefix_ipv6(b1, e2, version, prefixes, &pcount); return NI_ip_aggregate_tail(res, prefixes, pcount, version, buf); } /** * NI_ip_aggregate_ipv4(): get the aggregate range of two ranges as a string. * @begin_1: beginning integer IP address for first range. * @end_1: ending integer IP address for first range. * @begin_2: beginning integer IP address for second range. * @end_2: ending integer IP address for second range. * @version: IP address version. * @buf: the buffer for the new range. * * See NI_ip_aggregate(). */ int NI_ip_aggregate_ipv4(unsigned long b1, unsigned long e1, unsigned long b2, unsigned long e2, int version, char *buf) { char *prefixes[128]; int pcount; int res; if (e1 + 1 != b2) { return 160; } pcount = 0; res = NI_ip_range_to_prefix_ipv4(b1, e2, version, prefixes, &pcount); return NI_ip_aggregate_tail(res, prefixes, pcount, version, buf); } /** * NI_ip_aggregate(): get the aggregate range of two ranges as a string. * @begin_1: beginning bitstring IP address for first range. * @end_1: ending bitstring IP address for first range. * @begin_2: beginning bitstring IP address for second range. * @end_2: ending bitstring IP address for second range. * @version: IP address version. * @buf: the buffer for the new range. * * Returns zero and sets error messages if the ranges are not * contiguous or the aggregate of the ranges cannot be represented as * a single prefix. Otherwise, populates the buffer with the aggregate * of the two ranges as a prefix range string (e.g. '1.0.0.0/8'). */ int NI_ip_aggregate(const char *b1, const char *e1, const char *b2, const char *e2, int version, char *buf) { int res; int i; n128_t b1_n128; n128_t e1_n128; n128_t b2_n128; n128_t e2_n128; unsigned long b1_ulong; unsigned long e1_ulong; unsigned long b2_ulong; unsigned long e2_ulong; const char *addr_args[4]; addr_args[0] = b1; addr_args[1] = b2; addr_args[2] = e1; addr_args[3] = e2; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version for %s", b1); return 0; } else if (version == 4) { for (i = 0; i < 4; i++) { if (strlen(addr_args[i]) != 32) { NI_set_Error_Errno(107, "Invalid IP address %s", addr_args[i]); return 0; } } b1_ulong = NI_bintoint(b1, 32); e1_ulong = NI_bintoint(e1, 32); b2_ulong = NI_bintoint(b2, 32); e2_ulong = NI_bintoint(e2, 32); res = NI_ip_aggregate_ipv4(b1_ulong, e1_ulong, b2_ulong, e2_ulong, version, buf); } else { for (i = 0; i < 4; i++) { if (strlen(addr_args[i]) != 128) { NI_set_Error_Errno(108, "Invalid IP address %s", addr_args[i]); return 0; } } n128_set_str_binary(&b1_n128, b1, strlen(b1)); n128_set_str_binary(&e1_n128, e1, strlen(e1)); n128_set_str_binary(&b2_n128, b2, strlen(b2)); n128_set_str_binary(&e2_n128, e2, strlen(e2)); res = NI_ip_aggregate_ipv6(&b1_n128, &e1_n128, &b2_n128, &e2_n128, version, buf); } if (res == 0) { return 0; } if (res == 160) { NI_set_Error_Errno(160, "Ranges not contiguous - %s - %s", e1, b2); return 0; } if (res == 161) { NI_set_Error_Errno(161, "%s - %s is not a single prefix", b1, e2); return 0; } return 1; } /** * NI_ip_iptobin(): get bitstring from IP address. * @ip: single IP address as a string. * @version: IP address version. * @buf: bitstring buffer. * * Returns zero (and may also set Error/Errno) if the IP address is * invalid. If an IPv6 address is provided, it must be fully expanded * - IPv6 addresses like '0::0' or '0:0:0:0:0:0:0:0' will not be * handled correctly. */ int NI_ip_iptobin(const char *ip, int ipversion, char *buf) { int res; int j; int k; int y; int i; char c; int ncount; unsigned char ipv4[4]; if (ipversion == 4) { res = inet_pton4(ip, ipv4); if (res == 0) { return 0; } for (j = 0; j < 4; j++) { for (i = 0; i < 8; i++) { buf[(j * 8) + i] = ((ipv4[j] & (1 << (8 - i - 1)))) ? '1' : '0'; } } return 1; } else { j = 0; ncount = 0; while ((c = ip[j])) { if (c != ':') { ncount++; } j++; } if (ncount != 32) { NI_set_Error_Errno(102, "Bad IP address %s", ip); return 0; } i = -1; for (j = 0; ip[j] != '\0'; j++) { if (ip[j] == ':') { continue; } else { i++; } y = NI_hdtoi(ip[j]); if (y == -1) { return 0; } for (k = 0; k < 4; k++) { buf[ (i * 4) + k ] = ((y >> (3 - k)) & 1) ? '1' : '0'; } } return 1; } } /** * NI_ip_expand_address_ipv4(): expand an IPv4 address. * @ip: the IPv4 address as a string. * @buf: the IP address buffer. * * The IPv4 address string must be null-terminated. The buffer will be * null-terminated on success. */ int NI_ip_expand_address_ipv4(const char *ip, char *buf) { int res; unsigned char ipv4[4]; res = inet_pton4(ip, ipv4); if (!res) { return 0; } NI_ip_inttoip_ipv4(NI_ip_uchars_to_ulong(ipv4), buf); return 1; } /** * NI_ip_expand_address_ipv6(): expand an IPv6 address. * @ip: the IPv6 address as a string. * @buf: the IP address buffer. * * The IPv6 address string must be null-terminated. The buffer will be * null-terminated on success. */ int NI_ip_expand_address_ipv6(const char *ip, char *retbuf) { int res; int i; unsigned char ipv6[16]; unsigned long n[4]; res = inet_pton6(ip, ipv6); if (!res) { return 0; } for (i = 0; i < 4; i++) { n[i] = (ipv6[(i * 4) + 0] << 24) | (ipv6[(i * 4) + 1] << 16) | (ipv6[(i * 4) + 2] << 8) | (ipv6[(i * 4) + 3]); } NI_ip_inttoip_ipv6(n[0], n[1], n[2], n[3], retbuf); return 1; } /** * NI_ip_expand_address(): expand an IP address. * @ip: the IP address as a string. * @version: the IP address version. * @buf: the IP address buffer. * * See NI_ip_expand_address_ipv4() and NI_ip_expand_address_ipv6(). This * function dispatches to one of those functions depending on the * value of the @version argument. */ int NI_ip_expand_address(const char *ip, int version, char *buf) { return (version == 4) ? NI_ip_expand_address_ipv4(ip, buf) : NI_ip_expand_address_ipv6(ip, buf); } /** * NI_ip_reverse_ipv4(): get reverse domain for an IPv4 address. * @ip: the IP address as a string. * @len: the prefix length of the reverse domain. * @buf: the reverse domain buffer. * * If the length is not evenly divisible by eight, then it will be * treated as though it were the next number lower than it that is * evenly divisible by eight when determining how many octets to * print. So e.g. if the length is 31, three octets from the address * will be included in the domain. The buffer is null-terminated. The * longest possible IPv4 reverse domain name contains 25 characters * (including the null terminator). */ int NI_ip_reverse_ipv4(const char *ip, int len, char *buf) { int res; int quads; int i; char numbuf[5]; unsigned char ipv4[4]; if ((len < 0) || (len > 32)) { return 0; } quads = len / 8; res = inet_pton4(ip, ipv4); if (!res) { return 0; } for (i = (quads - 1); i >= 0; i--) { sprintf(numbuf, "%u.", ipv4[i]); strcat(buf, numbuf); } strcat(buf, "in-addr.arpa."); return 1; } /** * NI_ip_reverse_ipv6(): get reverse domain for an IPv4 address. * @ip: the IP address as a string. * @len: the prefix length of the reverse domain. * @buf: the reverse domain buffer. * * If the length is not evenly divisible by four, then it will be * treated as though it were the next number lower than it that is * evenly divisible by four when determining how many nibbles to * print. So e.g. if the length is 10, two nibbles from the address * will be included in the domain. The buffer is null-terminated. The * longest possible IPv6 reverse domain name contains 74 characters * (including the null terminator). */ int NI_ip_reverse_ipv6(const char *ip, int len, char *buf) { int res; int i; int index; int shift; unsigned char ipv6[16]; if ((len < 0) || (len > 128)) { return 0; } len = (len / 4); res = inet_pton6(ip, ipv6); if (!res) { return 0; } for (i = (len - 1); i >= 0; i--) { index = i / 2; shift = !(i % 2) * 4; sprintf(buf, "%x.", ((ipv6[index] >> shift) & 0xF)); buf += 2; } strcat(buf, "ip6.arpa."); return 1; } /** * NI_ip_reverse(): get reverse domain for an IP address. * @ip: the IP address as a string. * @len: the prefix length of the reverse domain. * @buf: the reverse domain buffer. * * See NI_ip_reverse_ipv4() and NI_ip_reverse_ipv6(). */ int NI_ip_reverse(const char *ip, int len, int ipversion, char *buf) { if (!ipversion) { ipversion = NI_ip_get_version(ip); } if (!ipversion) { NI_set_Error_Errno(101, "Cannot determine IP " "version for %s", ip); return 0; } if (ipversion == 4) { return NI_ip_reverse_ipv4(ip, len, buf); } else if (ipversion == 6) { return NI_ip_reverse_ipv6(ip, len, buf); } return 0; } /** * NI_ip_normalize_prefix_ipv4(): get first and last address from prefix range. * @ip: IP address. * @slash: pointer to first '/' in original string. * @ip1buf: first IP address buffer. * @ip2buf: second IP address buffer. * * Both buffers are null-terminated on success. */ int NI_ip_normalize_prefix_ipv4(unsigned long ip, char *slash, char *ip1buf, char *ip2buf) { unsigned long current; char *endptr = NULL; int res; long clen = 0; int addcst = 0; char c; current = ip; for (;;) { c = *slash++; if (c != '/') { break; } endptr = NULL; clen = strtol(slash, &endptr, 10); if (STRTOL_FAILED(clen, slash, endptr)) { return 0; } if (*endptr == ',') { addcst = 1; } else if (endptr != (slash + strlen(slash))) { NI_set_Error_Errno(172, "Invalid prefix length /%s", slash); return 0; } else { addcst = 0; } res = NI_ip_check_prefix_ipv4(current, clen); if (!res) { return 0; } current = NI_ip_last_address_ipv4(current, clen); if (addcst) { current += 1; slash = endptr + 1; } } NI_ip_inttoip_ipv4(ip, ip1buf); NI_ip_inttoip_ipv4(current, ip2buf); return 2; } /** * NI_ip_normalize_prefix_ipv6(): get first and last address from prefix range. * @ip: IP address (N128 integer). * @slash: pointer to first '/' in original string. * @ip1buf: first IP address buffer. * @ip2buf: second IP address buffer. * * Both buffers are null-terminated on success. */ int NI_ip_normalize_prefix_ipv6(n128_t *ip, char *slash, char *ip1buf, char *ip2buf) { n128_t current; char *endptr = NULL; int res; long clen = 0; int addcst = 0; char c; n128_set(¤t, ip); for (;;) { c = *slash++; if (c != '/') { break; } endptr = NULL; clen = strtol(slash, &endptr, 10); if (STRTOL_FAILED(clen, slash, endptr)) { return 0; } if (*endptr == ',') { addcst = 1; } else if (endptr != (slash + strlen(slash))) { NI_set_Error_Errno(172, "Invalid prefix length /%s", slash); return 0; } else { addcst = 0; } res = NI_ip_check_prefix_ipv6(¤t, clen); if (!res) { return 0; } NI_ip_last_address_ipv6(¤t, clen, ¤t); if (addcst) { n128_add_ui(¤t, 1); slash = endptr + 1; } } NI_ip_inttoip_n128(ip, ip1buf); NI_ip_inttoip_n128(¤t, ip2buf); return 2; } /** * NI_ip_normalize_prefix(): get first and last address from prefix range. * @ip: IP address prefix range as a string. * @ip1buf: first IP address buffer. * @ip2buf: second IP address buffer. * * The range can include commas and additional prefixes, e.g. * '0.0.0.0/32,/32,/32' will yield '0.0.0.0' and '0.0.0.2'. */ int NI_ip_normalize_prefix(char *ip, char *ip1buf, char *ip2buf) { char c; int res; int i; char *slash; int islash; char *start; unsigned char ipnum[16]; unsigned long ipv4; n128_t ipv6; int ipversion; i = 0; slash = NULL; islash = -1; start = ip; while ((c = *ip)) { if (isspace(c)) { return -1; } if (i && (c == '/') && (!slash)) { slash = ip; islash = i; } i++; ip++; } if (islash < 1) { return -1; } *slash = '\0'; ipversion = NI_ip_get_version(start); if (ipversion == 4) { res = inet_pton4(start, ipnum); if (!res) { return 0; } *slash = '/'; ipv4 = NI_ip_uchars_to_ulong(ipnum); return NI_ip_normalize_prefix_ipv4(ipv4, slash, ip1buf, ip2buf); } else if (ipversion == 6) { res = inet_pton6(start, ipnum); if (!res) { return 0; } *slash = '/'; NI_ip_uchars_to_n128(ipnum, &ipv6); res = NI_ip_normalize_prefix_ipv6(&ipv6, slash, ip1buf, ip2buf); return res; } else { return 0; } } /** * NI_ip_tokenize_on_char(): get parts of string before and after char. * @str: the string to tokenize. * @separator: the char that separates the two parts of the string. * @end_first: buffer for the end of the first string. * @second: buffer for the start of the second string. * * Tokenizes the string based on the @separator character. Ignores * whitespace occurring before and after @separator. For example, if * the string provided is '127.0.0.1 - 127.0.0.255', @end_first will * point to the space immediately after the first IP address, and * @second will point to the second IP address. */ int NI_ip_tokenize_on_char(char *str, char separator, char **end_first, char **second) { char c; char *break_char; int i; int hit_separator; break_char = NULL; i = 0; hit_separator = 0; while ((c = *str)) { if (c == separator) { hit_separator = 1; if (!break_char) { if (!i) { return 0; } else { break_char = str; } } break; } else if (isspace(c)) { if (!break_char) { break_char = str; } } else { break_char = NULL; } str++; i++; } if (!hit_separator) { return 0; } str++; c = *str; if (c == '\0') { return 0; } while ((c = *str) && (isspace(c))) { str++; } if (c == '\0') { return 0; } *end_first = break_char; *second = str; return 1; } /** * NI_ip_normalize_range(): get first and last address from a range. * @ip: the IP address range to normalize. * @ipbuf1: first IP address buffer. * @ipbuf2: second IP address buffer. * * @ip must be a range containing a hyphen, e.g. '127.0.0.0 - * 127.0.0.255'. Whitespace before and after the hyphen is ignored. * The IP address buffers will be null-terminated on success. */ int NI_ip_normalize_range(char *ip, char *ipbuf1, char *ipbuf2) { char *break_char; char *start; int ipversion; int res; char old_char; res = NI_ip_tokenize_on_char(ip, '-', &break_char, &start); if (!res) { return -1; } old_char = *break_char; *break_char = '\0'; ipversion = NI_ip_get_version(start); if (!ipversion) { *break_char = old_char; return 0; } res = NI_ip_expand_address(ip, ipversion, ipbuf1); *break_char = old_char; if (!res) { return 0; } res = NI_ip_expand_address(start, ipversion, ipbuf2); if (!res) { return 0; } return 2; } /** * NI_ip_normalize_plus_ipv4(): get first and last address from addition. * @ip: the IP address string. * @num: the number of addresses to add as a string. * @ipbuf1: first IP address buffer. * @ipbuf2: second IP address buffer. * * The IP address buffers will be null-terminated on success. */ int NI_ip_normalize_plus_ipv4(char *ip, char *num, char *ipbuf1, char *ipbuf2) { int res; char *endptr; unsigned char ipnum[4]; unsigned long ipv4; unsigned long addnum; res = inet_pton4(ip, ipnum); if (!res) { return 0; } ipv4 = NI_ip_uchars_to_ulong(ipnum); endptr = NULL; addnum = strtoul(num, &endptr, 10); if (STRTOUL_FAILED(addnum, num, endptr)) { return 0; } if (addnum > 0xFFFFFFFF) { return 0; } NI_ip_inttoip_ipv4(ipv4, ipbuf1); ipv4 += addnum; NI_ip_inttoip_ipv4(ipv4, ipbuf2); return 2; } /** * NI_ip_normalize_plus_ipv6(): get first and last address from addition. * @ip: the IP address string. * @num: the number of addresses to add as a string. * @ipbuf1: first IP address buffer. * @ipbuf2: second IP address buffer. * * The IP address buffers will be null-terminated on success. */ int NI_ip_normalize_plus_ipv6(char *ip, char *num, char *ipbuf1, char *ipbuf2) { unsigned char ipnum[16]; n128_t ipv6; n128_t addnum; int res; res = inet_pton6(ip, ipnum); if (!res) { return 0; } NI_ip_uchars_to_n128(ipnum, &ipv6); res = n128_set_str_decimal(&addnum, num, strlen(num)); if (!res) { return 0; } NI_ip_inttoip_n128(&ipv6, ipbuf1); n128_add(&ipv6, &addnum); NI_ip_inttoip_n128(&ipv6, ipbuf2); return 2; } /** * NI_ip_normalize_plus(): get first and last address from addition. * @ip: the IP address string. * @ipbuf1: first IP address buffer. * @ipbuf2: second IP address buffer. * * @ip must begin with an IP address, then contain a '+' and an * integer, e.g. '127.0.0.0 + 16777216', '2000::+1234849245892845'. * The IP address buffers will be null-terminated on success. */ int NI_ip_normalize_plus(char *ip1, char *ipbuf1, char *ipbuf2) { char *break_char; char *start; int ipversion; int res; char old_char; res = NI_ip_tokenize_on_char(ip1, '+', &break_char, &start); if (!res) { return -1; } old_char = *break_char; *break_char = '\0'; ipversion = NI_ip_get_version(ip1); switch (ipversion) { case 4: res = NI_ip_normalize_plus_ipv4(ip1, start, ipbuf1, ipbuf2); break; case 6: res = NI_ip_normalize_plus_ipv6(ip1, start, ipbuf1, ipbuf2); break; default: res = 0; } *break_char = old_char; return res; } /** * NI_ip_normalize_bare(): normalize a single IP address. * @ip: the IP address string. * @ipbuf1: the IP address buffer. * * Checks the version of the IP address and then expands it. For a * valid IP address, this function has the same effect as calling * NI_ip_expand_address(). The IP address buffer will be * null-terminated on success. */ int NI_ip_normalize_bare(char *ip, char *ipbuf1) { int ipversion; int res; ipversion = NI_ip_get_version(ip); if (!ipversion) { return 0; } res = NI_ip_expand_address(ip, ipversion, ipbuf1); if (!res) { return 0; } return 1; } /** * NI_ip_normalize(): normalize an IP address string. * @ip: the IP address string. * @ipbuf1: the first IP address buffer. * @ipbuf2: the second IP address buffer. * * The various formats that @ip can take are described in * NI_ip_normalize_prefix(), NI_ip_normalize_range(), * NI_ip_normalize_plus() and NI_ip_normalize_bare(). Returns zero on * failure, otherwise returns the number of IP address buffers that * were populated. Those buffers will be null-terminated on success. */ int NI_ip_normalize(char *ip, char *ipbuf1, char *ipbuf2) { int res; res = NI_ip_normalize_prefix(ip, ipbuf1, ipbuf2); if (res >= 0) { return res; } res = NI_ip_normalize_range(ip, ipbuf1, ipbuf2); if (res >= 0) { return res; } res = NI_ip_normalize_plus(ip, ipbuf1, ipbuf2); if (res >= 0) { return res; } res = NI_ip_normalize_bare(ip, ipbuf1); if (res >= 0) { return res; } return 0; } /** * NI_ip_normal_range(): normalize an IP address string into a range. * @ip: the IP address string. * @buf: the IP address range buffer. * * Uses NI_ip_normalize() to get the first and last (if applicable) * addresses from the string. Sets the buffer so that it is always in * range format (i.e. first address, hyphen, second address), even * where the IP address string contains only one address, in which * case both of the addresses will be the same. @buf is * null-terminated on success. */ int NI_ip_normal_range(char *ip, char *buf) { char ip1buf[MAX_IPV6_STR_LEN]; char ip2buf[MAX_IPV6_STR_LEN]; int res; res = NI_ip_normalize(ip, ip1buf, ip2buf); if (!res) { return 0; } sprintf(buf, "%s - %s", ip1buf, (res == 1) ? ip1buf : ip2buf); return 1; } /** * NI_ip_compress_v4_prefix(): get smallest representation of IPv4 prefix range. * @ip: the IP address. * @len: the prefix length of the range. * @buf: buffer for the compressed representation. * @maxlen: maximum capacity of buffer. */ int NI_ip_compress_v4_prefix(const char *ip, int len, char *buf, int maxlen) { int dotcount; const char *c; int buflen; if ((len < 0) || (len > 32)) { return 0; } if (strlen(ip) > (MAX_IPV4_RANGE_STR_LEN - 1)) { return 0; } c = ip; dotcount = (len == 0) ? 1 : ((len / 8) + (!(len % 8) ? 0 : 1)); while (dotcount--) { c = strchr(c, '.'); if (c == NULL) { c = ip + (strlen(ip) + 1); break; } if (*(c + 1) != '\0') { c++; } } buflen = c - ip - 1; if (buflen > maxlen) { buflen = maxlen; } strncpy(buf, ip, buflen); buf[buflen] = '\0'; return 1; } /** * NI_ip_compress_address(): get smallest representation of IPv6 prefix range. * @ip: the IP address. * @version: the IP address version. * @buf: buffer for the compressed representation. * * If @ip is an IPv4 address, it will simply be copied to @buf. */ int NI_ip_compress_address(const char *ip, int version, char *buf) { unsigned char ipv6[16]; int i; char mybuf[5]; int res; int in_ws = 0; int ws_index = -1; int ws_start[4]; int ws_count[4]; int largest_index; int largest; memset(ws_start, 0, 4 * sizeof(int)); memset(ws_count, 0, 4 * sizeof(int)); if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version for %s", ip); return 0; } if (version == 4) { strcpy(buf, ip); return 1; } res = inet_pton6(ip, ipv6); if (!res) { return 0; } for (i = 0; i < 16; i += 2) { if ((ipv6[i] == 0) && (ipv6[i + 1] == 0)) { if (!in_ws) { in_ws = 1; ws_start[++ws_index] = i; } ws_count[ws_index] += 1; } else { in_ws = 0; } } largest = 0; largest_index = -1; for (i = 0; i < 4; i++) { /* "The symbol '::' MUST NOT be used to shorten just one 16-bit 0 field. For example, the representation 2001:db8:0:1:1:1:1:1 is correct, but 2001:db8::1:1:1:1:1 is not correct" (RFC 5952, [4.2.2]). So make sure that ws_count is greater than 1. */ if (ws_count[i] > largest && ws_count[i] > 1) { largest = ws_count[i]; largest_index = i; } } for (i = 0; i < 16; i += 2) { if ((largest_index != -1) && (i == ws_start[largest_index])) { if (i == 0) { strcat(buf, ":"); } i += ((largest * 2) - 2); strcat(buf, ":"); } else { sprintf(mybuf, "%x", (ipv6[i] << 8) + ipv6[i + 1]); strcat(buf, mybuf); if (i < 14) { strcat(buf, ":"); } } } return 1; } /** * NI_ip_splitprefix(): split range into IP address and prefix length. * @prefix: the IP address prefix range. * @ipbuf: the IP address buffer. * @lenbuf: the prefix length buffer. */ int NI_ip_splitprefix(const char *prefix, char *ipbuf, int *lenbuf) { const char *c; const char *slash; char *endptr; long num; int len; c = slash = strchr(prefix, '/'); if (!slash) { return 0; } len = slash - prefix; if ((len == 0) || (len > (MAX_IPV6_STR_LEN - 1))) { return 0; } c++; if (*c == '\0') { return 0; } endptr = NULL; num = strtol(c, &endptr, 10); if (STRTOL_FAILED(num, c, endptr)) { return 0; } if (num < 0) { return 0; } memcpy(ipbuf, prefix, len); ipbuf[len] = '\0'; *lenbuf = num; return 1; } /** * NI_ip_iptype(): get type of IP address as a string. * @ip: the IP address. * @version: the IP address version. * @buf: the type buffer. * * The type buffer will be null-terminated on success. Relies on * IPv4ranges and IPv6ranges for determining types. */ int NI_ip_iptype(const char *ip, int version, char *buf) { HV *hash; HE *entry; char *key; I32 keylen; SV *value; STRLEN len; int current_keylen; char *typestr; hash = get_hv( (version == 4 ? "Net::IP::XS::IPv4ranges" : "Net::IP::XS::IPv6ranges"), 0); if (!hash) { return 0; } hv_iterinit(hash); current_keylen = 0; while ((entry = hv_iternext(hash))) { key = hv_iterkey(entry, &keylen); if (keylen > current_keylen) { if (!strncmp(key, ip, keylen)) { current_keylen = keylen; value = hv_iterval(hash, entry); typestr = SvPV(value, len); if (len > (MAX_TYPE_STR_LEN - 1)) { len = (MAX_TYPE_STR_LEN - 1); } memcpy(buf, typestr, len); buf[len] = '\0'; } } } if (current_keylen) { return 1; } if (version == 4) { memcpy(buf, "PUBLIC", 6); buf[6] = '\0'; return 1; } NI_set_Error_Errno(180, "Cannot determine type for %s", ip); return 0; } /** * NI_ip_is_valid_mask(): determine the validity of a bitstring mask. * @mask: bitstring mask. * @version: mask's IP address version. */ int NI_ip_is_valid_mask(const char *mask, int version) { const char *c; int iplen; int mask_len; int state; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version for %s", mask); return 0; } iplen = NI_iplengths(version); mask_len = strlen(mask); if (mask_len != iplen) { NI_set_Error_Errno(150, "Invalid mask length for %s", mask); return 0; } state = 0; c = mask; while (*c != '\0') { if ((*c == '1') && (state == 0)) { c++; continue; } if (*c == '0') { if (state == 0) { state = 1; } c++; continue; } NI_set_Error_Errno(151, "Invalid mask %s", mask); return 0; } return 1; } /** * NI_ip_prefix_to_range(): get begin/end addresses from address and length. * @ip: IP address. * @len: prefix length of range. * @version: IP address version. * @buf: last IP address buffer. */ int NI_ip_prefix_to_range(const char *ip, int len, int version, char *buf) { char bitstr1[IPV6_BITSTR_LEN]; char bitstr2[IPV6_BITSTR_LEN]; if (!version) { NI_set_Error_Errno(101, "Cannot determine IP version"); return 0; } if (!NI_ip_expand_address(ip, version, buf)) { return 0; } if (!NI_ip_iptobin(ip, version, bitstr1)) { return 0; } bitstr1[(version == 4) ? 32 : 128] = '\0'; if (!NI_ip_check_prefix(bitstr1, len, version)) { return 0; } NI_ip_last_address_bin(bitstr1, len, version, bitstr2); bitstr2[(version == 4) ? 32 : 128] = '\0'; if (!NI_ip_bintoip(bitstr2, version, buf)) { return 0; } return 1; } /** * NI_ip_get_embedded_ipv4(): get IPv4 address contained within IPv6 address. * @ipv6: IPv6 address as a string. * @buf: IPv4 address buffer. */ int NI_ip_get_embedded_ipv4(const char *ipv6, char *buf) { const char *c; int len; c = strrchr(ipv6, ':'); if (c == NULL) { c = ipv6; } else { c++; } len = strlen(c); if (len > (MAX_IPV4_STR_LEN - 1)) { len = (MAX_IPV4_STR_LEN - 1); } if ((len > 0) && NI_ip_is_ipv4(c)) { strncpy(buf, c, len); buf[len] = '\0'; return 1; } else { return 0; } } #ifdef __cplusplus } #endif Net-IP-XS-0.22/lib/0000755000175000017500000000000014363577433012306 5ustar tomhtomhNet-IP-XS-0.22/lib/Net/0000755000175000017500000000000014363577433013034 5ustar tomhtomhNet-IP-XS-0.22/lib/Net/IP/0000755000175000017500000000000014363577433013344 5ustar tomhtomhNet-IP-XS-0.22/lib/Net/IP/XS.pm0000644000175000017500000003031314363577220014226 0ustar tomhtomhpackage Net::IP::XS; use warnings; use strict; use 5.006; use Math::BigInt; use Tie::Simple; our $VERSION = '0.22'; our $IP_NO_OVERLAP = 0; our $IP_PARTIAL_OVERLAP = 1; our $IP_A_IN_B_OVERLAP = -1; our $IP_B_IN_A_OVERLAP = -2; our $IP_IDENTICAL = -3; our @EXPORT_OK = qw(Error Errno ip_iptobin ip_bintoip ip_iplengths ip_bintoint ip_inttobin ip_expand_address ip_is_ipv4 ip_is_ipv6 ip_get_version ip_get_mask ip_last_address_bin ip_splitprefix ip_is_valid_mask ip_bincomp ip_binadd ip_get_prefix_length ip_compress_v4_prefix ip_is_overlap ip_check_prefix ip_range_to_prefix ip_get_embedded_ipv4 ip_aggregate ip_prefix_to_range ip_reverse ip_normalize ip_compress_address ip_iptype ip_auth ip_normal_range $IP_NO_OVERLAP $IP_PARTIAL_OVERLAP $IP_A_IN_B_OVERLAP $IP_B_IN_A_OVERLAP $IP_IDENTICAL); our %EXPORT_TAGS = (PROC => [@EXPORT_OK]); use overload ( '+' => 'ip_add_num', 'bool' => sub { @_ }, ); use base qw(DynaLoader Exporter); __PACKAGE__->bootstrap($VERSION); our $ERROR; our $ERRNO; BEGIN { tie $ERROR, 'Tie::Simple', 1, FETCH => \&ip_get_Error, STORE => \&ip_set_Error; tie $ERRNO, 'Tie::Simple', 1, FETCH => \&ip_get_Errno, STORE => \&ip_set_Errno; }; our %IPv4ranges = ( '00000000' => 'PRIVATE', # 0/8 '00001010' => 'PRIVATE', # 10/8 '01111111' => 'PRIVATE', # 127.0/8 '101011000001' => 'PRIVATE', # 172.16/12 '1100000010101000' => 'PRIVATE', # 192.168/16 '1010100111111110' => 'RESERVED', # 169.254/16 '110000000000000000000010' => 'RESERVED', # 192.0.2/24 '1110' => 'RESERVED', # 224/4 '11110' => 'RESERVED', # 240/5 '11111' => 'RESERVED', # 248/5 ); our %IPv6ranges = ( '00000000' => 'RESERVED', # ::/8 '00000001' => 'RESERVED', # 0100::/8 '0000001' => 'RESERVED', # 0200::/7 '000001' => 'RESERVED', # 0400::/6 '00001' => 'RESERVED', # 0800::/5 '0001' => 'RESERVED', # 1000::/4 '001' => 'GLOBAL-UNICAST', # 2000::/3 '010' => 'RESERVED', # 4000::/3 '011' => 'RESERVED', # 6000::/3 '100' => 'RESERVED', # 8000::/3 '101' => 'RESERVED', # A000::/3 '110' => 'RESERVED', # C000::/3 '1110' => 'RESERVED', # E000::/4 '11110' => 'RESERVED', # F000::/5 '111110' => 'RESERVED', # F800::/6 '1111101' => 'RESERVED', # FA00::/7 '1111110' => 'UNIQUE-LOCAL-UNICAST', # FC00::/7 '111111100' => 'RESERVED', # FE00::/9 '1111111010' => 'LINK-LOCAL-UNICAST', # FE80::/10 '1111111011' => 'RESERVED', # FEC0::/10 '11111111' => 'MULTICAST', # FF00::/8 '00100000000000010000110110111000' => 'RESERVED', # 2001:DB8::/32 '0' x 96 => 'IPV4COMP', # ::/96 ('0' x 80) . ('1' x 16) => 'IPV4MAP', # ::FFFF:0:0/96 '0' x 128 => 'UNSPECIFIED', # ::/128 ('0' x 127) . '1' => 'LOOPBACK' # ::1/128 ); sub Error { $ERROR } sub Errno { $ERRNO } sub ip_bintoint { Math::BigInt->new(ip_bintoint_str($_[0])) } sub ip_inttobin { ip_inttobin_str(Math::BigInt->new($_[0]), $_[1]) } sub binip { $_[0]->{'binip'} } sub version { $_[0]->{'ipversion'} } sub error { $_[0]->{'error'} } sub errno { $_[0]->{'errno'} } sub prefixlen { $_[0]->{'prefixlen'} } sub ip { $_[0]->{'ip'} } sub is_prefix { $_[0]->{'is_prefix'} } sub binmask { $_[0]->{'binmask'} } sub size { Math::BigInt->new(size_str($_[0])) } sub intip { Math::BigInt->new(intip_str($_[0])) } sub last_int { Math::BigInt->new(last_int_str($_[0])) } sub auth { my ($self) = shift; return ($self->{auth}) if defined($self->{auth}); my $auth = ip_auth($self->ip, $self->version); if (!$auth) { $self->{error} = $ERROR; $self->{errno} = $ERRNO; return; } $self->{auth} = $auth; return ($self->{auth}); } sub ip_auth { my ($ip, $ip_version) = (@_); if (not $ip_version) { $ERROR = "Cannot determine IP version for $ip"; $ERRNO = 101; return; } if ($ip_version != 4) { $ERROR = "Cannot get auth information: Not an IPv4 address"; $ERRNO = 308; return; } require IP::Authority; my $reg = new IP::Authority; return ($reg->inet_atoauth($ip)); } 1; __END__ =head1 NAME Net::IP::XS - IPv4/IPv6 address library =head1 SYNOPSIS use Net::IP::XS; my $ip = new Net::IP::XS ('193.0.1/24') or die (Net::IP::XS::Error()); print ("IP : ".$ip->ip()."\n"); print ("Sho : ".$ip->short()."\n"); print ("Bin : ".$ip->binip()."\n"); print ("Int : ".$ip->intip()."\n"); print ("Mask: ".$ip->mask()."\n"); print ("Last: ".$ip->last_ip()."\n"); print ("Len : ".$ip->prefixlen()."\n"); print ("Size: ".$ip->size()."\n"); print ("Type: ".$ip->iptype()."\n"); print ("Rev: ".$ip->reverse_ip()."\n"); =head1 DESCRIPTION An XS (C) implementation of L. See L's documentation (as at version 1.25) for the functions and methods that are available. =head1 DIFFERENCES BETWEEN NET::IP AND NET::IP::XS =over 4 =item Exports Nothing is exported by default. =item Error messages In some instances this won't set error codes or messages where C would, though it should be mostly the same. =item Object-oriented interface The object-oriented interface uses function calls and hashref lookups internally, such that subclassing C will not have the same effect as it does with C. =item ip_auth Returns C on failure, instead of dying. =item ip_binadd Returns C if either of the bitstring arguments is more than 128 characters in length. Any character of the bitstring that is not a 0 is treated as a 1. The C version returns different results for different digits, and treats non-digits as 0. =item ip_bintoint The integer returned will be at most ((1 << 128) - 1) (i.e. the largest possible IPv6 address). C handles bitstrings of arbitrary length. =item ip_compress_address Returns C if the IPv6 address argument is invalid. =item ip_compress_v4_prefix Returns C if the C argument is negative or greater than 32. =item ip_expand_address Does not set C or C where there is a problem with an embedded IPv4 address within an IPv6 address. Returns the zero IP address if the empty string is provided. The C version returns C. Returns a full IPv6 address if a partial address is provided (e.g. returns 'ffff:ffff:0000:0000:0000:0000:0000:0000' if 'ffff:ffff' is provided). The C version returns the partial address. Returns C on an invalid IPv4/IPv6 address. The C version returns the zero address for IPv4 and whatever was provided for IPv6. =item ip_get_mask The mask returned will always have a length equal to the number of bits in an address of the specified IP version (e.g. an IPv4 mask will always comprise 32 characters). The C version will return a longer mask when the C argument is larger than the number of bits in the specified IP version. If a negative C is provided, it will be treated as zero. =item ip_inttobin The bitstring returned will always be either 32 or 128 characters in length, and it returns C if the integer argument would require more than 128 characters to represent as a bitstring. If an invalid version is provided, the returned bitstring will be 128 characters in length. The C version handles arbitrary integers and expands to accommodate those integers, regardless of the version argument. Also, if an invalid version is provided, the returned bitstring is only as long as is necessary to accommodate the bitstring. =item ip_iptobin Returns C on an invalid IPv4/IPv6 address. =item ip_last_address_bin Returns an empty string if an invalid version (i.e. not 4 or 6) is provided. If the bitstring provided is longer than the number of bits in the specified version, then only the first 32/128 bits will be used in determining the last address. If the C provided is invalid (negative or more than 32/128 depending on the version), it will be treated as the maximum length of the specified version. =item ip_normalize For the 'plus' style of string (e.g. '1.0.0.0 + 255'), whitespace between the plus character and the parts before and after it is optional. In the C version, there has to be some whitespace before and after the plus character. Also, C will be returned if the part after the plus sign is not a number. The C version will return two copies of the single address in this instance. For the 'prefix range' style of string (e.g. '1.0.0.0/8'), the part after the slash must be a number. If it is not, C will be returned. The C version will return two copies of the single address in this instance. =item ip_range_to_prefix Returns C if the version argument is invalid. =item ip_reverse The C argument determines the length of the reverse domain - e.g., if the arguments are '127.0.0.1', '16' and '4', the reverse domain will be '0.127.in-addr.arpa.'. The C version does not take the C argument into account for IPv4 addresses. For IPv6 addresses, a compressed IP address string may be provided. =item ip_splitprefix Returns C unless the first component of the string is less than or equal to 64 characters in length. The C version handles strings of arbitrary length. =item prefix Returns a string with a prefix length of zero (e.g. '127.0.0.1/0') where C is not defined in the object. The C version will not include any prefix length in the returned string (e.g. '127.0.0.1/'). =back =head1 AUTHOR Tom Harrison, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. =head1 ACKNOWLEDGEMENTS Manuel Valente (C<< >>) and the other authors of L. =head1 SEE ALSO L, L. =head1 COPYRIGHT & LICENSE Copyright (C) 2010-2023 Tom Harrison . Original inet_pton4 and inet_pton6 functions are copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut Net-IP-XS-0.22/NetIpXs.xs0000644000175000017500000006607314363577312013456 0ustar tomhtomh/* NetIpXs.xs - XS wrapper around the core Net::IP::XS functions. Copyright (C) 2010-2023 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include #include #include #include #include "inet_pton.h" #include "functions.h" #include "object.h" MODULE = Net::IP::XS::N128 PACKAGE = Net::IP::XS::N128 PREFIX = net_ip_xs_n128 SV * new(package) char *package PREINIT: HV *stash; SV *ref; SV *num_ref; n128_t num; CODE: stash = gv_stashpv("Net::IP::XS::N128", 1); n128_set_ui(&num, 0); num_ref = newSVpv((const char*) &num, 16); ref = newRV_noinc(num_ref); sv_bless(ref, stash); RETVAL = ref; OUTPUT: RETVAL int set_ui(self, ui) SV *self unsigned int ui PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_set_ui(&num, ui); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int set_binstr(self, binstr) SV *self const char *binstr PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_set_str_binary(&num, binstr, strlen(binstr)); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int set_decstr(self, decstr) SV *self const char *decstr PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_set_str_decimal(&num, decstr, strlen(decstr)); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int cmp_ui(self, ui) SV *self unsigned int ui PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); RETVAL = n128_cmp_ui(&num, ui); } OUTPUT: RETVAL int cmp(self, other) SV *self SV *other PREINIT: STRLEN len; n128_t num1; n128_t num2; CODE: if (!sv_isa(self, "Net::IP::XS::N128") || !sv_isa(other, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num1, 1, n128_t); Copy(SvPV(SvRV(other), len), &num2, 1, n128_t); RETVAL = n128_cmp(&num1, &num2); } OUTPUT: RETVAL int blsft(self, shift) SV *self int shift PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_blsft(&num, shift); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int brsft(self, shift) SV *self int shift PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_brsft(&num, shift); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int band(self, other) SV *self SV *other PREINIT: STRLEN len; n128_t num1; n128_t num2; CODE: if (!sv_isa(self, "Net::IP::XS::N128") || !sv_isa(other, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num1, 1, n128_t); Copy(SvPV(SvRV(other), len), &num2, 1, n128_t); n128_and(&num1, &num2); sv_setpvn(SvRV(self), (const char*) &num1, 16); RETVAL = 1; } OUTPUT: RETVAL int bior(self, other) SV *self SV *other PREINIT: STRLEN len; n128_t num1; n128_t num2; CODE: if (!sv_isa(self, "Net::IP::XS::N128") || !sv_isa(other, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num1, 1, n128_t); Copy(SvPV(SvRV(other), len), &num2, 1, n128_t); n128_ior(&num1, &num2); sv_setpvn(SvRV(self), (const char*) &num1, 16); RETVAL = 1; } OUTPUT: RETVAL int bxor(self, other) SV *self SV *other PREINIT: STRLEN len; n128_t num1; n128_t num2; CODE: if (!sv_isa(self, "Net::IP::XS::N128") || !sv_isa(other, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num1, 1, n128_t); Copy(SvPV(SvRV(other), len), &num2, 1, n128_t); n128_xor(&num1, &num2); sv_setpvn(SvRV(self), (const char*) &num1, 16); RETVAL = 1; } OUTPUT: RETVAL int badd(self, other) SV *self SV *other PREINIT: STRLEN len; n128_t num1; n128_t num2; CODE: if (!sv_isa(self, "Net::IP::XS::N128") || !sv_isa(other, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num1, 1, n128_t); Copy(SvPV(SvRV(other), len), &num2, 1, n128_t); n128_add(&num1, &num2); sv_setpvn(SvRV(self), (const char*) &num1, 16); RETVAL = 1; } OUTPUT: RETVAL int bsub(self, other) SV *self SV *other PREINIT: STRLEN len; n128_t num1; n128_t num2; CODE: if (!sv_isa(self, "Net::IP::XS::N128") || !sv_isa(other, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num1, 1, n128_t); Copy(SvPV(SvRV(other), len), &num2, 1, n128_t); n128_sub(&num1, &num2); sv_setpvn(SvRV(self), (const char*) &num1, 16); RETVAL = 1; } OUTPUT: RETVAL int badd_ui(self, ui) SV *self unsigned int ui PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_add_ui(&num, ui); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int bnot(self) SV *self PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_com(&num); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int tstbit(self, bit) SV *self int bit PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); RETVAL = n128_tstbit(&num, bit); } OUTPUT: RETVAL int setbit(self, bit) SV *self int bit PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_setbit(&num, bit); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL int clrbit(self, bit) SV *self int bit PREINIT: STRLEN len; n128_t num; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = 0; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_clrbit(&num, bit); sv_setpvn(SvRV(self), (const char*) &num, 16); RETVAL = 1; } OUTPUT: RETVAL SV * bstr(self) SV *self PREINIT: STRLEN len; n128_t num; char buf[40]; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = &PL_sv_undef; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_print_dec(&num, buf); RETVAL = newSVpv(buf, 0); } OUTPUT: RETVAL SV * as_hex(self) SV *self PREINIT: STRLEN len; n128_t num; char buf[40]; CODE: if (!sv_isa(self, "Net::IP::XS::N128")) { RETVAL = &PL_sv_undef; } else { Copy(SvPV(SvRV(self), len), &num, 1, n128_t); n128_print_hex(&num, buf); RETVAL = newSVpv(buf, 0); } OUTPUT: RETVAL MODULE = Net::IP::XS PACKAGE = Net::IP::XS PROTOTYPES: ENABLE SV * ip_get_Error(data) void *data CODE: RETVAL = newSVpv(NI_get_Error(), 0); OUTPUT: RETVAL void ip_set_Error(data, str) void *data char *str CODE: NI_set_Error(str); SV * ip_get_Errno(data) void *data CODE: RETVAL = newSViv(NI_get_Errno()); OUTPUT: RETVAL void ip_set_Errno(data, num) void *data int num CODE: NI_set_Errno(num); SV * ip_is_ipv4(ip) char *ip CODE: RETVAL = newSViv(NI_ip_is_ipv4(ip)); OUTPUT: RETVAL SV * ip_is_ipv6(ip) char *ip CODE: RETVAL = newSViv(NI_ip_is_ipv6(ip)); OUTPUT: RETVAL SV * ip_binadd(begin, end) char *begin char *end PREINIT: char buf[IPV6_BITSTR_LEN]; int res; CODE: buf[0] = '\0'; res = NI_ip_binadd(begin, end, buf, IPV6_BITSTR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_get_prefix_length(bin1, bin2) char *bin1 char *bin2 PREINIT: int res; int result; CODE: res = NI_ip_get_prefix_length(bin1, bin2, &result); RETVAL = (res) ? newSViv(result) : &PL_sv_undef; OUTPUT: RETVAL void ip_splitprefix(prefix) char *prefix PREINIT: char buf[MAX_IPV6_STR_LEN]; int len; int res; PPCODE: res = NI_ip_splitprefix(prefix, buf, &len); if (res) { XPUSHs(sv_2mortal(newSVpv(buf, 0))); XPUSHs(sv_2mortal(newSViv(len))); } SV * ip_is_valid_mask(mask, ipversion) char *mask int ipversion PREINIT: int res; CODE: res = NI_ip_is_valid_mask(mask, ipversion); RETVAL = (res) ? newSViv(1) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_expand_address(ip, ipversion) char *ip int ipversion PREINIT: int res; char buf[MAX_IPV6_STR_LEN]; CODE: buf[0] = '\0'; res = NI_ip_expand_address(ip, ipversion, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_bincomp(begin, op_arg, end) char *begin char *op_arg char *end PREINIT: int res; int result; CODE: res = NI_ip_bincomp(begin, op_arg, end, &result); RETVAL = (res) ? newSViv(result) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_get_mask(len, ipversion) int len int ipversion PREINIT: int res; char buf[128]; CODE: res = NI_ip_get_mask(len, ipversion, buf); RETVAL = (res) ? newSVpv(buf, NI_iplengths(ipversion)) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_last_address_bin(binip, len, ipversion) char *binip int len int ipversion PREINIT: char buf[128]; int res; CODE: res = NI_ip_last_address_bin(binip, len, ipversion, buf); RETVAL = (res) ? newSVpv(buf, NI_iplengths(ipversion)) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_get_version(ip) char *ip PREINIT: int res; CODE: res = NI_ip_get_version(ip); RETVAL = (res) ? newSViv(res) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_inttobin_str(str, ipversion) char *str int ipversion PREINIT: char buf[129]; int res; CODE: res = NI_ip_inttobin_str(str, ipversion, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_bintoint_str(binip) char *binip PREINIT: char buf[MAX_IPV6_NUM_STR_LEN]; CODE: NI_ip_bintoint_str(binip, buf); RETVAL = newSVpv(buf, 0); OUTPUT: RETVAL SV * ip_iplengths(ipversion) int ipversion PREINIT: int res; CODE: res = NI_iplengths(ipversion); RETVAL = (res) ? newSViv(res) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_bintoip(ip, ipversion) char *ip int ipversion PREINIT: char buf[MAX_IPV6_STR_LEN]; int res; CODE: buf[0] = '\0'; res = NI_ip_bintoip(ip, ipversion, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_iptobin(ip, ipversion) char *ip int ipversion PREINIT: char buf[128]; int res; CODE: res = NI_ip_iptobin(ip, ipversion, buf); RETVAL = (res) ? newSVpv(buf, NI_iplengths(ipversion)) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_is_overlap(b1, e1, b2, e2) char *b1 char *e1 char *b2 char *e2 PREINIT: int res; int result; CODE: res = NI_ip_is_overlap(b1, e1, b2, e2, &result); RETVAL = (res) ? newSViv(result) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_check_prefix(ip, len, ipversion) char *ip int len int ipversion PREINIT: int res; CODE: res = NI_ip_check_prefix(ip, len, ipversion); RETVAL = (res) ? newSViv(res) : &PL_sv_undef; OUTPUT: RETVAL void ip_range_to_prefix(begin, end, ipversion) char *begin char *end int ipversion PREINIT: char *prefixes[MAX_PREFIXES]; int pcount; int res; int i; PPCODE: pcount = 0; res = NI_ip_range_to_prefix(begin, end, ipversion, prefixes, &pcount); if (!res) { for (i = 0; i < pcount; i++) { free(prefixes[i]); } ST(0) = &PL_sv_undef; } else { for (i = 0; i < pcount; i++) { XPUSHs(sv_2mortal(newSVpv(prefixes[i], 0))); free(prefixes[i]); } } SV * ip_get_embedded_ipv4(ipv6) char *ipv6 PREINIT: char buf[16]; int res; CODE: res = NI_ip_get_embedded_ipv4(ipv6, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_aggregate(b1, e1, b2, e2, ipversion) char *b1 char *e1 char *b2 char *e2 int ipversion PREINIT: char buf[MAX_IPV6_RANGE_STR_LEN]; int res; CODE: res = NI_ip_aggregate(b1, e1, b2, e2, ipversion, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL void ip_prefix_to_range(ip, len, version) char *ip int len int version PREINIT: char buf[MAX_IPV6_RANGE_STR_LEN]; int res; PPCODE: res = NI_ip_prefix_to_range(ip, len, version, buf); if (res) { XPUSHs(sv_2mortal(newSVpv(ip, 0))); XPUSHs(sv_2mortal(newSVpv(buf, 0))); } else { ST(0) = &PL_sv_undef; } SV * ip_reverse(ip, len, ipversion) char *ip int len int ipversion PREINIT: char buf[MAX_IPV6_REVERSE_LEN]; int res; CODE: buf[0] = '\0'; res = NI_ip_reverse(ip, len, ipversion, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL void ip_normalize(ip) char *ip PREINIT: char buf1[MAX_IPV6_STR_LEN]; char buf2[MAX_IPV6_STR_LEN]; int res; PPCODE: buf1[0] = '\0'; buf2[0] = '\0'; res = NI_ip_normalize(ip, buf1, buf2); if (res >= 1) { XPUSHs(sv_2mortal(newSVpv(buf1, 0))); } if (res >= 2) { XPUSHs(sv_2mortal(newSVpv(buf2, 0))); } SV * ip_normal_range(ip) char *ip PREINIT: char buf[MAX_IPV6_NORMAL_RANGE]; int res; CODE: res = NI_ip_normal_range(ip, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_compress_address(ip, version) char *ip int version PREINIT: char buf[MAX_IPV6_STR_LEN]; int res; CODE: buf[0] = '\0'; res = NI_ip_compress_address(ip, version, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_compress_v4_prefix(ip, len) char *ip int len PREINIT: char buf[MAX_IPV4_RANGE_STR_LEN]; int res; CODE: buf[0] = '\0'; res = NI_ip_compress_v4_prefix(ip, len, buf, MAX_IPV4_RANGE_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * ip_iptype(ip, ipversion) char *ip int ipversion PREINIT: char buf[MAX_TYPE_STR_LEN]; int res; CODE: res = NI_ip_iptype(ip, ipversion, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; OUTPUT: RETVAL SV * new(package, data, ...) char *package char *data PREINIT: HV *stash; HV *hash; SV *ref; int res; int ipversion; CODE: ipversion = (items > 2) ? SvIV(ST(2)) : 0; hash = newHV(); ref = newRV_noinc((SV*) hash); stash = gv_stashpv(package, 1); sv_bless(ref, stash); res = NI_set(ref, data, ipversion); if (!res) { SvREFCNT_dec(ref); RETVAL = &PL_sv_undef; } else { RETVAL = ref; } OUTPUT: RETVAL SV * print(self) SV *self PREINIT: char buf[MAX_IPV6_NORMAL_RANGE]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_print(self, buf, MAX_IPV6_NORMAL_RANGE); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * size_str(self) SV *self PREINIT: char buf[MAX_IPV6_NUM_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_size_str(self, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * intip_str(self) SV *self PREINIT: char buf[MAX_IPV6_NUM_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_intip_str(self, buf, MAX_IPV6_NUM_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * hexip(self) SV *self PREINIT: char buf[MAX_IPV6_HEXIP_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_hexip(self, buf, MAX_IPV6_HEXIP_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * hexmask(self) SV *self PREINIT: char buf[MAX_IPV6_HEXIP_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_hexmask(self, buf, MAX_IPV6_HEXIP_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * prefix(self) SV *self PREINIT: char buf[MAX_IPV6_RANGE_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_prefix(self, buf, MAX_IPV6_RANGE_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * mask(self) SV *self PREINIT: char buf[128]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_mask(self, buf, 128); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * iptype(self) SV *self PREINIT: char buf[MAX_TYPE_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_iptype(self, buf, MAX_TYPE_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * reverse_ip(self) SV *self PREINIT: char buf[MAX_IPV6_REVERSE_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { buf[0] = '\0'; res = NI_reverse_ip(self, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * last_bin(self) SV *self PREINIT: char buf[IPV6_BITSTR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { buf[0] = '\0'; res = NI_last_bin(self, buf, IPV6_BITSTR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * last_int_str(self) SV *self PREINIT: char buf[MAX_IPV6_NUM_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { buf[0] = '\0'; res = NI_last_int_str(self, buf, MAX_IPV6_NUM_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * last_ip(self) SV *self PREINIT: char buf[MAX_IPV6_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { buf[0] = '\0'; res = NI_last_ip(self, buf, MAX_IPV6_STR_LEN); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * short(self) SV *self PREINIT: char buf[MAX_IPV6_STR_LEN]; int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { buf[0] = '\0'; res = NI_short(self, buf); RETVAL = (res) ? newSVpv(buf, 0) : &PL_sv_undef; } OUTPUT: RETVAL SV * bincomp(self, op, other) SV *self char *op SV *other PREINIT: int res; int result; CODE: if (!sv_isa(self, "Net::IP::XS") || !sv_isa(other, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_bincomp(self, op, other, &result); RETVAL = (res) ? newSViv(result) : &PL_sv_undef; } OUTPUT: RETVAL SV * binadd(self, other) SV *self SV *other PREINIT: SV *new_ip; CODE: if (!sv_isa(self, "Net::IP::XS") || !sv_isa(other, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { new_ip = NI_binadd(self, other); RETVAL = (new_ip) ? new_ip : &PL_sv_undef; } OUTPUT: RETVAL SV * aggregate(self, other) SV *self SV *other PREINIT: SV *new_ip; CODE: if (!sv_isa(self, "Net::IP::XS") || !sv_isa(other, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { new_ip = NI_aggregate(self, other); RETVAL = (new_ip) ? new_ip : &PL_sv_undef; } OUTPUT: RETVAL SV * overlaps(self, other) SV *self SV *other PREINIT: int res; int result; CODE: if (!sv_isa(self, "Net::IP::XS") || !sv_isa(other, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_overlaps(self, other, &result); RETVAL = (res) ? newSViv(result) : &PL_sv_undef; } OUTPUT: RETVAL void find_prefixes(self) SV *self PREINIT: char *prefixes[MAX_PREFIXES]; int pcount; int res; int i; PPCODE: if (!sv_isa(self, "Net::IP::XS")) { ST(0) = &PL_sv_undef; } else { pcount = 0; res = NI_find_prefixes(self, prefixes, &pcount); if (!res) { for (i = 0; i < pcount; i++) { free(prefixes[i]); } ST(0) = &PL_sv_undef; } else { for (i = 0; i < pcount; i++) { XPUSHs(sv_2mortal(newSVpv(prefixes[i], 0))); free(prefixes[i]); } } } SV * ip_add_num(self, num, unused) SV *self char *num SV *unused PREINIT: SV *new_ip; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { new_ip = NI_ip_add_num(self, num); RETVAL = (new_ip) ? new_ip : &PL_sv_undef; } OUTPUT: RETVAL SV * set_ipv6_n128s(self) SV *self PREINIT: int res; CODE: if (!sv_isa(self, "Net::IP::XS")) { RETVAL = &PL_sv_undef; } else { res = NI_set_ipv6_n128s(self); RETVAL = (res) ? newSViv(1) : &PL_sv_undef; } OUTPUT: RETVAL Net-IP-XS-0.22/n128.h0000644000175000017500000000402414363576625012403 0ustar tomhtomh/* n128.h - 128-bit integer. Copyright (C) 2012-2014 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef N128 #define N128 #include #ifdef __cplusplus extern "C" { #endif typedef struct n128 { uint32_t nums[4]; } n128_t; void n128_print_bin(n128_t *n, char *buf, int ui_only); void n128_set(n128_t *dst, n128_t *src); int n128_scan1(n128_t *n); int n128_scan0(n128_t *n); int n128_add(n128_t *a, n128_t *b); int n128_add_ui(n128_t *a, unsigned int ui); int n128_set_str_decimal(n128_t *n, const char *str, int len); void n128_set_str_binary(n128_t *n, const char *bitstr, int len); void n128_ior(n128_t *n1, n128_t *n2); void n128_xor(n128_t *n1, n128_t *n2); void n128_and(n128_t *n1, n128_t *n2); void n128_com(n128_t *n1); int n128_cmp(n128_t *n1, n128_t *n2); void n128_clrbit(n128_t *n, int bit); void n128_setbit(n128_t *n, int bit); int n128_tstbit(n128_t *n, int bit); int n128_sub(n128_t *n, n128_t *sub); void n128_brsft(n128_t *n, int sft); void n128_blsft(n128_t *n, int sft); int n128_cmp_ui(n128_t *n, unsigned int ui); void n128_set_ui(n128_t *n, unsigned int ui); void n128_print_hex(n128_t *n, char *buf); void n128_print_dec(n128_t *narg, char *buf); #ifdef __cplusplus } #endif #endif Net-IP-XS-0.22/inet_pton.h0000644000175000017500000000361114363576625013713 0ustar tomhtomh/* inet_pton.h -- convert IPv4 and IPv6 addresses from text to binary form Modifications to inet_pton6 allowing IPv4 addresses to appear throughout, and miscellaneous modifications to inet_pton4, by Tom Harrison (Copyright (C) 2010). Copyright (C) 2006 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Copyright (c) 1996,1999 by Internet Software Consortium. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef NETIP_INET_PTON #define NETIP_INET_PTON int inet_pton4 (const char *src, unsigned char *dst); int inet_pton6 (const char *src, unsigned char *dst); #endif Net-IP-XS-0.22/object.h0000644000175000017500000000573514363577226013171 0ustar tomhtomh/* object.h - Functions for Net::IP::XS's object-oriented interface. Copyright (C) 2010-2023 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef NETIP_OBJECT #define NETIP_OBJECT #ifdef __cplusplus extern "C" { #endif #include "limits.h" void NI_object_set_Error_Errno(SV *ipo, int Errno, char *Error, ...); void NI_copy_Error_Errno(SV *ipo); int NI_find_prefixes(SV *ipo, char **prefixes, int *pcount); int NI_set_ipv6_n128s(SV *ipo); int NI_set(SV* ip, char *data, int ipversion); int NI_get_begin_n128(SV *ipo, n128_t **begin); int NI_get_end_n128(SV *ipo, n128_t **end); int NI_get_n128s(SV *ipo, n128_t **begin, n128_t **end); int NI_short(SV *ipo, char *buf); int NI_print(SV *ipo, char *buf, int maxlen); int NI_last_ip(SV *ipo, char *buf, int maxlen); int NI_size_str_ipv4(SV *ipo, char *buf); int NI_size_str_ipv6(SV *ipo, char *buf); int NI_size_str(SV *ipo, char *size); int NI_intip_str_ipv4(SV *ipo, char *buf); int NI_intip_str_ipv6(SV *ipo, char *buf); int NI_intip_str(SV *ipo, char *buf, int maxlen); int NI_hexip_ipv4(SV *ipo, char *buf); int NI_hexip_ipv6(SV *ipo, char *hexip); int NI_hexip(SV *ipo, char *buf, int maxlen); int NI_hexmask(SV *ipo, char *buf, int maxlen); int NI_prefix(SV *ipo, char *buf, int maxlen); int NI_mask(SV *ipo, char *buf, int maxlen); int NI_iptype(SV *ipo, char *buf, int maxlen); int NI_reverse_ip(SV *ipo, char *buf); int NI_last_bin(SV *ipo, char *buf, int maxlen); int NI_last_int_str_ipv4(SV *ipo, char *buf); int NI_last_int_str_ipv6(SV *ipo, char *buf); int NI_last_int_str(SV *ipo, char *buf, int maxlen); int NI_bincomp(SV *ipo1, char *op, SV *ipo2, int *resbuf); int NI_overlaps_ipv4(SV *ipo1, SV* ipo2, int *buf); int NI_overlaps_ipv6(SV *ipo1, SV* ipo2, int *buf); int NI_overlaps(SV *ipo1, SV* ipo2, int *buf); SV *NI_binadd(SV *ipo1, SV *ipo2); SV *NI_aggregate_ipv4(SV *ipo1, SV *ipo2); SV *NI_aggregate_ipv6(SV *ipo1, SV *ipo2); SV *NI_aggregate(SV *ipo1, SV *ipo2); int NI_ip_add_num_ipv4(SV *ipo, unsigned long num, char *buf); int NI_ip_add_num_ipv6(SV *ipo, n128_t *num, char *buf); SV *NI_ip_add_num(SV *ipo, const char *num); #ifdef __cplusplus } #endif #endif Net-IP-XS-0.22/Changes0000644000175000017500000000671614363577052013042 0ustar tomhtomhRevision history for Net-IP-XS 0.22 24 January 2023 - Fix uninitialised buffer problem (Github #4). 0.21 5 February 2018 - Avoid unnecessary calls to NI_ip_get_version when constructing objects (Github #2). 0.20 26 October 2017 - Fix subroutine redefinition problem (RT #122438). 0.19 5 November 2016 - Updated manifest. 0.18 3 November 2016 - Fix strtol usage (RT #118605). - Fix parsing problem in ip_normalize (RT #118593). 0.17 22 March 2016 - Make IP::Authority an optional dependency, as in Net::IP. 0.16 9 November 2015 - Set array size correctly in NI_ip_is_ipv6 (RT #108605). 0.15 18 February 2015 - Improve the fixes done as part of 0.14. - Fix a similar problem with the Net::IP::XS::N128 bindings. This only affects tests, since the IPv6 functions use the n128_t type directly. 0.14 17 February 2015 - Update internal n128 handling so that IP objects can be used in conjunction with threads. 0.13 15 February 2014 - Updated manifest. 0.12 15 February 2014 - Adjust ip_is_ipv6 so that it rejects truncated addresses. - Handle '::' when it is used to shorten a single 16-bit field. (Such an address is technically invalid (RFC 5952 [4.2.2]), but this change is in keeping with this module's generally lax approach to parsing.) 0.11 20 June 2013 - Fix IPv4 object size problem (if a range began at 0.0.0.0, size() returned (1 << 32), regardless of the actual size of the range). 0.10 22 December 2012 - Minor text/makefile updates. 0.09 16 May 2012 - Remove Tie::Hash::Sorted dependency. It was not strictly necessary that the hash always be sorted, and it's more in line with how Net::IP works for it to only sort when NI_ip_iptype is called. - Fix NI_ip_is_ipv4 and NI_ip_is_ipv6 problems (both were writing past the end of an array in some cases). - Add 128-bit integer tests. 0.08 15 May 2012 - Remove GMP dependency. - Fix NI_ip_aggregate memory error on invalid input. - Change NI_ip_binadd's behaviour on invalid input (non-zeroes treated as ones). - NI_ip_bintoint will now return at most ((1 << 128) - 1). - NI_ip_inttobin's result will now always be either 32 or 128 characters in length. 0.07 18 August 2011 - NI_ip_compress_v4_prefix now matches the behaviour of the version from Net::IP (it was returning incorrect results when the prefix length was not on an octet boundary). 0.06 11 March 2011 - Fixed 0.05 change (it led to a memory leak in NI_aggregate). - Now compiles when using a C++ compiler (tested with gcc-g++). - Miscellaneous problems (wrong types, unused variables). 0.05 08 March 2011 - Missing 'return 0' statement on aggregation failure in the IPv6 aggregate object method. 0.04 07 March 2011 - Use Devel::CheckLib when installing to see whether GMP is present. 0.03 26 February 2011 - Modified NI_ip_compress_address so that it does not use '::' to shorten a single 16-bit 0 field (see RFC 5952). 0.02 10 April 2010 - Added ip_add_num function and +/bool overloads. - Added 'PROC' export tag. 0.01 06 April 2010 - Initial release. Net-IP-XS-0.22/functions.h0000644000175000017500000002227414363577237013732 0ustar tomhtomh/* functions.h - Core functions for Net::IP::XS. Copyright (C) 2010-2023 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef NETIP_FUNCTIONS #define NETIP_FUNCTIONS #include "limits.h" #include "n128.h" #ifdef __cplusplus extern "C" { #endif #define STRTOL_FAILED(ret, str, end) \ (((((ret) == LONG_MAX || ((ret) == LONG_MIN)) && (errno == ERANGE)) \ || (((ret) == 0) && ((str) == (end))))) #define STRTOUL_FAILED(ret, str, end) \ (((((ret) == ULONG_MAX || ((ret) == 0)) && (errno == ERANGE)) \ || (((ret) == 0) && ((str) == (end))))) /* Comparator constants (less than, less than or equal to, etc.). */ #define LT 1 #define LE 2 #define GT 3 #define GE 4 /* Overlap constants. */ #define IP_NO_OVERLAP 0 #define IP_PARTIAL_OVERLAP 1 #define IP_A_IN_B_OVERLAP -1 #define IP_B_IN_A_OVERLAP -2 #define IP_IDENTICAL -3 /* String length constants. */ #define MAX_IPV4_STR_LEN 16 #define MAX_IPV4_RANGE_STR_LEN 19 #define IPV4_BITSTR_LEN 33 #define MAX_IPV4_REVERSE_LEN 30 #define MAX_IPV6_STR_LEN 64 #define MAX_IPV6_RANGE_STR_LEN 68 #define IPV6_BITSTR_LEN 129 #define MAX_IPV6_REVERSE_LEN 74 #define MAX_IPV6_NORMAL_RANGE 82 #define MAX_IPV6_HEXIP_STR_LEN 35 #define MAX_TYPE_STR_LEN 256 #define MAX_PREFIXES 128 #define MAX_IPV6_NUM_STR_LEN 40 const char *NI_hv_get_pv(SV *object, const char *key, int keylen); int NI_hv_get_iv(SV *object, const char *key, int keylen); unsigned int NI_hv_get_uv(SV *object, const char *key, int keylen); void NI_set_Errno(int Errno); int NI_get_Errno(void); void NI_set_Error(const char *Error); const char *NI_get_Error(void); void NI_set_Error_Errno(int Errno, const char *Error, ...); int NI_hdtoi(char c); int NI_trailing_zeroes(unsigned long n); unsigned long NI_bintoint(const char *bitstr, int len); unsigned long NI_ip_uchars_to_ulong(unsigned char uchars[4]); void NI_ip_uchars_to_n128(unsigned char uchars[16], n128_t *num); int NI_iplengths(int version); void NI_ip_n128tobin(n128_t *num, int len, char *buf); void NI_ip_binton128(const char *bitstr, int len, n128_t *num); int NI_ip_inttobin_str(const char *ip_int_str, int version, char *buf); int NI_ip_bintoint_str(const char *bitstr, char *buf); int NI_ip_is_ipv4(const char *str); int NI_ip_is_ipv6(const char *str); int NI_ip_get_version(const char *str); int NI_ip_get_mask(int len, int version, char *buf); int NI_ip_last_address_ipv6(n128_t *ip, int len, n128_t *buf); unsigned long NI_ip_last_address_ipv4(unsigned long ip, int len); int NI_ip_last_address_bin(const char *bitstr, int len, int version, char *buf); int NI_ip_bincomp(const char *bitstr_1, const char *op_str, const char *bitstr_2, int *result); void NI_ip_is_overlap_ipv6(n128_t *begin_1, n128_t *end_1, n128_t *begin_2, n128_t *end_2, int *result); void NI_ip_is_overlap_ipv4(unsigned long begin_1, unsigned long end_1, unsigned long begin_2, unsigned long end_2, int *result); int NI_ip_is_overlap(const char *begin_1, const char *end_1, const char *begin_2, const char *end_2, int *result); int NI_ip_check_prefix_ipv6(n128_t *ip, int len); int NI_ip_check_prefix_ipv4(unsigned long ip, int len); int NI_ip_check_prefix(const char *bitstr, int len, int version); void NI_ip_get_prefix_length_ipv6(n128_t *n128_1, n128_t *n128_2, int bits, int *len); void NI_ip_get_prefix_length_ipv4(unsigned long begin, unsigned long end, int bits, int *len); int NI_ip_get_prefix_length(const char *bitstr_1, const char *bitstr_2, int *len); void NI_ip_inttoip_ipv6(unsigned long n1, unsigned long n2, unsigned long n3, unsigned long n4, char *buf); void NI_ip_inttoip_ipv4(unsigned long n, char *buf); void NI_ip_inttoip_n128(n128_t *ip, char *buf); int NI_ip_bintoip(const char *bitstr, int version, char *buf); int NI_ip_binadd(const char *first, const char *second, char *buf, int maxlen); int NI_ip_range_to_prefix_ipv6(n128_t *begin, n128_t *end, int version, char **prefixes, int *pcount); int NI_ip_range_to_prefix_ipv4(unsigned long begin, unsigned long end, int version, char **prefixes, int *pcount); int NI_ip_range_to_prefix(const char *bitstr_1, const char *bitstr_2, int version, char **prefixes, int *pcount); int NI_ip_aggregate_tail(int res, char **prefixes, int pcount, int version, char *buf); int NI_ip_aggregate_ipv6(n128_t *b1, n128_t *e1, n128_t *b2, n128_t *e2, int ipversion, char *buf); int NI_ip_aggregate_ipv4(unsigned long b1, unsigned long e1, unsigned long b2, unsigned long e2, int ipversion, char *buf); int NI_ip_aggregate(const char *b1, const char *e1, const char *b2, const char *e2, int ipversion, char *buf); int NI_ip_iptobin(const char *ip, int ipversion, char *buf); int NI_ip_expand_address_ipv6(const char *ip, char *retbuf); int NI_ip_expand_address_ipv4(const char *ip, char *buf); int NI_ip_expand_address(const char *ip, int version, char *buf); int NI_ip_reverse_ipv6(const char *ip, int len, char *buf); int NI_ip_reverse_ipv4(const char *ip, int len, char *buf); int NI_ip_reverse(const char *ip, int len, int ipversion, char *buf); int NI_ip_normalize_prefix_ipv6(n128_t *ip, char *slash, char *ip1buf, char *ip2buf); int NI_ip_normalize_prefix_ipv4(unsigned long ip, char *slash, char *ip1buf, char *ip2buf); int NI_ip_normalize_prefix(char *ip, char *ip1buf, char *ip2buf); int NI_ip_tokenize_on_char(char *str, char separator, char **end_first, char **second); int NI_ip_normalize_plus_ipv6(char *ip, char *num, char *ipbuf1, char *ipbuf2); int NI_ip_normalize_plus_ipv4(char *ip, char *num, char *ipbuf1, char *ipbuf2); int NI_ip_normalize_range(char *ip, char *ipbuf1, char *ipbuf2); int NI_ip_normalize_plus(char *ip1, char *ipbuf1, char *ipbuf2); int NI_ip_normalize_bare(char *ip, char *ipbuf1); int NI_ip_normalize(char *ip, char *ipbuf1, char *ipbuf2); int NI_ip_normal_range(char *ip, char *buf); int NI_ip_compress_v4_prefix(const char *ip, int len, char *buf, int maxlen); int NI_ip_compress_address(const char *ip, int version, char *buf); int NI_ip_splitprefix(const char *prefix, char *ipbuf, int *lenbuf); int NI_ip_iptype(const char *ip, int version, char *buf); int NI_ip_is_valid_mask(const char *mask, int version); int NI_ip_prefix_to_range(const char *ip, int len, int version, char *buf); int NI_ip_get_embedded_ipv4(const char *ipv6, char *buf); #ifdef __cplusplus } #endif #endif Net-IP-XS-0.22/object.c0000644000175000017500000010164314363577245013160 0ustar tomhtomh/* object.c - Functions for Net::IP::XS's object-oriented interface. Copyright (C) 2010-2023 Tom Harrison Original inet_pton4, inet_pton6 are Copyright (C) 2006 Free Software Foundation. Original interface, and the auth and ip_auth functions, are Copyright (C) 1999-2002 RIPE NCC. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "functions.h" #define HV_PV_GET_OR_RETURN(name, object, str, len) \ name = NI_hv_get_pv(object, str, len); \ if (!name) { return 0; } #define HV_MY_DELETE(object, str, len) \ hv_delete((HV*) SvRV(object), str, len, G_DISCARD); #define HV_MY_STORE_IV(object, str, len, var) \ hv_store((HV*) SvRV(object), str, len, newSViv(var), 0); #define HV_MY_STORE_UV(object, str, len, var) \ hv_store((HV*) SvRV(object), str, len, newSVuv(var), 0); #define HV_MY_STORE_PV(object, str, len, var, varlen) \ hv_store((HV*) SvRV(object), str, len, newSVpv(var, varlen), 0); #ifdef __cplusplus extern "C" { #endif /** * NI_object_set_Error_Errno() - set object error number and string. * @ip: Net::IP::XS object. * @Errno: the new error number. * @Error: the new error string (can include printf modifiers). * @...: format arguments to substitute into @Error. */ void NI_object_set_Error_Errno(SV *ipo, int Errno, const char *Error, ...) { char errtmp[512]; va_list args; va_start(args, Error); vsnprintf(errtmp, 512, Error, args); errtmp[511] = '\0'; HV_MY_STORE_PV(ipo, "error", 5, errtmp, 0); HV_MY_STORE_IV(ipo, "errno", 5, Errno); va_end(args); } /** * NI_copy_Error_Errno() - copy global error details to object. * @ip: Net::IP::XS object. */ void NI_copy_Error_Errno(SV *ipo) { HV_MY_STORE_PV(ipo, "error", 5, NI_get_Error(), 0); HV_MY_STORE_IV(ipo, "errno", 5, NI_get_Errno()); } /** * NI_find_prefixes(): get prefix for Net::IP::XS object. * @ip: Net::IP::XS object. * @prefixes: prefix strings buffer. * @pcount: prefix count buffer. * * See NI_ip_range_to_prefix(). */ int NI_find_prefixes(SV *ipo, char **prefixes, int *pcount) { const char *binip; const char *last_bin; int ipversion; int res; HV_PV_GET_OR_RETURN(binip, ipo, "binip", 5); HV_PV_GET_OR_RETURN(last_bin, ipo, "last_bin", 8); ipversion = NI_hv_get_iv(ipo, "ipversion", 9); res = NI_ip_range_to_prefix(binip, last_bin, ipversion, prefixes, pcount); if (!res || !(*pcount)) { NI_copy_Error_Errno(ipo); return 0; } return 1; } /** * NI_set_ipv6_n128s(): set N128 integers in IPv6 Net::IP::XS object. * @ip: Net::IP::XS object. * * Relies on 'binip' and 'last_bin' being set in the object. */ int NI_set_ipv6_n128s(SV *ipo) { n128_t ipv6_begin; n128_t ipv6_end; const char *binbuf1; const char *binbuf2; SV *begin; SV *end; HV_PV_GET_OR_RETURN(binbuf1, ipo, "binip", 5); HV_PV_GET_OR_RETURN(binbuf2, ipo, "last_bin", 8); n128_set_str_binary(&ipv6_begin, binbuf1, 128); n128_set_str_binary(&ipv6_end, binbuf2, 128); /* Previously, this part of the code used malloc to allocate * n128_ts, which were then stored within the Net::IP::XS object. * This didn't work properly when threads were in use, because * those raw pointers were copied to each new thread, and * consequently freed by each thread in DESTROY. This now stores * the raw data as PVs instead. See * https://rt.cpan.org/Ticket/Display.html?id=102155 for more * information. */ begin = newSVpv((const char*) &ipv6_begin, 16); end = newSVpv((const char*) &ipv6_end, 16); hv_store((HV*) SvRV(ipo), "xs_v6_ip0", 9, begin, 0); hv_store((HV*) SvRV(ipo), "xs_v6_ip1", 9, end, 0); return 1; } /** * NI_set(): construct a new Net::IP::XS object. * @ip: Net::IP::XS object (can be initialised). * @version: IP address version. */ int NI_set(SV* ipo, char *data, int ipversion) { char buf1[MAX_IPV6_STR_LEN]; char buf2[MAX_IPV6_STR_LEN]; char binbuf1[IPV6_BITSTR_LEN]; char binbuf2[IPV6_BITSTR_LEN]; char maskbuf[IPV6_BITSTR_LEN]; char prefixbuf[MAX_IPV6_STR_LEN]; char *prefixes[MAX_PREFIXES]; char *binbuf2p; int res; int cmp_res; int num_addrs; int endipversion; int iplen; int pcount; int prefixlen; int i; buf1[0] = '\0'; buf2[0] = '\0'; binbuf1[0] = '\0'; binbuf2[0] = '\0'; maskbuf[0] = '\0'; num_addrs = NI_ip_normalize(data, buf1, buf2); if (!num_addrs) { NI_copy_Error_Errno(ipo); return 0; } HV_MY_DELETE(ipo, "ipversion", 9); HV_MY_DELETE(ipo, "prefixlen", 9); HV_MY_DELETE(ipo, "binmask", 7); HV_MY_DELETE(ipo, "reverse_ip", 10); HV_MY_DELETE(ipo, "last_ip", 7); HV_MY_DELETE(ipo, "iptype", 6); HV_MY_DELETE(ipo, "binip", 5); HV_MY_DELETE(ipo, "error", 5); HV_MY_DELETE(ipo, "ip", 2); HV_MY_DELETE(ipo, "intformat", 9); HV_MY_DELETE(ipo, "mask", 4); HV_MY_DELETE(ipo, "last_bin", 8); HV_MY_DELETE(ipo, "last_int", 8); HV_MY_DELETE(ipo, "prefix", 6); HV_MY_DELETE(ipo, "is_prefix", 9); if (!ipversion) { ipversion = strchr(buf1, '.') ? 4 : 6; } iplen = NI_iplengths(ipversion); if (!iplen) { return 0; } HV_MY_STORE_IV(ipo, "ipversion", 9, ipversion); HV_MY_STORE_PV(ipo, "ip", 2, buf1, 0); binbuf1[iplen] = '\0'; res = NI_ip_iptobin(buf1, ipversion, binbuf1); if (!res) { return 0; } HV_MY_STORE_PV(ipo, "binip", 5, binbuf1, iplen); HV_MY_STORE_IV(ipo, "is_prefix", 9, 0); if (num_addrs == 1) { HV_MY_STORE_PV(ipo, "last_ip", 7, buf1, 0); HV_MY_STORE_PV(ipo, "last_bin", 8, binbuf1, iplen); binbuf2p = binbuf1; } else { endipversion = strchr(buf2, '.') ? 4 : 6; if (!endipversion) { return 0; } if (endipversion != ipversion) { NI_set_Error_Errno(201, "Begin and End addresses have " "different IP versions - %s - %s", buf1, buf2); NI_copy_Error_Errno(ipo); return 0; } binbuf2[iplen] = '\0'; res = NI_ip_iptobin(buf2, ipversion, binbuf2); if (!res) { return 0; } HV_MY_STORE_PV(ipo, "last_ip", 7, buf2, 0); HV_MY_STORE_PV(ipo, "last_bin", 8, binbuf2, iplen); res = NI_ip_bincomp(binbuf1, "le", binbuf2, &cmp_res); if (!res) { return 0; } if (!cmp_res) { NI_set_Error_Errno(202, "Begin address is greater than End " "address %s - %s", buf1, buf2); NI_copy_Error_Errno(ipo); return 0; } binbuf2p = binbuf2; } pcount = 0; res = NI_find_prefixes(ipo, prefixes, &pcount); if (!res) { return 0; } if (pcount == 1) { char *prefix = prefixes[0]; res = NI_ip_splitprefix(prefix, prefixbuf, &prefixlen); if (!res) { free(prefix); return 0; } NI_ip_get_mask(prefixlen, ipversion, maskbuf); res = NI_ip_check_prefix(binbuf1, prefixlen, ipversion); if (!res) { free(prefix); NI_copy_Error_Errno(ipo); return 0; } HV_MY_STORE_IV(ipo, "prefixlen", 9, prefixlen); HV_MY_STORE_IV(ipo, "is_prefix", 9, 1); HV_MY_STORE_PV(ipo, "binmask", 7, maskbuf, iplen); } for (i = 0; i < pcount; i++) { free(prefixes[i]); } if (ipversion == 4) { HV_MY_STORE_UV(ipo, "xs_v4_ip0", 9, NI_bintoint(binbuf1, 32)); HV_MY_STORE_UV(ipo, "xs_v4_ip1", 9, NI_bintoint(binbuf2p, 32)); } else { res = NI_set_ipv6_n128s(ipo); if (!res) { return 0; } } return 1; } /** * NI_get_begin_n128(): get first address of IPv6 object as N128 integer. * @ip: Net::IP::XS object. * @begin: reference to N128 integer. * * On success, @begin will point to the beginning address stored in * the IPv6 object. */ int NI_get_begin_n128(SV *ipo, n128_t *begin) { SV **ref; STRLEN len; const char *raw_begin; ref = hv_fetch((HV*) SvRV(ipo), "xs_v6_ip0", 9, 0); if (!ref || !(*ref)) { return 0; } raw_begin = SvPV(*ref, len); memcpy(begin, raw_begin, 16); return 1; } /** * NI_get_end_n128(): get last address of IPv6 object as N128 integer. * @ip: Net::IP::XS object. * @end: reference to N128 integer. * * On success, @end will point to the ending address stored in the * IPv6 object. */ int NI_get_end_n128(SV *ipo, n128_t *end) { SV **ref; STRLEN len; const char *raw_end; ref = hv_fetch((HV*) SvRV(ipo), "xs_v6_ip1", 9, 0); if (!ref || !(*ref)) { return 0; } raw_end = SvPV(*ref, len); memcpy(end, raw_end, 16); return 1; } /** * NI_get_n128s(): get begin-end addresses of IPv6 object as N128 integers. * @ip: Net::IP::XS object. * @begin: reference to N128 integer. * @end: reference to N128 integer. * * See NI_get_begin_n128() and NI_get_end_n128(). */ int NI_get_n128s(SV *ipo, n128_t *begin, n128_t *end) { return NI_get_begin_n128(ipo, begin) && NI_get_end_n128(ipo, end); } /** * NI_short(): get the short format of the first IP address in the object. * @ip: Net::IP::XS object. * @buf: buffer for short format string. * * @buf will be null-terminated on success. */ int NI_short(SV *ipo, char *buf) { int version; int prefixlen; int res; const char *ipstr; version = NI_hv_get_iv(ipo, "ipversion", 9); ipstr = NI_hv_get_pv(ipo, "ip", 2); if (!ipstr) { ipstr = ""; } if (version == 6) { res = NI_ip_compress_address(ipstr, 6, buf); } else { prefixlen = NI_hv_get_iv(ipo, "prefixlen", 9); res = NI_ip_compress_v4_prefix(ipstr, prefixlen, buf, 40); } if (!res) { NI_copy_Error_Errno(ipo); return 0; } return 1; } /** * NI_last_ip(): get last IP address of a range as a string. * @ipo: Net::IP::XS object. * @buf: IP address buffer. * @maxlen: maximum capacity of buffer. */ int NI_last_ip(SV *ipo, char *buf, int maxlen) { const char *last_ip; const char *last_bin; int version; int res; if ((last_ip = NI_hv_get_pv(ipo, "last_ip", 7))) { snprintf(buf, maxlen, "%s", last_ip); return 1; } last_bin = NI_hv_get_pv(ipo, "last_bin", 8); if (!last_bin) { last_bin = ""; } version = NI_hv_get_iv(ipo, "ipversion", 9); res = NI_ip_bintoip(last_bin, version, buf); if (!res) { NI_copy_Error_Errno(ipo); return 0; } HV_MY_STORE_PV(ipo, "last_ip", 7, buf, 0); return 1; } /** * NI_print(): get the IP address/range in string format. * @ip: Net::IP::XS object. * @buf: buffer for the string. * * If the object represents a single prefix, the buffer will get the * short format of the first address (as per NI_short()), plus a '/', * plus the prefix length. Otherwise, it will get the first address, * plus " - ", plus the last address (neither in short (compressed) * format). */ int NI_print(SV *ipo, char *buf, int maxlen) { int is_prefix; int prefixlen; const char *first_ip; const char *second_ip; int res; char mybuf[MAX_IPV6_STR_LEN]; mybuf[0] = '\0'; is_prefix = NI_hv_get_iv(ipo, "is_prefix", 9); if (is_prefix) { res = NI_short(ipo, mybuf); if (!res) { return 0; } prefixlen = NI_hv_get_iv(ipo, "prefixlen", 9); snprintf(buf, maxlen, "%s/%d", mybuf, prefixlen); } else { first_ip = NI_hv_get_pv(ipo, "ip", 2); if (!first_ip) { return 0; } NI_last_ip(ipo, mybuf, MAX_IPV6_STR_LEN); second_ip = NI_hv_get_pv(ipo, "last_ip", 7); if (!second_ip) { return 0; } snprintf(buf, maxlen, "%s - %s", first_ip, second_ip); } return 1; } /** * NI_size_str_ipv4(): get size of IPv4 object as a string. * @ip: Net::IP::XS object. * @buf: size buffer. */ int NI_size_str_ipv4(SV *ipo, char *buf) { unsigned long begin; unsigned long end; begin = NI_hv_get_uv(ipo, "xs_v4_ip0", 9); end = NI_hv_get_uv(ipo, "xs_v4_ip1", 9); if ((begin == 0) && (end == 0xFFFFFFFF)) { sprintf(buf, "4294967296"); } else { sprintf(buf, "%lu", end - begin + 1); } return 1; } /** * NI_size_str_ipv6(): get size of IPv6 object as a string. * @ip: Net::IP::XS object. * @buf: size buffer. */ int NI_size_str_ipv6(SV *ipo, char *buf) { n128_t begin; n128_t end; int res; res = NI_get_n128s(ipo, &begin, &end); if (!res) { return 0; } if ( n128_scan1(&begin) == INT_MAX && n128_scan0(&end) == INT_MAX) { sprintf(buf, "340282366920938463463374607431768211456"); return 1; } n128_sub(&end, &begin); n128_add_ui(&end, 1); n128_print_dec(&end, buf); return 1; } /** * NI_size_str(): get size of Net::IP::XS object as a string. * @ip: Net::IP::XS object. * @buf: size buffer. * * See NI_size_str_ipv4() and NI_size_str_ipv6(). */ int NI_size_str(SV *ipo, char *size) { switch (NI_hv_get_iv(ipo, "ipversion", 9)) { case 4: return NI_size_str_ipv4(ipo, size); case 6: return NI_size_str_ipv6(ipo, size); default: return 0; } } /** * NI_intip_str_ipv4(): get first IP address as an integer string. * @ip: Net::IP::XS object. * @buf: integer string buffer. */ int NI_intip_str_ipv4(SV *ipo, char *buf) { sprintf(buf, "%lu", (unsigned long) NI_hv_get_uv(ipo, "xs_v4_ip0", 9)); return 1; } /** * NI_intip_str_ipv6(): get first IP address as an integer string. * @ip: Net::IP::XS object. * @buf: integer string buffer. */ int NI_intip_str_ipv6(SV *ipo, char *buf) { n128_t begin; if (!NI_get_begin_n128(ipo, &begin)) { return 0; } n128_print_dec(&begin, buf); return 1; } /** * NI_intip_str(): get first IP address as an integer string. * @ip: Net::IP::XS object. * @buf: integer string buffer. * @maxlen: maximum capacity of buffer. */ int NI_intip_str(SV *ipo, char *buf, int maxlen) { const char *intformat; int res; if ((intformat = NI_hv_get_pv(ipo, "intformat", 9))) { snprintf(buf, maxlen, "%s", intformat); return 1; } switch (NI_hv_get_iv(ipo, "ipversion", 9)) { case 4: res = NI_intip_str_ipv4(ipo, buf); break; case 6: res = NI_intip_str_ipv6(ipo, buf); break; default: res = 0; } if (res) { HV_MY_STORE_PV(ipo, "intformat", 9, buf, strlen(buf)); } return res; } /** * NI_hexip_ipv4(): get first IP address as a hex string. * @ip: Net::IP::XS object. * @buf: hex string buffer. * * The string has '0x' prefixed to it. */ int NI_hexip_ipv4(SV *ipo, char *buf) { sprintf(buf, "0x%lx", (unsigned long) NI_hv_get_uv(ipo, "xs_v4_ip0", 9)); return 1; } /** * NI_hexip_ipv6(): get first IP address as a hex string. * @ip: Net::IP::XS object. * @buf: hex string buffer. * * The string has '0x' prefixed to it. */ int NI_hexip_ipv6(SV *ipo, char *hexip) { n128_t begin; if (!NI_get_begin_n128(ipo, &begin)) { return 0; } n128_print_hex(&begin, hexip); return 1; } /** * NI_hexip(): get first IP address as a hex string. * @ip: Net::IP::XS object. * @buf: hex string buffer. * @maxlen: maximum capacity of buffer. * * See NI_hexip_ipv4() and NI_hexip_ipv6(). */ int NI_hexip(SV *ipo, char *buf, int maxlen) { const char *hexformat; int res; if ((hexformat = NI_hv_get_pv(ipo, "hexformat", 9))) { snprintf(buf, maxlen, "%s", hexformat); return 1; } switch (NI_hv_get_iv(ipo, "ipversion", 9)) { case 4: res = NI_hexip_ipv4(ipo, buf); break; case 6: res = NI_hexip_ipv6(ipo, buf); break; default: res = 0; } if (res) { HV_MY_STORE_PV(ipo, "hexformat", 9, buf, strlen(buf)); } return res; } /** * NI_hexmask(): return network mask as a hex string. * @ip: Net::IP::XS object. * @buf: hex string buffer. * @maxlen: maximum capacity of buffer. */ int NI_hexmask(SV *ipo, char *buf, int maxlen) { const char *binmask; const char *hexmask; n128_t dec; if ((hexmask = NI_hv_get_pv(ipo, "hexmask", 7))) { snprintf(buf, maxlen, "%s", hexmask); return 1; } /* Net::IP continues with the ip_bintoint call regardless of * whether binmask is defined, but that won't produce reasonable * output anyway, so will return undef instead. */ HV_PV_GET_OR_RETURN(binmask, ipo, "binmask", 7); n128_set_str_binary(&dec, binmask, strlen(binmask)); n128_print_hex(&dec, buf); HV_MY_STORE_PV(ipo, "hexmask", 7, buf, strlen(buf)); return 1; } /** * NI_prefix(): return range in prefix format. * @ipo: Net::IP::XS object. * @buf: prefix buffer. * @maxlen: maximum capacity of buffer. * * Sets Error and Errno in the object if the object does not represent * a single prefix. */ int NI_prefix(SV *ipo, char *buf, int maxlen) { const char *ip; const char *prefix; int is_prefix; int prefixlen; ip = NI_hv_get_pv(ipo, "ip", 2); if (!ip) { ip = ""; } is_prefix = NI_hv_get_iv(ipo, "is_prefix", 9); if (!is_prefix) { NI_object_set_Error_Errno(ipo, 209, "IP range %s is not a Prefix.", ip); return 0; } if ((prefix = NI_hv_get_pv(ipo, "prefix", 6))) { snprintf(buf, maxlen, "%s", prefix); return 1; } prefixlen = NI_hv_get_iv(ipo, "prefixlen", 9); if (prefixlen == -1) { return 0; } snprintf(buf, maxlen, "%s/%d", ip, prefixlen); HV_MY_STORE_PV(ipo, "prefix", 6, buf, 0); return 1; } /** * NI_mask(): return the IP address mask in IP address format. * @ipo: Net::IP::XS object. * @buf: mask buffer. * @maxlen: maximum capacity of buffer. */ int NI_mask(SV *ipo, char *buf, int maxlen) { const char *mask; const char *binmask; const char *ip; int is_prefix; int version; int res; is_prefix = NI_hv_get_iv(ipo, "is_prefix", 9); if (!is_prefix) { ip = NI_hv_get_pv(ipo, "ip", 2); if (!ip) { ip = ""; } NI_object_set_Error_Errno(ipo, 209, "IP range %s is not a Prefix.", ip); return 0; } if ((mask = NI_hv_get_pv(ipo, "mask", 4))) { snprintf(buf, maxlen, "%s", mask); return 1; } binmask = NI_hv_get_pv(ipo, "binmask", 7); if (!binmask) { binmask = ""; } version = NI_hv_get_iv(ipo, "ipversion", 9); res = NI_ip_bintoip(binmask, version, buf); if (!res) { NI_copy_Error_Errno(ipo); return 0; } HV_MY_STORE_PV(ipo, "mask", 4, buf, 0); return 1; } /** * NI_iptype(): get the type of the first IP address in the object. * @ipo: Net::IP::XS object. * @buf: type buffer. * @maxlen: maximum capacity of buffer. * * See NI_ip_iptype(). */ int NI_iptype(SV *ipo, char *buf, int maxlen) { const char *binip; const char *iptype; int version; int res; if ((iptype = NI_hv_get_pv(ipo, "iptype", 6))) { snprintf(buf, maxlen, "%s", iptype); return 1; } binip = NI_hv_get_pv(ipo, "binip", 5); if (!binip) { binip = ""; } version = NI_hv_get_iv(ipo, "ipversion", 9); res = NI_ip_iptype(binip, version, buf); if (!res) { NI_copy_Error_Errno(ipo); return 0; } HV_MY_STORE_PV(ipo, "iptype", 6, buf, 0); return 1; } /** * NI_reverse_ip(): get reverse domain for the first address of an object. * @ipo: Net::IP::XS object. * @buf: reverse domain buffer. * * See NI_ip_reverse(). */ int NI_reverse_ip(SV *ipo, char *buf) { const char *ip; int prefixlen; int version; int res; ip = NI_hv_get_pv(ipo, "ip", 2); if (!ip) { ip = ""; } if (!NI_hv_get_iv(ipo, "is_prefix", 9)) { NI_object_set_Error_Errno(ipo, 209, "IP range %s is not a Prefix.", ip); return 0; } prefixlen = NI_hv_get_iv(ipo, "prefixlen", 9); version = NI_hv_get_iv(ipo, "ipversion", 9); res = NI_ip_reverse(ip, prefixlen, version, buf); if (!res) { NI_copy_Error_Errno(ipo); return 0; } return 1; } /** * NI_last_bin(): get the last IP address of a range as a bitstring. * @ipo: Net::IP::XS object. * @buf: bitstring buffer. * @maxlen: maximum capacity of buffer. */ int NI_last_bin(SV *ipo, char *buf, int maxlen) { const char *last_bin; const char *binip; const char *last_ip; int version; int is_prefix; int prefixlen; int res; if ((last_bin = NI_hv_get_pv(ipo, "last_bin", 8))) { snprintf(buf, maxlen, "%s", last_bin); return 1; } is_prefix = NI_hv_get_iv(ipo, "is_prefix", 9); version = NI_hv_get_iv(ipo, "ipversion", 9); if (is_prefix) { binip = NI_hv_get_pv(ipo, "binip", 5); if (!binip) { return 0; } prefixlen = NI_hv_get_iv(ipo, "prefixlen", 9); res = NI_ip_last_address_bin(binip, prefixlen, version, buf); } else { last_ip = NI_hv_get_pv(ipo, "last_ip", 7); if (!last_ip) { return 0; } res = NI_ip_iptobin(last_ip, version, buf); } if (!res) { NI_copy_Error_Errno(ipo); return 0; } buf[NI_iplengths(version)] = '\0'; HV_MY_STORE_PV(ipo, "last_bin", 8, buf, 0); return 1; } /** * NI_last_int_str_ipv4(): get last IP address of a range as an integer string. * @ipo: Net::IP::XS object. * @buf: integer string buffer. */ int NI_last_int_str_ipv4(SV *ipo, char *buf) { unsigned long end; end = NI_hv_get_uv(ipo, "xs_v4_ip1", 9); sprintf(buf, "%lu", end); return 1; } /** * NI_last_int_str_ipv6(): get last IP address of a range as an integer string. * @ipo: Net::IP::XS object. * @buf: integer string buffer. */ int NI_last_int_str_ipv6(SV *ipo, char *buf) { n128_t end; if (!NI_get_end_n128(ipo, &end)) { return 0; } n128_print_dec(&end, buf); return 1; } /** * NI_last_int_str(): get last IP address of a range as an integer string. * @ipo: Net::IP::XS object. * @buf: integer string buffer. * @maxlen: maximum capacity of buffer. */ int NI_last_int_str(SV *ipo, char *buf, int maxlen) { const char *last_int; int res; if ((last_int = NI_hv_get_pv(ipo, "last_int", 8))) { snprintf(buf, maxlen, "%s", last_int); return 1; } switch (NI_hv_get_iv(ipo, "ipversion", 9)) { case 4: res = NI_last_int_str_ipv4(ipo, buf); break; case 6: res = NI_last_int_str_ipv6(ipo, buf); break; default: res = 0; } if (res) { HV_MY_STORE_PV(ipo, "last_int", 8, buf, 0); } return res; } /** * NI_bincomp(): compare first IP addresses of two ranges. * @ipo1: first Net::IP::XS object. * @op: the comparator as a string. * @ipo2: second Net::IP::XS object. * @buf: result buffer. * * See NI_ip_bincomp(). */ int NI_bincomp(SV *ipo1, const char *op, SV *ipo2, int *resbuf) { const char *binip1; const char *binip2; int res; binip1 = NI_hv_get_pv(ipo1, "binip", 5); if (!binip1) { binip1 = ""; } binip2 = NI_hv_get_pv(ipo2, "binip", 5); if (!binip2) { binip2 = ""; } res = NI_ip_bincomp(binip1, op, binip2, resbuf); if (!res) { NI_copy_Error_Errno(ipo1); return 0; } return 1; } /** * NI_binadd(): get new object from the sum of two IP addresses. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. */ SV * NI_binadd(SV *ipo1, SV *ipo2) { const char *binip1; const char *binip2; int version; char binbuf[130]; char buf[45]; int res; HV *stash; HV *hash; SV *ref; int iplen; binip1 = NI_hv_get_pv(ipo1, "binip", 5); if (!binip1) { binip1 = ""; } binip2 = NI_hv_get_pv(ipo2, "binip", 5); if (!binip2) { binip2 = ""; } res = NI_ip_binadd(binip1, binip2, binbuf, IPV6_BITSTR_LEN); if (!res) { NI_copy_Error_Errno(ipo1); return NULL; } version = NI_hv_get_iv(ipo1, "ipversion", 9); iplen = NI_iplengths(version); binbuf[iplen] = '\0'; buf[0] = '\0'; res = NI_ip_bintoip(binbuf, version, buf); if (!res) { return NULL; } hash = newHV(); ref = newRV_noinc((SV*) hash); stash = gv_stashpv("Net::IP::XS", 1); sv_bless(ref, stash); res = NI_set(ref, buf, version); if (!res) { return NULL; } return ref; } /** * NI_aggregate_ipv4(): aggregate two IP address ranges into new object. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. */ int NI_aggregate_ipv4(SV *ipo1, SV *ipo2, char *buf) { unsigned long b1; unsigned long b2; unsigned long e1; unsigned long e2; const char *ip1; const char *ip2; int res; b1 = NI_hv_get_uv(ipo1, "xs_v4_ip0", 9); e1 = NI_hv_get_uv(ipo1, "xs_v4_ip1", 9); b2 = NI_hv_get_uv(ipo2, "xs_v4_ip0", 9); e2 = NI_hv_get_uv(ipo2, "xs_v4_ip1", 9); res = NI_ip_aggregate_ipv4(b1, e1, b2, e2, 4, buf); if (res == 0) { NI_copy_Error_Errno(ipo1); return 0; } if (res == 160) { ip1 = NI_hv_get_pv(ipo1, "last_ip", 7); if (!ip1) { ip1 = ""; } ip2 = NI_hv_get_pv(ipo2, "ip", 2); if (!ip2) { ip2 = ""; } NI_set_Error_Errno(160, "Ranges not contiguous - %s - %s", ip1, ip2); NI_copy_Error_Errno(ipo1); return 0; } if (res == 161) { ip1 = NI_hv_get_pv(ipo1, "ip", 7); if (!ip1) { ip1 = ""; } ip2 = NI_hv_get_pv(ipo2, "last_ip", 2); if (!ip2) { ip2 = ""; } NI_set_Error_Errno(161, "%s - %s is not a single prefix", ip1, ip2); NI_copy_Error_Errno(ipo1); return 0; } return 1; } /** * NI_aggregate_ipv6(): aggregate two IP address ranges into new object. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. */ int NI_aggregate_ipv6(SV *ipo1, SV *ipo2, char *buf) { n128_t b1; n128_t e1; n128_t b2; n128_t e2; int res; const char *ip1; const char *ip2; if (!NI_get_n128s(ipo1, &b1, &e1)) { return 0; } if (!NI_get_n128s(ipo2, &b2, &e2)) { return 0; } res = NI_ip_aggregate_ipv6(&b1, &e1, &b2, &e2, 6, buf); if (res == 0) { NI_copy_Error_Errno(ipo1); return 0; } if (res == 160) { ip1 = NI_hv_get_pv(ipo1, "last_ip", 7); if (!ip1) { ip1 = ""; } ip2 = NI_hv_get_pv(ipo2, "ip", 2); if (!ip2) { ip2 = ""; } NI_set_Error_Errno(160, "Ranges not contiguous - %s - %s", ip1, ip2); NI_copy_Error_Errno(ipo1); return 0; } if (res == 161) { ip1 = NI_hv_get_pv(ipo1, "ip", 7); if (!ip1) { ip1 = ""; } ip2 = NI_hv_get_pv(ipo2, "last_ip", 2); if (!ip2) { ip2 = ""; } NI_set_Error_Errno(161, "%s - %s is not a single prefix", ip1, ip2); NI_copy_Error_Errno(ipo1); return 0; } return res; } /** * NI_aggregate(): aggregate two IP address ranges into new object. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. */ SV * NI_aggregate(SV *ipo1, SV *ipo2) { int version; int res; char buf[90]; HV *stash; HV *hash; SV *ref; switch ((version = NI_hv_get_iv(ipo1, "ipversion", 9))) { case 4: res = NI_aggregate_ipv4(ipo1, ipo2, buf); break; case 6: res = NI_aggregate_ipv6(ipo1, ipo2, buf); break; default: res = 0; } if (!res) { return NULL; } hash = newHV(); ref = newRV_noinc((SV*) hash); stash = gv_stashpv("Net::IP::XS", 1); sv_bless(ref, stash); res = NI_set(ref, buf, version); if (!res) { return NULL; } return ref; } /** * NI_overlaps_ipv4(): check if two address ranges overlap. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. * @buf: result buffer. */ int NI_overlaps_ipv4(SV *ipo1, SV *ipo2, int *buf) { unsigned long b1; unsigned long b2; unsigned long e1; unsigned long e2; b1 = NI_hv_get_uv(ipo1, "xs_v4_ip0", 9); e1 = NI_hv_get_uv(ipo1, "xs_v4_ip1", 9); b2 = NI_hv_get_uv(ipo2, "xs_v4_ip0", 9); e2 = NI_hv_get_uv(ipo2, "xs_v4_ip1", 9); NI_ip_is_overlap_ipv4(b1, e1, b2, e2, buf); return 1; } /** * NI_overlaps_ipv6(): check if two address ranges overlap. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. * @buf: result buffer. */ int NI_overlaps_ipv6(SV *ipo1, SV *ipo2, int *buf) { n128_t b1; n128_t e1; n128_t b2; n128_t e2; if (!NI_get_n128s(ipo1, &b1, &e1)) { return 0; } if (!NI_get_n128s(ipo2, &b2, &e2)) { return 0; } NI_ip_is_overlap_ipv6(&b1, &e1, &b2, &e2, buf); return 1; } /** * NI_overlaps(): check if two address ranges overlap. * @ipo1: first Net::IP::XS object. * @ipo2: second Net::IP::XS object. * @buf: result buffer. * * See NI_ip_is_overlap(). */ int NI_overlaps(SV *ipo1, SV* ipo2, int *buf) { switch (NI_hv_get_iv(ipo1, "ipversion", 9)) { case 4: return NI_overlaps_ipv4(ipo1, ipo2, buf); case 6: return NI_overlaps_ipv6(ipo1, ipo2, buf); default: return 0; } } /** * NI_ip_add_num_ipv4(): add integer to object, get new range as string. * @ipo: Net::IP::XS object. * @num: integer to add to object. * @buf: range buffer. */ int NI_ip_add_num_ipv4(SV *ipo, unsigned long num, char *buf) { unsigned long begin; unsigned long end; int len; begin = NI_hv_get_uv(ipo, "xs_v4_ip0", 9); end = NI_hv_get_uv(ipo, "xs_v4_ip1", 9); if ((0xFFFFFFFF - num) < begin) { return 0; } if ((begin + num) > end) { return 0; } begin += num; NI_ip_inttoip_ipv4(begin, buf); len = strlen(buf); sprintf(buf + len, " - "); NI_ip_inttoip_ipv4(end, buf + len + 3); return 1; } /** * NI_ip_add_num_ipv6(): add integer to object, get new range as string. * @ipo: Net::IP::XS object. * @num: integer to add to object. * @buf: range buffer. */ int NI_ip_add_num_ipv6(SV *ipo, n128_t *num, char *buf) { n128_t begin; n128_t end; int len; int res; if (!NI_get_n128s(ipo, &begin, &end)) { return 0; } res = n128_add(num, &begin); if (!res) { return 0; } if ( (n128_scan1(num) == INT_MAX) || (n128_cmp(num, &begin) <= 0) || (n128_cmp(num, &end) > 0)) { return 0; } NI_ip_inttoip_n128(num, buf); len = strlen(buf); sprintf(buf + len, " - "); NI_ip_inttoip_n128(&end, buf + len + 3); return 1; } /** * NI_ip_add_num(): add integer to object and get new object. * @ipo: Net::IP::XS object. * @num: integer to add to object (as a string). */ SV * NI_ip_add_num(SV *ipo, const char *num) { int version; unsigned long num_ulong; char *endptr; n128_t num_n128; char buf[(2 * (MAX_IPV6_STR_LEN - 1)) + 4]; int res; HV *stash; HV *hash; SV *ref; int size; version = NI_hv_get_iv(ipo, "ipversion", 9); if (version == 4) { endptr = NULL; num_ulong = strtoul(num, &endptr, 10); if (STRTOUL_FAILED(num_ulong, num, endptr)) { return 0; } if (num_ulong > 0xFFFFFFFF) { return 0; } res = NI_ip_add_num_ipv4(ipo, num_ulong, buf); if (!res) { return 0; } } else if (version == 6) { res = n128_set_str_decimal(&num_n128, num, strlen(num)); if (!res) { return 0; } res = NI_ip_add_num_ipv6(ipo, &num_n128, buf); if (!res) { return 0; } } else { return 0; } hash = newHV(); ref = newRV_noinc((SV*) hash); stash = gv_stashpv("Net::IP::XS", 1); sv_bless(ref, stash); res = NI_set(ref, buf, version); if (!res) { return NULL; } return ref; } #ifdef __cplusplus } #endif Net-IP-XS-0.22/MANIFEST0000644000175000017500000000216714363577433012677 0ustar tomhtomhChanges functions.c functions.h inet_pton.c inet_pton.h n128.c n128.h lib/Net/IP/XS.pm LICENSE Makefile.PL MANIFEST NetIpXs.xs object.c object.h README t/00-load.t t/01-pod.t t/10-basic.t t/10-n128.t t/11-iptobin.t t/12-iplengths.t t/13-bintoip.t t/14-bintoint.t t/15-inttobin.t t/16-is-ipv4.t t/17-is-ipv6.t t/18-get-version.t t/19-get-mask.t t/20-last-address.t t/21-split-prefix.t t/22-is-valid-mask.t t/23-expand-address.t t/24-bincomp.t t/25-binadd.t t/26-get-prefix-length.t t/27-compress-v4.t t/28-overlap.t t/29-check-prefix.t t/30-range-to-prefix.t t/31-get-embedded-ipv4.t t/32-aggregate.t t/33-prefix-to-range.t t/34-reverse.t t/35-normalize.t t/36-normal-range.t t/37-compress-v6.t t/38-type.t t/39-auth.t t/40-objects.t t/41-serialise.t t/42-tags.t t/43-ip-add-num-v4.t t/44-ip-add-num-v6.t t/45-short.t t/46-wrong-object.t t/47-object-size.t t/48-rt-73232.t t/49-rt-92211.t t/50-rt-102155.t t/51-rt-102155-2.t t/52-rt-118605.t t/53-rt-118593.t t/54-rt-122438.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker)