py-radix-0.5004075500017500001750000000000001073166317200112145ustar00djmdjmpy-radix-0.5/ChangeLog010064400017500001750000000064211073163243500130430ustar00djmdjm20071218 - (djm) Implement pickle protocol via __reduce__ and __setstate__, inspired by wrapper from Erik Curiel (ecuriel AT kefta.com) - (djm) Crank version number to 0.5 - (djm) Make this compile on Python 2.4, which lacks Py_ssize_t 20071010 - (djm) fix for Windows platforms: WSAStartup was not called, resulting in failures inside getaddrinfo. Report from harpreet.sawhney AT gmail.com 20060808 - (djm) Support for Win32 (AF_INET6 is still broken though) 20060628 - (djm) Add RPM spec file - (djm) Hygene fixes: be more strict with types, use Python memory allocators instead of libc malloc/free 20050425 - (djm) Fix bug that could corrupt some prefixes; reported by siganos AT cs.ucr.edu - (djm) Delete semicolons from EOL in test.py - (djm) Release py-radix-0.4 20041123 - (djm) Use RFC3849 IPv6 prefixes in documentation - (djm) Some stupid platforms still lack strl*, so avoid their use - (djm) Allow mixing of IPv4 and IPv6 prefixes in the same tree by keeping separate trees in the RadixObject for each AF - (djm) Release beta version: py-radix-0.3 20041122 - (djm) Raise a RuntimeWarning and abort iteration if the tree changes while we are iterating over it - (djm) Add new regress tests: nodes() on an empty tree, iterate on an empty tree, modify tree while iterating, add/delete/check lots of prefixes - (djm) order and tidy regress test function names - (djm) Don't need to use a callback to do Radix.nodes(), so don't use one - (djm) Avoid callback for tree deletes too - (djm) GC unusued args to newRadixNodeObject - (djm) KNF and GC radix.h - (djm) unifdef radix.c - (djm) KNF radix.c (more remains) - (djm) Kill asserts() in radix.c, more KNF (starting to be readable now) - (djm) Implement Radix.prefixes() returning a list of prefix strings - (djm) Allow Radix methods to accept separate prefix lengths. E.g. method("xx.xx.xx.xx", yy) - (djm) Support keyword arguments for Radix methods. E.g. tree.add(network="127.0.0.1", masklen=32) - (djm) scrub off address bits past masklen, E.g. so tree.add("10.0.0.255/24") creates an idential object to tree.add("10.0.0.0/24") - (djm) Regress tests for bad addresses and insane mask lengths - (djm) Support for binary packed addresses in Radix methods and a RadixNode member 20041121 - (djm) Fix a bug: forgot to set the exception type when trying to get RadixNode members that don't exist - (djm) Prepare for Radix.method(address, masklen) by extending radix API - (djm) Add regression tests (not yet integrated with build) - (djm) Always allocate user objects in RadixNode - (djm) API change: don't allow creation of arbirtary members in RadixNode. Instead, provide a data dict for storage of user-specified state. This avoid all of the introspection breakage of providing our get/setattr functions - (djm) Add RadixNode docstring - (djm) Add iterator support - (djm) Flesh out setup.py and add correct URL - (djm) Raise an exception when trying to mix V4 and V6 addresses in a single tree (rather than continuing and returning confusing results). Hopefully this will be fixed in the future - (djm) Release alpha version: py-radix-0.2 20041118 - (djm) Start the changelog - (djm) Release alpha version: py-radix-0.1 $Id: ChangeLog,v 1.39 2007/12/18 02:49:01 djm Exp $ py-radix-0.5/LICENSE010064400017500001750000000124001046577534300123020ustar00djmdjmThe Python binding code is subject to this license: /* * Copyright (c) 2004 Damien Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. */ This underlying radix tree code is derived from MRT (http://www.mrtd.net/) and is subject to the following license: /* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. */ Windows platforms link in code subject the the following licenses: /* Copyright (c) 1996 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. */ /* * Copyright (c) 1998 Todd C. Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. */ py-radix-0.5/MANIFEST010064400017500001750000000001171014702615700124150ustar00djmdjmChangeLog LICENSE PKG-INFO README TODO radix.c radix.h radix_python.c setup.py py-radix-0.5/PKG-INFO010064400017500001750000000016441073161504100123620ustar00djmdjmMetadata-Version: 1.0 Name: py-radix Version: 0.5 Summary: Radix tree for storage and retrieval of network prefixes Home-page: http://www.mindrot.org/py-radix.html Author: Damien Miller Author-email: djm@mindrot.org License: BSD Download-URL: http://www2.mindrot.org/files/py-radix/ Description: py-radix is an implementation of a radix tree data structure for the storage and retrieval of IPv4 and IPv6 network prefixes. The radix tree is the data structure most commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. Keywords: radix,tree,python,routing Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Natural Language :: English Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules py-radix-0.5/README010064400017500001750000000063361015117143200121450ustar00djmdjmpy-radix is an implementation of a radix tree data structure for the storage and retrieval of IPv4 and IPv6 network prefixes. The radix tree is the data structure most commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. To install, use the standard Python distutils incantation: python setup.py build python setup.py install Regression tests are in the test.py file. py-radix is licensed under a ISC/BSD licence. The underlying radix tree implementation is taken (and modified) from MRTd and is subject to a 4-term BSD license. See the LICENSE file for details. Please report bugs to Damien Miller . Please check the TODO file first, in case your problem is something I already know about (please send patches!) A simple example that demonstrates most of the features: import radix # Create a new tree rtree = radix.Radix() # Adding a node returns a RadixNode object. You can create # arbitrary members in its 'data' dict to store your data rnode = rtree.add("10.0.0.0/8") rnode.data["blah"] = "whatever you want" # You can specify nodes as CIDR addresses, or networks with # separate mask lengths. The following three invocations are # identical: rnode = rtree.add("10.0.0.0/16") rnode = rtree.add("10.0.0.0", 16) rnode = rtree.add(network = "10.0.0.0", masklen = 16) # It is also possible to specify nodes using binary packed # addresses, such as those returned by the socket module # functions. In this case, the radix module will assume that # a four-byte address is an IPv4 address and a sixteen-byte # address is an IPv6 address. For example: binary_addr = inet_ntoa("172.18.22.0") rnode = rtree.add(packed = binary_addr, masklen = 23) # Exact search will only return prefixes you have entered # You can use all of the above ways to specify the address rnode = rtree.search_exact("10.0.0.0/8") # Get your data back out print rnode.data["blah"] # Use a packed address addr = socket.inet_ntoa("10.0.0.0") rnode = rtree.search_exact(packed = addr, masklen = 8) # Best-match search will return the longest matching prefix # that contains the search term (routing-style lookup) rnode = rtree.search_best("10.123.45.6") # There are a couple of implicit members of a RadixNode: print rnode.network # -> "10.0.0.0" print rnode.prefix # -> "10.0.0.0/8" print rnode.prefixlen # -> 8 print rnode.family # -> socket.AF_INET print rnode.packed # -> '\n\x00\x00\x00' # IPv6 prefixes are fully supported in the same tree rnode = rtree.add("2001:DB8::/3") rnode = rtree.add("::/0") # Use the nodes() method to return all RadixNodes created nodes = rtree.nodes() for rnode in nodes: print rnode.prefix # The prefixes() method will return all the prefixes (as a # list of strings) that have been entered prefixes = rtree.prefixes() # You can also directly iterate over the tree itself # this would save some memory if the tree is big # NB. Don't modify the tree (add or delete nodes) while # iterating otherwise you will abort the iteration and # receive a RuntimeWarning. Changing a node's data dict # is permitted. for rnode in rtree: print rnode.prefix $Id: README,v 1.12 2004/11/24 20:46:18 djm Exp $ py-radix-0.5/TODO010064400017500001750000000011061073161504100117460ustar00djmdjmFix bug(?) on Win32: getaddrinfo doesn't seem to parse IPv6 addresses - Maybe Extend Radix methods to accept (network, mask) (contiguous masks only) - tree.delete(RadixNode) tree.copy() more KNF tree.search_all_containing(network) -> [ network, network ] (sorted by prefix) (use search_best2 internally) tree.search_containing(network) -> RadixNode or None Find the prefix containing 'network', not including an exact match. tree.num_prefixes() dict-like interface: tree[addr] = user_object "blur" prefix - reduce masklen $Id: TODO,v 1.24 2007/12/18 00:53:53 djm Exp $ py-radix-0.5/inet_ntop.c010064400017500001750000000122541046577534300134470ustar00djmdjm/* $OpenBSD: inet_ntop.c,v 1.7 2005/08/06 20:30:03 espie Exp $ */ /* Copyright (c) 1996 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 "radix.h" #include #include #include #include #ifndef EAFNOSUPPORT # define EAFNOSUPPORT EINVAL #endif #ifndef IN6ADDRSZ # define IN6ADDRSZ 16 #endif #ifndef INT16SZ # define INT16SZ 2 #endif /* * WARNING: Don't even consider trying to compile this on a system where * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. */ static const char *inet_ntop4(const u_char *src, char *dst, size_t size); static const char *inet_ntop6(const u_char *src, char *dst, size_t size); /* char * * inet_ntop(af, src, dst, size) * convert a network format address to presentation format. * return: * pointer to presentation format address (`dst'), or NULL (see errno). * author: * Paul Vixie, 1996. */ const char * inet_ntop(int af, const void *src, char *dst, size_t size) { switch (af) { case AF_INET: return (inet_ntop4(src, dst, size)); case AF_INET6: return (inet_ntop6(src, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); } /* NOTREACHED */ } /* const char * * inet_ntop4(src, dst, size) * format an IPv4 address, more or less like inet_ntoa() * return: * `dst' (as a const) * notes: * (1) uses no statics * (2) takes a u_char* not an in_addr as input * author: * Paul Vixie, 1996. */ static const char * inet_ntop4(const u_char *src, char *dst, size_t size) { static const char fmt[] = "%u.%u.%u.%u"; char tmp[sizeof "255.255.255.255"]; int l; l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]); if (l <= 0 || (unsigned)l >= size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; char *tp, *ep; struct { int base, len; } best, cur; u_int words[IN6ADDRSZ / INT16SZ]; int i; int advance; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; cur.base = -1; for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; ep = tmp + sizeof(tmp); for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } continue; } /* Are we following an initial run of 0x00s or any real hex? */ if (i != 0) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) return (NULL); tp += strlen(tp); break; } advance = snprintf(tp, ep - tp, "%x", words[i]); if (advance <= 0 || advance >= ep - tp) return (NULL); tp += advance; } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { if (tp + 1 >= ep) return (NULL); *tp++ = ':'; } if (tp + 1 >= ep) return (NULL); *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((size_t)(tp - tmp) > size) { errno = ENOSPC; return (NULL); } strlcpy(dst, tmp, size); return (dst); } py-radix-0.5/py-radix.spec010064400017500001750000000024521073161504100136740ustar00djmdjmName: py-radix Summary: Radix tree data structure for Python Version: 0.5 Release: 1 Source0: http://www2.mindrot.org/files/py-radix/py-radix-%{version}.tar.gz License: BSD Group: Development/Libraries BuildRoot: %{_tmppath}/%{name}-buildroot Requires: %{__python} BuildRequires: python-devel, gcc Url: http://www.mindrot.org/py-radix.html %description py-radix is an implementation of a radix tree for Python, which supports storage and lookups of IPv4 and IPv6 networks. The radix tree (a.k.a Patricia tree) is the data structure most commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. py-radix's implementation is built solely for networks (the data structure itself is more general). %prep %setup %build %{__python} setup.py build %install %{__python} setup.py install --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES sed -e 's|/[^/]*$||' INSTALLED_FILES | grep "site-packages/" | \ sort | uniq | awk '{ print "%attr(755,root,root) %dir " $1}' > INSTALLED_DIRS cat INSTALLED_FILES INSTALLED_DIRS > INSTALLED_OBJECTS %clean rm -rf $RPM_BUILD_ROOT %files -f INSTALLED_OBJECTS %defattr(-,root,root) %doc LICENSE README TODO ChangeLog %changelog * Wed Jun 28 2006 Damien Miller - Build RPM py-radix-0.5/radix.c010064400017500001750000000363071070756015700125560ustar00djmdjm/* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 "Python.h" #include #include #include #include #include "radix.h" /* $Id: radix.c,v 1.17 2007/10/24 06:04:31 djm Exp $ */ /* * Originally from MRT include/defs.h * $MRTId: defs.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ #define BIT_TEST(f, b) ((f) & (b)) /* * Originally from MRT include/mrt.h * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ #define prefix_tochar(prefix) ((char *)&(prefix)->add) #define prefix_touchar(prefix) ((u_char *)&(prefix)->add) /* * Originally from MRT lib/mrt/prefix.c * $MRTId: prefix.c,v 1.1.1.1 2000/08/14 18:46:11 labovit Exp $ */ static int comp_with_mask(u_char *addr, u_char *dest, u_int mask) { if (memcmp(addr, dest, mask / 8) == 0) { u_int n = mask / 8; u_int m = ((~0) << (8 - (mask % 8))); if (mask % 8 == 0 || (addr[n] & m) == (dest[n] & m)) return (1); } return (0); } static prefix_t *New_Prefix2(int family, void *dest, int bitlen, prefix_t *prefix) { int dynamic_allocated = 0; int default_bitlen = 32; if (family == AF_INET6) { default_bitlen = 128; if (prefix == NULL) { if ((prefix = PyMem_Malloc(sizeof(*prefix))) == NULL) return (NULL); memset(prefix, '\0', sizeof(*prefix)); dynamic_allocated++; } memcpy(&prefix->add.sin6, dest, 16); } else if (family == AF_INET) { if (prefix == NULL) { if ((prefix = PyMem_Malloc(sizeof(*prefix))) == NULL) return (NULL); memset(prefix, '\0', sizeof(*prefix)); dynamic_allocated++; } memcpy(&prefix->add.sin, dest, 4); } else return (NULL); prefix->bitlen = (bitlen >= 0) ? bitlen : default_bitlen; prefix->family = family; prefix->ref_count = 0; if (dynamic_allocated) prefix->ref_count++; return (prefix); } static prefix_t *Ref_Prefix(prefix_t *prefix) { if (prefix == NULL) return (NULL); if (prefix->ref_count == 0) { /* make a copy in case of a static prefix */ return (New_Prefix2(prefix->family, &prefix->add, prefix->bitlen, NULL)); } prefix->ref_count++; return (prefix); } void Deref_Prefix(prefix_t *prefix) { if (prefix == NULL) return; prefix->ref_count--; if (prefix->ref_count <= 0) { PyMem_Free(prefix); return; } } /* * Originally from MRT lib/radix/radix.c * $MRTId: radix.c,v 1.1.1.1 2000/08/14 18:46:13 labovit Exp $ */ /* these routines support continuous mask only */ radix_tree_t *New_Radix(void) { radix_tree_t *radix; if ((radix = PyMem_Malloc(sizeof(*radix))) == NULL) return (NULL); memset(radix, '\0', sizeof(*radix)); radix->maxbits = 128; radix->head = NULL; radix->num_active_node = 0; return (radix); } /* * if func is supplied, it will be called as func(node->data) * before deleting the node */ static void Clear_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { if (radix->head) { radix_node_t *Xstack[RADIX_MAXBITS + 1]; radix_node_t **Xsp = Xstack; radix_node_t *Xrn = radix->head; while (Xrn) { radix_node_t *l = Xrn->l; radix_node_t *r = Xrn->r; if (Xrn->prefix) { Deref_Prefix(Xrn->prefix); if (Xrn->data && func) func(Xrn, cbctx); } PyMem_Free(Xrn); radix->num_active_node--; if (l) { if (r) *Xsp++ = r; Xrn = l; } else if (r) { Xrn = r; } else if (Xsp != Xstack) { Xrn = *(--Xsp); } else { Xrn = (radix_node_t *) 0; } } } } void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { Clear_Radix(radix, func, cbctx); PyMem_Free(radix); } /* * if func is supplied, it will be called as func(node->prefix, node->data) */ void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx) { radix_node_t *node; RADIX_WALK(radix->head, node) { func(node, cbctx); } RADIX_WALK_END; } radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix) { radix_node_t *node; u_char *addr; u_int bitlen; if (radix->head == NULL) return (NULL); node = radix->head; addr = prefix_touchar(prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) node = node->r; else node = node->l; if (node == NULL) return (NULL); } if (node->bit > bitlen || node->prefix == NULL) return (NULL); if (comp_with_mask(prefix_tochar(node->prefix), prefix_tochar(prefix), bitlen)) return (node); return (NULL); } /* if inclusive != 0, "best" may be the given prefix itself */ static radix_node_t *radix_search_best2(radix_tree_t *radix, prefix_t *prefix, int inclusive) { radix_node_t *node; radix_node_t *stack[RADIX_MAXBITS + 1]; u_char *addr; u_int bitlen; int cnt = 0; if (radix->head == NULL) return (NULL); node = radix->head; addr = prefix_touchar(prefix); bitlen = prefix->bitlen; while (node->bit < bitlen) { if (node->prefix) stack[cnt++] = node; if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) node = node->r; else node = node->l; if (node == NULL) break; } if (inclusive && node && node->prefix) stack[cnt++] = node; if (cnt <= 0) return (NULL); while (--cnt >= 0) { node = stack[cnt]; if (comp_with_mask(prefix_tochar(node->prefix), prefix_tochar(prefix), node->prefix->bitlen)) return (node); } return (NULL); } radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix) { return (radix_search_best2(radix, prefix, 1)); } radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix) { radix_node_t *node, *new_node, *parent, *glue; u_char *addr, *test_addr; u_int bitlen, check_bit, differ_bit; u_int i, j, r; if (radix->head == NULL) { if ((node = PyMem_Malloc(sizeof(*node))) == NULL) return (NULL); memset(node, '\0', sizeof(*node)); node->bit = prefix->bitlen; node->prefix = Ref_Prefix(prefix); node->parent = NULL; node->l = node->r = NULL; node->data = NULL; radix->head = node; radix->num_active_node++; return (node); } addr = prefix_touchar(prefix); bitlen = prefix->bitlen; node = radix->head; while (node->bit < bitlen || node->prefix == NULL) { if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) { if (node->r == NULL) break; node = node->r; } else { if (node->l == NULL) break; node = node->l; } } test_addr = prefix_touchar(node->prefix); /* find the first bit different */ check_bit = (node->bit < bitlen) ? node->bit : bitlen; differ_bit = 0; for (i = 0; i * 8 < check_bit; i++) { if ((r = (addr[i] ^ test_addr[i])) == 0) { differ_bit = (i + 1) * 8; continue; } /* I know the better way, but for now */ for (j = 0; j < 8; j++) { if (BIT_TEST(r, (0x80 >> j))) break; } /* must be found */ differ_bit = i * 8 + j; break; } if (differ_bit > check_bit) differ_bit = check_bit; parent = node->parent; while (parent && parent->bit >= differ_bit) { node = parent; parent = node->parent; } if (differ_bit == bitlen && node->bit == bitlen) { if (node->prefix == NULL) node->prefix = Ref_Prefix(prefix); return (node); } if ((new_node = PyMem_Malloc(sizeof(*new_node))) == NULL) return (NULL); memset(new_node, '\0', sizeof(*new_node)); new_node->bit = prefix->bitlen; new_node->prefix = Ref_Prefix(prefix); new_node->parent = NULL; new_node->l = new_node->r = NULL; new_node->data = NULL; radix->num_active_node++; if (node->bit == differ_bit) { new_node->parent = node; if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) node->r = new_node; else node->l = new_node; return (new_node); } if (bitlen == differ_bit) { if (bitlen < radix->maxbits && BIT_TEST(test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07))) new_node->r = node; else new_node->l = node; new_node->parent = node->parent; if (node->parent == NULL) radix->head = new_node; else if (node->parent->r == node) node->parent->r = new_node; else node->parent->l = new_node; node->parent = new_node; } else { if ((glue = PyMem_Malloc(sizeof(*glue))) == NULL) return (NULL); memset(glue, '\0', sizeof(*glue)); glue->bit = differ_bit; glue->prefix = NULL; glue->parent = node->parent; glue->data = NULL; radix->num_active_node++; if (differ_bit < radix->maxbits && BIT_TEST(addr[differ_bit >> 3], 0x80 >> (differ_bit & 0x07))) { glue->r = new_node; glue->l = node; } else { glue->r = node; glue->l = new_node; } new_node->parent = glue; if (node->parent == NULL) radix->head = glue; else if (node->parent->r == node) node->parent->r = glue; else node->parent->l = glue; node->parent = glue; } return (new_node); } void radix_remove(radix_tree_t *radix, radix_node_t *node) { radix_node_t *parent, *child; if (node->r && node->l) { /* * this might be a placeholder node -- have to check and make * sure there is a prefix aossciated with it ! */ if (node->prefix != NULL) Deref_Prefix(node->prefix); node->prefix = NULL; /* Also I needed to clear data pointer -- masaki */ node->data = NULL; return; } if (node->r == NULL && node->l == NULL) { parent = node->parent; Deref_Prefix(node->prefix); PyMem_Free(node); radix->num_active_node--; if (parent == NULL) { radix->head = NULL; return; } if (parent->r == node) { parent->r = NULL; child = parent->l; } else { parent->l = NULL; child = parent->r; } if (parent->prefix) return; /* we need to remove parent too */ if (parent->parent == NULL) radix->head = child; else if (parent->parent->r == parent) parent->parent->r = child; else parent->parent->l = child; child->parent = parent->parent; PyMem_Free(parent); radix->num_active_node--; return; } if (node->r) child = node->r; else child = node->l; parent = node->parent; child->parent = parent; Deref_Prefix(node->prefix); PyMem_Free(node); radix->num_active_node--; if (parent == NULL) { radix->head = child; return; } if (parent->r == node) parent->r = child; else parent->l = child; } /* Local additions */ static void sanitise_mask(u_char *addr, u_int masklen, u_int maskbits) { u_int i = masklen / 8; u_int j = masklen % 8; if (j != 0) { addr[i] &= (~0) << (8 - j); i++; } for (; i < maskbits / 8; i++) addr[i] = 0; } prefix_t *prefix_pton(const char *string, long len, const char **errmsg) { char save[256], *cp, *ep; struct addrinfo hints, *ai; void *addr; prefix_t *ret; size_t slen; int r; ret = NULL; /* Copy the string to parse, because we modify it */ if ((slen = strlen(string) + 1) > sizeof(save)) { *errmsg = "string too long"; return (NULL); } memcpy(save, string, slen); if ((cp = strchr(save, '/')) != NULL) { if (len != -1 ) { *errmsg = "masklen specified twice"; return (NULL); } *cp++ = '\0'; len = strtol(cp, &ep, 10); if (*cp == '\0' || *ep != '\0' || len < 0) { *errmsg = "could not parse masklen"; return (NULL); } /* More checks below */ } memset(&hints, '\0', sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; if ((r = getaddrinfo(save, NULL, &hints, &ai)) != 0) { snprintf(save, sizeof(save), "getaddrinfo: %s:", gai_strerror(r)); *errmsg = save; return NULL; } if (ai == NULL || ai->ai_addr == NULL) { *errmsg = "getaddrinfo returned no result"; goto out; } switch (ai->ai_addr->sa_family) { case AF_INET: if (len == -1) len = 32; else if (len < 0 || len > 32) goto out; addr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr; sanitise_mask(addr, len, 32); break; case AF_INET6: if (len == -1) len = 128; else if (len < 0 || len > 128) goto out; addr = &((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr; sanitise_mask(addr, len, 128); break; default: goto out; } ret = New_Prefix2(ai->ai_addr->sa_family, addr, len, NULL); if (ret == NULL) *errmsg = "New_Prefix2 failed"; out: freeaddrinfo(ai); return (ret); } prefix_t *prefix_from_blob(u_char *blob, int len, int prefixlen) { int family, maxprefix; switch (len) { case 4: /* Assume AF_INET */ family = AF_INET; maxprefix = 32; break; case 16: /* Assume AF_INET6 */ family = AF_INET6; maxprefix = 128; break; default: /* Who knows? */ return NULL; } if (prefixlen == -1) prefixlen = maxprefix; if (prefixlen < 0 || prefixlen > maxprefix) return NULL; return (New_Prefix2(family, blob, prefixlen, NULL)); } const char * prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len) { return (inet_ntop(prefix->family, &prefix->add, buf, len)); } const char * prefix_ntop(prefix_t *prefix, char *buf, size_t len) { char addrbuf[128]; if (prefix_addr_ntop(prefix, addrbuf, sizeof(addrbuf)) == NULL) return (NULL); snprintf(buf, len, "%s/%d", addrbuf, prefix->bitlen); return (buf); } py-radix-0.5/radix.h010064400017500001750000000127471070756003400125570ustar00djmdjm/* * Copyright (c) 1999-2000 * * The Regents of the University of Michigan ("The Regents") and * Merit Network, Inc. All rights reserved. Redistribution and use * in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. All advertising materials mentioning features or use of * this software must display the following acknowledgement: * * This product includes software developed by the University of * Michigan, Merit Network, Inc., and their contributors. * * 4. Neither the name of the University, Merit Network, nor the * names of their contributors may be used to endorse or * promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Portions Copyright (c) 2004,2005 Damien Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. */ /* $Id: radix.h,v 1.9 2007/10/24 06:03:08 djm Exp $ */ #ifndef _RADIX_H #define _RADIX_H #if defined(_MSC_VER) #include #include #else # include # include # include # include # include #endif #if defined(_MSC_VER) # define snprintf _snprintf typedef unsigned __int8 u_int8_t; typedef unsigned __int16 u_int16_t; typedef unsigned __int32 u_int32_t; const char *inet_ntop(int af, const void *src, char *dst, size_t size); size_t strlcpy(char *dst, const char *src, size_t size); #endif /* * Originally from MRT include/mrt.h * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ typedef struct _prefix_t { u_int family; /* AF_INET | AF_INET6 */ u_int bitlen; /* same as mask? */ int ref_count; /* reference count */ union { struct in_addr sin; struct in6_addr sin6; } add; } prefix_t; void Deref_Prefix(prefix_t *prefix); /* * Originally from MRT include/radix.h * $MRTId: radix.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ */ typedef struct _radix_node_t { u_int bit; /* flag if this node used */ prefix_t *prefix; /* who we are in radix tree */ struct _radix_node_t *l, *r; /* left and right children */ struct _radix_node_t *parent; /* may be used */ void *data; /* pointer to data */ } radix_node_t; typedef struct _radix_tree_t { radix_node_t *head; u_int maxbits; /* for IP, 32 bit addresses */ int num_active_node; /* for debug purpose */ } radix_tree_t; /* Type of callback function */ typedef void (*rdx_cb_t)(radix_node_t *, void *); radix_tree_t *New_Radix(void); void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx); radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix); void radix_remove(radix_tree_t *radix, radix_node_t *node); radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix); radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix); void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx); #define RADIX_MAXBITS 128 #define RADIX_WALK(Xhead, Xnode) \ do { \ radix_node_t *Xstack[RADIX_MAXBITS+1]; \ radix_node_t **Xsp = Xstack; \ radix_node_t *Xrn = (Xhead); \ while ((Xnode = Xrn)) { \ if (Xnode->prefix) #define RADIX_WALK_END \ if (Xrn->l) { \ if (Xrn->r) { \ *Xsp++ = Xrn->r; \ } \ Xrn = Xrn->l; \ } else if (Xrn->r) { \ Xrn = Xrn->r; \ } else if (Xsp != Xstack) { \ Xrn = *(--Xsp); \ } else { \ Xrn = (radix_node_t *) 0; \ } \ } \ } while (0) /* Local additions */ prefix_t *prefix_pton(const char *string, long len, const char **errmsg); prefix_t *prefix_from_blob(u_char *blob, int len, int prefixlen); const char *prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len); const char *prefix_ntop(prefix_t *prefix, char *buf, size_t len); #endif /* _RADIX_H */ py-radix-0.5/radix_python.c010064400017500001750000000632321073163243500141500ustar00djmdjm/* * Copyright (c) 2004 Damien Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 "Python.h" #include "structmember.h" #include "radix.h" /* $Id: radix_python.c,v 1.30 2007/12/18 02:49:01 djm Exp $ */ /* Prototypes */ struct _RadixObject; struct _RadixIterObject; static struct _RadixIterObject *newRadixIterObject(struct _RadixObject *); static PyObject *radix_Radix(PyObject *, PyObject *); /* ------------------------------------------------------------------------ */ PyObject *radix_constructor; /* RadixNode: tree nodes */ typedef struct { PyObject_HEAD PyObject *user_attr; /* User-specified attributes */ PyObject *network; PyObject *prefix; PyObject *prefixlen; PyObject *family; PyObject *packed; radix_node_t *rn; /* Actual radix node (pointer to parent) */ } RadixNodeObject; static PyTypeObject RadixNode_Type; static RadixNodeObject * newRadixNodeObject(radix_node_t *rn) { RadixNodeObject *self; char network[256], prefix[256]; /* Sanity check */ if (rn == NULL || rn->prefix == NULL || (rn->prefix->family != AF_INET && rn->prefix->family != AF_INET6)) return NULL; self = PyObject_New(RadixNodeObject, &RadixNode_Type); if (self == NULL) return NULL; self->rn = rn; /* Format addresses for packing into objects */ prefix_addr_ntop(rn->prefix, network, sizeof(network)); prefix_ntop(rn->prefix, prefix, sizeof(prefix)); self->user_attr = PyDict_New(); self->network = PyString_FromString(network); self->prefix = PyString_FromString(prefix); self->prefixlen = PyInt_FromLong(rn->prefix->bitlen); self->family = PyInt_FromLong(rn->prefix->family); self->packed = PyString_FromStringAndSize((char*)&rn->prefix->add, rn->prefix->family == AF_INET ? 4 : 16); if (self->user_attr == NULL || self->prefixlen == NULL || self->family == NULL || self->network == NULL || self->prefix == NULL) { /* RadixNode_dealloc will clean up for us */ Py_XDECREF(self); return (NULL); } return self; } /* RadixNode methods */ static void RadixNode_dealloc(RadixNodeObject *self) { Py_XDECREF(self->user_attr); Py_XDECREF(self->prefixlen); Py_XDECREF(self->family); Py_XDECREF(self->network); Py_XDECREF(self->prefix); PyObject_Del(self); } static PyMemberDef RadixNode_members[] = { {"data", T_OBJECT, offsetof(RadixNodeObject, user_attr), READONLY}, {"network", T_OBJECT, offsetof(RadixNodeObject, network), READONLY}, {"prefix", T_OBJECT, offsetof(RadixNodeObject, prefix), READONLY}, {"prefixlen", T_OBJECT, offsetof(RadixNodeObject, prefixlen), READONLY}, {"family", T_OBJECT, offsetof(RadixNodeObject, family), READONLY}, {"packed", T_OBJECT, offsetof(RadixNodeObject, packed), READONLY}, {NULL} }; PyDoc_STRVAR(RadixNode_doc, "Node in a radix tree"); static PyTypeObject RadixNode_Type = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "radix.RadixNode", /*tp_name*/ sizeof(RadixNodeObject),/*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)RadixNode_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ RadixNode_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ 0, /*tp_methods*/ RadixNode_members, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; /* ------------------------------------------------------------------------ */ typedef struct _RadixObject { PyObject_HEAD radix_tree_t *rt4; /* Radix tree for IPv4 addresses */ radix_tree_t *rt6; /* Radix tree for IPv6 addresses */ unsigned int gen_id; /* Detect modification during iterations */ } RadixObject; static PyTypeObject Radix_Type; #define Radix_CheckExact(op) ((op)->ob_type == &Radix_Type) static RadixObject * newRadixObject(void) { RadixObject *self; radix_tree_t *rt4, *rt6; if ((rt4 = New_Radix()) == NULL) return (NULL); if ((rt6 = New_Radix()) == NULL) { free(rt4); return (NULL); } if ((self = PyObject_New(RadixObject, &Radix_Type)) == NULL) { free(rt4); free(rt6); return (NULL); } self->rt4 = rt4; self->rt6 = rt6; self->gen_id = 0; return (self); } /* Radix methods */ static void Radix_dealloc(RadixObject *self) { radix_node_t *rn; RadixNodeObject *node; RADIX_WALK(self->rt4->head, rn) { if (rn->data != NULL) { node = rn->data; node->rn = NULL; Py_DECREF(node); } } RADIX_WALK_END; RADIX_WALK(self->rt6->head, rn) { if (rn->data != NULL) { node = rn->data; node->rn = NULL; Py_DECREF(node); } } RADIX_WALK_END; Destroy_Radix(self->rt4, NULL, NULL); Destroy_Radix(self->rt6, NULL, NULL); PyObject_Del(self); } static prefix_t *args_to_prefix(char *addr, char *packed, int packlen, long prefixlen) { prefix_t *prefix = NULL; const char *errmsg; if (addr != NULL && packed != NULL) { PyErr_SetString(PyExc_TypeError, "Two address types specified. Please pick one."); return NULL; } if (addr == NULL && packed == NULL) { PyErr_SetString(PyExc_TypeError, "No address specified (use 'address' or 'packed')"); return NULL; } if (addr != NULL) { /* Parse a string address */ if ((prefix = prefix_pton(addr, prefixlen, &errmsg)) == NULL) { PyErr_SetString(PyExc_ValueError, errmsg ? errmsg : "Invalid address format"); } } else if (packed != NULL) { /* "parse" a packed binary address */ if ((prefix = prefix_from_blob((u_char*)packed, packlen, prefixlen)) == NULL) { PyErr_SetString(PyExc_ValueError, "Invalid packed address format"); } } if (prefix != NULL && prefix->family != AF_INET && prefix->family != AF_INET6) { Deref_Prefix(prefix); return (NULL); } return prefix; } #define PICKRT(prefix, rno) (prefix->family == AF_INET6 ? rno->rt6 : rno->rt4) static PyObject * create_add_node(RadixObject *self, prefix_t *prefix) { radix_node_t *node; RadixNodeObject *node_obj; if ((node = radix_lookup(PICKRT(prefix, self), prefix)) == NULL) { PyErr_SetString(PyExc_MemoryError, "Couldn't add prefix"); return NULL; } /* * Create a RadixNode object in the data area of the node * We duplicate most of the node's identity, because the radix.c:node * itself has a lifetime independent of the Python node object * Confusing? yeah... */ if (node->data == NULL) { if ((node_obj = newRadixNodeObject(node)) == NULL) return (NULL); node->data = node_obj; } else node_obj = node->data; self->gen_id++; Py_XINCREF(node_obj); return (PyObject *)node_obj; } PyDoc_STRVAR(Radix_add_doc, "Radix.add(network[, masklen][, packed]) -> new RadixNode object\n\ \n\ Adds the network specified by 'network' and 'masklen' to the radix\n\ tree. 'network' may be a string in CIDR format, a unicast host\n\ address or a network address, with the mask length specified using\n\ the optional 'masklen' parameter.\n\ \n\ Alternately, the address may be specified in a packed binary format\n\ using the 'packed' keyword argument (instead of 'network'). This is\n\ useful with binary addresses returned by socket.getpeername(),\n\ socket.inet_ntoa(), etc.\n\ \n\ Both IPv4 and IPv6 addresses/networks are supported and may be mixed in\n\ the same tree.\n\ \n\ This method returns a RadixNode object. Arbitrary data may be strored\n\ in the RadixNode.data dict."); static PyObject * Radix_add(RadixObject *self, PyObject *args, PyObject *kw_args) { prefix_t *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; PyObject *node_obj; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|sls#:add", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(addr, packed, packlen, prefixlen)) == NULL) return NULL; node_obj = create_add_node(self, prefix); Deref_Prefix(prefix); return node_obj; } PyDoc_STRVAR(Radix_delete_doc, "Radix.delete(network[, masklen][, packed] -> None\n\ \n\ Deletes the specified network from the radix tree."); static PyObject * Radix_delete(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|sls#:delete", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(addr, packed, packlen, prefixlen)) == NULL) return NULL; if ((node = radix_search_exact(PICKRT(prefix, self), prefix)) == NULL) { Deref_Prefix(prefix); PyErr_SetString(PyExc_KeyError, "no such address"); return NULL; } if (node->data != NULL) { node_obj = node->data; node_obj->rn = NULL; Py_XDECREF(node_obj); } radix_remove(PICKRT(prefix, self), node); Deref_Prefix(prefix); self->gen_id++; Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(Radix_search_exact_doc, "Radix.search_exact(network[, masklen][, packed] -> RadixNode or None\n\ \n\ Search for the specified network in the radix tree. In order to\n\ match, the 'prefix' must be specified exactly. Contrast with the\n\ Radix.search_best method.\n\ \n\ If no match is found, then this method returns None."); static PyObject * Radix_search_exact(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|sls#:search_exact", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(addr, packed, packlen, prefixlen)) == NULL) return NULL; node = radix_search_exact(PICKRT(prefix, self), prefix); if (node == NULL || node->data == NULL) { Deref_Prefix(prefix); Py_INCREF(Py_None); return Py_None; } Deref_Prefix(prefix); node_obj = node->data; Py_XINCREF(node_obj); return (PyObject *)node_obj; } PyDoc_STRVAR(Radix_search_best_doc, "Radix.search_best(network[, masklen][, packed] -> None\n\ \n\ Search for the specified network in the radix tree.\n\ \n\ search_best will return the best (longest) entry that includes the\n\ specified 'prefix', much like a IP routing table lookup.\n\ \n\ If no match is found, then returns None."); static PyObject * Radix_search_best(RadixObject *self, PyObject *args, PyObject *kw_args) { radix_node_t *node; RadixNodeObject *node_obj; prefix_t *prefix; static char *keywords[] = { "network", "masklen", "packed", NULL }; char *addr = NULL, *packed = NULL; long prefixlen = -1; int packlen = -1; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "|sls#:search_best", keywords, &addr, &prefixlen, &packed, &packlen)) return NULL; if ((prefix = args_to_prefix(addr, packed, packlen, prefixlen)) == NULL) return NULL; if ((node = radix_search_best(PICKRT(prefix, self), prefix)) == NULL || node->data == NULL) { Deref_Prefix(prefix); Py_INCREF(Py_None); return Py_None; } Deref_Prefix(prefix); node_obj = node->data; Py_XINCREF(node_obj); return (PyObject *)node_obj; } PyDoc_STRVAR(Radix_nodes_doc, "Radix.nodes(prefix) -> List of RadixNode\n\ \n\ Returns a list containing all the RadixNode objects that have been\n\ entered into the tree. This list may be empty if no prefixes have\n\ been entered."); static PyObject * Radix_nodes(RadixObject *self, PyObject *args) { radix_node_t *node; PyObject *ret; if (!PyArg_ParseTuple(args, ":nodes")) return NULL; if ((ret = PyList_New(0)) == NULL) return NULL; RADIX_WALK(self->rt4->head, node) { if (node->data != NULL) PyList_Append(ret, (PyObject *)node->data); } RADIX_WALK_END; RADIX_WALK(self->rt6->head, node) { if (node->data != NULL) PyList_Append(ret, (PyObject *)node->data); } RADIX_WALK_END; return (ret); } PyDoc_STRVAR(Radix_prefixes_doc, "Radix.prefixes(prefix) -> List of prefix strings\n\ \n\ Returns a list containing all the prefixes that have been entered\n\ into the tree. This list may be empty if no prefixes have been\n\ entered."); static PyObject * Radix_prefixes(RadixObject *self, PyObject *args) { radix_node_t *node; PyObject *ret; if (!PyArg_ParseTuple(args, ":prefixes")) return NULL; if ((ret = PyList_New(0)) == NULL) return NULL; RADIX_WALK(self->rt4->head, node) { if (node->data != NULL) { PyList_Append(ret, ((RadixNodeObject *)node->data)->prefix); } } RADIX_WALK_END; RADIX_WALK(self->rt6->head, node) { if (node->data != NULL) { PyList_Append(ret, ((RadixNodeObject *)node->data)->prefix); } } RADIX_WALK_END; return (ret); } /* Used for pickling */ static PyObject * radix_getstate(RadixObject *self) { radix_node_t *node; PyObject *ret; RadixNodeObject *rnode; if ((ret = PyList_New(0)) == NULL) return NULL; RADIX_WALK(self->rt4->head, node) { if (node->data != NULL) { rnode = (RadixNodeObject *)node->data; PyList_Append(ret, Py_BuildValue("(OO)", rnode->prefix, rnode->user_attr)); Py_INCREF(rnode->prefix); Py_INCREF(rnode->user_attr); } } RADIX_WALK_END; RADIX_WALK(self->rt6->head, node) { if (node->data != NULL) { rnode = (RadixNodeObject *)node->data; PyList_Append(ret, Py_BuildValue("(OO)", rnode->prefix, rnode->user_attr)); Py_INCREF(rnode->prefix); Py_INCREF(rnode->user_attr); } } RADIX_WALK_END; return (ret); } static PyObject * Radix_getstate(RadixObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":__getstate__")) return NULL; return radix_getstate(self); } static PyObject * Radix_reduce(RadixObject *self, PyObject *args) { PyObject *state, *ret; if (!PyArg_ParseTuple(args, ":__reduce__")) return NULL; if ((state = radix_getstate(self)) == NULL) return NULL; ret = Py_BuildValue("(O()O)", radix_constructor, state); Py_XINCREF(radix_constructor); Py_XINCREF(state); return ret; } /* Used for unpickling */ static PyObject * Radix_setstate(RadixObject *self, PyObject *args) { PyObject *state, *tpl, *addr, *data; int len, i; RadixNodeObject *node; prefix_t *prefix; char *addr_string; const char *errmsg; if (!Radix_CheckExact(self)) { PyErr_SetString(PyExc_ValueError, "not a Radix object"); return NULL; } /* XXX type-checking is overstrict here */ if (!PyArg_ParseTuple(args, "O!:__setstate__", &PyList_Type, &state)) return NULL; len = PyList_Size(state); for (i = 0; i < len; i++) { if ((tpl = PyList_GetItem(state, i)) == NULL) return NULL; if ((addr = PyTuple_GetItem(tpl, 0)) == NULL) return NULL; if ((data = PyTuple_GetItem(tpl, 1)) == NULL) return NULL; if ((addr_string = PyString_AsString(addr)) == NULL) return NULL; if ((prefix = prefix_pton(addr_string, -1, &errmsg)) == NULL) { PyErr_SetString(PyExc_ValueError, errmsg ? errmsg : "Invalid address format"); return NULL; } if ((node = (RadixNodeObject *)create_add_node(self, prefix)) == NULL) { Deref_Prefix(prefix); return NULL; } Deref_Prefix(prefix); Py_XDECREF(node->user_attr); node->user_attr = data; Py_INCREF(node->user_attr); } Py_INCREF(Py_None); return Py_None; } static PyObject * Radix_getiter(RadixObject *self) { return (PyObject *)newRadixIterObject(self); } PyDoc_STRVAR(Radix_doc, "Radix tree"); static PyMethodDef Radix_methods[] = { {"add", (PyCFunction)Radix_add, METH_VARARGS|METH_KEYWORDS, Radix_add_doc }, {"delete", (PyCFunction)Radix_delete, METH_VARARGS|METH_KEYWORDS, Radix_delete_doc }, {"search_exact",(PyCFunction)Radix_search_exact,METH_VARARGS|METH_KEYWORDS, Radix_search_exact_doc }, {"search_best", (PyCFunction)Radix_search_best, METH_VARARGS|METH_KEYWORDS, Radix_search_best_doc }, {"nodes", (PyCFunction)Radix_nodes, METH_VARARGS, Radix_nodes_doc }, {"prefixes", (PyCFunction)Radix_prefixes, METH_VARARGS, Radix_prefixes_doc }, {"__getstate__",(PyCFunction)Radix_getstate, METH_VARARGS, NULL }, {"__setstate__",(PyCFunction)Radix_setstate, METH_VARARGS, NULL }, {"__reduce__", (PyCFunction)Radix_reduce, METH_VARARGS, NULL }, {NULL, NULL} /* sentinel */ }; static PyTypeObject Radix_Type = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "radix.Radix", /*tp_name*/ sizeof(RadixObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)Radix_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ Radix_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ (getiterfunc)Radix_getiter, /*tp_iter*/ 0, /*tp_iternext*/ Radix_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; /* ------------------------------------------------------------------------ */ /* RadixIter: radix tree iterator */ typedef struct _RadixIterObject { PyObject_HEAD RadixObject *parent; radix_node_t *iterstack[RADIX_MAXBITS+1]; radix_node_t **sp; radix_node_t *rn; int af; unsigned int gen_id; /* Detect tree modifications */ } RadixIterObject; static PyTypeObject RadixIter_Type; static RadixIterObject * newRadixIterObject(RadixObject *parent) { RadixIterObject *self; self = PyObject_New(RadixIterObject, &RadixIter_Type); if (self == NULL) return NULL; self->parent = parent; Py_XINCREF(self->parent); self->sp = self->iterstack; self->rn = self->parent->rt4->head; self->gen_id = self->parent->gen_id; self->af = AF_INET; return self; } /* RadixIter methods */ static void RadixIter_dealloc(RadixIterObject *self) { Py_XDECREF(self->parent); PyObject_Del(self); } static PyObject * RadixIter_iternext(RadixIterObject *self) { radix_node_t *node; PyObject *ret; if (self->gen_id != self->parent->gen_id) { PyErr_SetString(PyExc_RuntimeWarning, "Radix tree modified during iteration"); return (NULL); } again: if ((node = self->rn) == NULL) { /* We have walked both trees */ if (self->af == AF_INET6) return NULL; /* Otherwise reset and start walk of IPv6 tree */ self->sp = self->iterstack; self->rn = self->parent->rt6->head; self->af = AF_INET6; goto again; } /* Get next node */ if (self->rn->l) { if (self->rn->r) *self->sp++ = self->rn->r; self->rn = self->rn->l; } else if (self->rn->r) self->rn = self->rn->r; else if (self->sp != self->iterstack) self->rn = *(--self->sp); else self->rn = NULL; if (node->prefix == NULL || node->data == NULL) goto again; ret = node->data; Py_INCREF(ret); return (ret); } PyDoc_STRVAR(RadixIter_doc, "Radix tree iterator"); static PyTypeObject RadixIter_Type = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "radix.RadixIter", /*tp_name*/ sizeof(RadixIterObject),/*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)RadixIter_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ RadixIter_doc, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ (iternextfunc)RadixIter_iternext, /*tp_iternext*/ 0, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; /* ------------------------------------------------------------------------ */ /* Radix object creator */ PyDoc_STRVAR(radix_Radix_doc, "Radix() -> new Radix tree object\n\ \n\ Instantiate a new radix tree object."); static PyObject * radix_Radix(PyObject *self, PyObject *args) { RadixObject *rv; if (!PyArg_ParseTuple(args, ":Radix")) return NULL; rv = newRadixObject(); if (rv == NULL) return NULL; return (PyObject *)rv; } static PyMethodDef radix_methods[] = { {"Radix", radix_Radix, METH_VARARGS, radix_Radix_doc }, {NULL, NULL} /* sentinel */ }; PyDoc_STRVAR(module_doc, "Implementation of a radix tree data structure for network prefixes.\n" "\n" "The radix tree is the data structure most commonly used for routing\n" "table lookups. It efficiently stores network prefixes of varying\n" "lengths and allows fast lookups of containing networks.\n" "\n" "Simple example:\n" "\n" " import radix\n" "\n" " # Create a new tree\n" " rtree = radix.Radix()\n" "\n" " # Adding a node returns a RadixNode object. You can create\n" " # arbitrary members in its 'data' dict to store your data\n" " rnode = rtree.add(\"10.0.0.0/8\")\n" " rnode.data[\"blah\"] = \"whatever you want\"\n" "\n" " # You can specify nodes as CIDR addresses, or networks with\n" " # separate mask lengths. The following three invocations are\n" " # identical:\n" " rnode = rtree.add(\"10.0.0.0/16\")\n" " rnode = rtree.add(\"10.0.0.0\", 16)\n" " rnode = rtree.add(network = \"10.0.0.0\", masklen = 16)\n" "\n" " # It is also possible to specify nodes using binary packed\n" " # addresses, such as those returned by the socket module\n" " # functions. In this case, the radix module will assume that\n" " # a four-byte address is an IPv4 address and a sixteen-byte\n" " # address is an IPv6 address. For example:\n" " binary_addr = inet_ntoa(\"172.18.22.0\")\n" " rnode = rtree.add(packed = binary_addr, masklen = 23)\n" "\n" " # Exact search will only return prefixes you have entered\n" " # You can use all of the above ways to specify the address\n" " rnode = rtree.search_exact(\"10.0.0.0/8\")\n" " # Get your data back out\n" " print rnode.data[\"blah\"]\n" " # Use a packed address\n" " addr = socket.inet_ntoa(\"10.0.0.0\")\n" " rnode = rtree.search_exact(packed = addr, masklen = 8)\n" "\n" " # Best-match search will return the longest matching prefix\n" " # that contains the search term (routing-style lookup)\n" " rnode = rtree.search_best(\"10.123.45.6\")\n" "\n" " # There are a couple of implicit members of a RadixNode:\n" " print rnode.network # -> \"10.0.0.0\"\n" " print rnode.prefix # -> \"10.0.0.0/8\"\n" " print rnode.prefixlen # -> 8\n" " print rnode.family # -> socket.AF_INET\n" " print rnode.packed # -> '\\n\\x00\\x00\\x00'\n" "\n" " # IPv6 prefixes are fully supported in the same tree\n" " rnode = rtree.add(\"2001:DB8::/32\")\n" " rnode = rtree.add(\"::/0\")\n" "\n" " # Use the nodes() method to return all RadixNodes created\n" " nodes = rtree.nodes()\n" " for rnode in nodes:\n" " print rnode.prefix\n" "\n" " # The prefixes() method will return all the prefixes (as a\n" " # list of strings) that have been entered\n" " prefixes = rtree.prefixes()\n" "\n" " # You can also directly iterate over the tree itself\n" " # this would save some memory if the tree is big\n" " # NB. Don't modify the tree (add or delete nodes) while\n" " # iterating otherwise you will abort the iteration and\n" " # receive a RuntimeWarning. Changing a node's data dict\n" " # is permitted.\n" " for rnode in rtree:\n" " print rnode.prefix\n" ); #if defined(_MSC_VER) static void cleanupradix(void) { WSACleanup(); } #endif PyMODINIT_FUNC initradix(void) { PyObject *m, *d; #if defined(_MSC_VER) WSADATA winsock_data; int r; char errbuf[256]; r = WSAStartup(0x0202, &winsock_data); switch (r) { case 0: Py_AtExit(cleanupradix); break; case WSASYSNOTREADY: PyErr_SetString(PyExc_ImportError, "Winsock error: " "network subsystem not ready"); return; case WSAEINVAL: case WSAVERNOTSUPPORTED: PyErr_SetString(PyExc_ImportError, "Winsock error: " "required winsock version 2.2 not supported"); return; default: snprintf(errbuf, sizeof(errbuf), "Winsock error: code %d", r); PyErr_SetString(PyExc_ImportError, errbuf); return; } #endif if (PyType_Ready(&Radix_Type) < 0) return; if (PyType_Ready(&RadixNode_Type) < 0) return; m = Py_InitModule3("radix", radix_methods, module_doc); /* Stash the callable constructor for use in Radix.__reduce__ */ d = PyModule_GetDict(m); radix_constructor = PyDict_GetItemString(d, "Radix"); PyModule_AddStringConstant(m, "__version__", "0.4"); } py-radix-0.5/setup.py010075500017500001750000000033311073161504100127750ustar00djmdjm#!/usr/bin/env python # Copyright (c) 2004 Damien Miller # # 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 THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. # $Id: setup.py,v 1.7 2007/12/18 00:53:53 djm Exp $ import sys from distutils.core import setup, Extension VERSION = "0.5" if __name__ == '__main__': libs = [] src = [ 'radix.c', 'radix_python.c' ] if sys.platform == 'win32': libs += [ 'ws2_32' ] src += [ 'inet_ntop.c', 'strlcpy.c' ] radix = Extension('radix', libraries = libs, sources = src) setup( name = "radix", version = VERSION, author = "Damien Miller", author_email = "djm@mindrot.org", url = "http://www.mindrot.org/py-radix.html", description = "Radix tree implementation", long_description = """\ py-radix is an implementation of a radix tree data structure for the storage and retrieval of IPv4 and IPv6 network prefixes. The radix tree is the data structure most commonly used for routing table lookups. It efficiently stores network prefixes of varying lengths and allows fast lookups of containing networks. """, license = "BSD", ext_modules = [radix] ) py-radix-0.5/strlcpy.c010064400017500001750000000030311046577534300131410ustar00djmdjm/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } py-radix-0.5/test.py010075500017500001750000000255461073161504100126300ustar00djmdjm#!/usr/bin/env python # Copyright (c) 2004 Damien Miller # # 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 THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. # $Id: test.py,v 1.15 2007/12/18 00:53:53 djm Exp $ import radix import unittest import socket import pickle import cPickle class TestRadix(unittest.TestCase): def test_00__create_destroy(self): tree = radix.Radix() self.assertEqual(str(type(tree)), "") del tree def test_01__create_node(self): tree = radix.Radix() node = tree.add("10.0.0.0/8") self.assertEqual(str(type(node)), "") self.assertEqual(node.prefix, "10.0.0.0/8") self.assertEqual(node.network, "10.0.0.0") self.assertEqual(node.prefixlen, 8) self.assertEqual(node.family, socket.AF_INET) node = tree.add("10.0.0.0", 16) self.assertEqual(node.network, "10.0.0.0") self.assertEqual(node.prefixlen, 16) node = tree.add(network = "10.0.0.0", masklen = 24) self.assertEqual(node.network, "10.0.0.0") self.assertEqual(node.prefixlen, 24) node2 = tree.add(network = "ff00::", masklen = 24) self.assertEqual(node2.network, "ff00::") self.assertEqual(node2.prefixlen, 24) self.assert_(node is not node2) def test_02__node_userdata(self): tree = radix.Radix() node = tree.add(network = "10.0.0.0", masklen=28) node.data["blah"] = "abc123" node.data["foo"] = 12345 self.assertEqual(node.data["blah"], "abc123") self.assertEqual(node.data["foo"], 12345) self.assertRaises(AttributeError, lambda x: x.nonexist, node) del node.data["blah"] self.assertRaises(KeyError, lambda x: x.data["blah"], node) def test_03__search_exact(self): tree = radix.Radix() node1 = tree.add("10.0.0.0/8") node2 = tree.add("10.0.0.0/16") node3 = tree.add("10.0.0.0/24") node2.data["foo"] = 12345 node = tree.search_exact("127.0.0.1") self.assertEqual(node, None) node = tree.search_exact("10.0.0.0") self.assertEqual(node, None) node = tree.search_exact("10.0.0.0/24") self.assertEqual(node, node3) node = tree.search_exact("10.0.0.0/8") self.assertEqual(node, node1) node = tree.search_exact("10.0.0.0/16") self.assertEqual(node.data["foo"], 12345) def test_04__search_best(self): tree = radix.Radix() node1 = tree.add("10.0.0.0/8") node2 = tree.add("10.0.0.0/16") node3 = tree.add("10.0.0.0/24") node = tree.search_best("127.0.0.1") self.assertEqual(node, None) node = tree.search_best("10.0.0.0") self.assertEqual(node, node3) node = tree.search_best("10.0.0.0/24") self.assertEqual(node, node3) node = tree.search_best("10.0.1.0/24") self.assertEqual(node, node2) def test_05__concurrent_trees(self): tree1 = radix.Radix() node1_1 = tree1.add("20.0.0.0/8") node1_1 = tree1.add("10.0.0.0/8") node1_2 = tree1.add("10.0.0.0/16") node1_3 = tree1.add("10.0.0.0/24") node1_3.data["blah"] = 12345 tree2 = radix.Radix() node2_1 = tree2.add("30.0.0.0/8") node2_1 = tree2.add("10.0.0.0/8") node2_2 = tree2.add("10.0.0.0/16") node2_3 = tree2.add("10.0.0.0/24") node2_3.data["blah"] = 45678 self.assertNotEqual(tree1, tree2) self.assertNotEqual(node1_2, node2_2) node = tree1.search_best("10.0.1.0/24") self.assertEqual(node, node1_2) self.assertNotEqual(node, node2_2) node = tree2.search_best("20.0.0.0/24") self.assertEqual(node, None) node = tree2.search_best("10.0.0.10") self.assertEqual(node.data["blah"], 45678) def test_06__deletes(self): tree = radix.Radix() node1 = tree.add("10.0.0.0/8") node3 = tree.add("10.0.0.0/24") tree.delete("10.0.0.0/24") self.assertRaises(KeyError, tree.delete, "127.0.0.1") self.assertRaises(KeyError, tree.delete, "10.0.0.0/24") node = tree.search_best("10.0.0.10") self.assertEqual(node, node1) def test_07__nodes(self): tree = radix.Radix() prefixes = [ "10.0.0.0/8", "127.0.0.1/32", "10.1.0.0/16", "10.100.100.100/32", "abcd:ef12::/32", "abcd:ef01:2345:6789::/64", "::1/128" ] prefixes.sort() for prefix in prefixes: tree.add(prefix) nodes = tree.nodes() addrs = map(lambda x: x.prefix, nodes) addrs.sort() self.assertEqual(addrs, prefixes) def test_08__nodes_empty_tree(self): tree = radix.Radix() nodes = tree.nodes() self.assertEqual(nodes, []) def test_09__prefixes(self): tree = radix.Radix() prefixes = [ "10.0.0.0/8", "127.0.0.1/32", "10.1.0.0/16", "10.100.100.100/32", "abcd:ef12::/32", "abcd:ef01:2345:6789::/64", "::1/128" ] prefixes.sort() for prefix in prefixes: tree.add(prefix) addrs = tree.prefixes() addrs.sort() self.assertEqual(addrs, prefixes) def test_10__use_after_free(self): tree = radix.Radix() node1 = tree.add("10.0.0.0/8") del tree self.assertEquals(node1.prefix, "10.0.0.0/8") def test_11__unique_instance(self): tree = radix.Radix() node1 = tree.add("10.0.0.0/8") node2 = tree.add("10.0.0.0/8") self.assert_(node1 is node2) self.assert_(node1.prefix is node2.prefix) def test_12__inconsistent_masks4(self): tree = radix.Radix() node1 = tree.add("10.255.255.255", 28) node2 = tree.add(network = "10.255.255.240/28") node3 = tree.add(network = "10.255.255.252", masklen = 28) self.assert_(node1 is node2) self.assert_(node1 is node3) self.assertEquals(node1.prefix, "10.255.255.240/28") def test_13__inconsistent_masks6(self): tree = radix.Radix() node1 = tree.add("dead:beef:1234:5678::", 32) node2 = tree.add(network = "dead:beef:8888:9999::/32") node3 = tree.add(network = "dead:beef::", masklen = 32) self.assert_(node1 is node2) self.assert_(node1 is node3) self.assertEquals(node1.prefix, "dead:beef::/32") def test_14__packed_addresses4(self): tree = radix.Radix() p = '\xe0\x14\x0b@' node = tree.add(packed = p, masklen = 26) self.assertEquals(node.family, socket.AF_INET) self.assertEquals(node.prefix, "224.20.11.64/26") self.assertEquals(node.packed, p) def test_15__packed_addresses6(self): tree = radix.Radix() p = '\xde\xad\xbe\xef\x124Vx\x9a\xbc\xde\xf0\x00\x00\x00\x00' node = tree.add(packed = p, masklen = 108) self.assertEquals(node.family, socket.AF_INET6) self.assertEquals(node.prefix, "dead:beef:1234:5678:9abc:def0::/108") self.assertEquals(node.packed, p) def test_16__bad_addresses(self): tree = radix.Radix() self.assertRaises(TypeError, tree.add) self.assertRaises(ValueError, tree.add, "blah/32") self.assertRaises(ValueError, tree.add, "blah", 32) self.assertRaises(ValueError, tree.add, "127.0.0.1", -2) self.assertRaises(ValueError, tree.add, "127.0.0.1", 64) self.assertRaises(ValueError, tree.add, "::", -2) self.assertRaises(ValueError, tree.add, "::", 256) self.assertEquals(len(tree.nodes()), 0) def test_17__mixed_address_family(self): tree = radix.Radix() node1 = tree.add("255.255.255.255", 32) node2 = tree.add("ffff::/32") node1_o = tree.search_best("255.255.255.255") node2_o = tree.search_best("ffff::") self.assert_(node1 is node1_o) self.assert_(node2 is node2_o) self.assertNotEquals(node1.prefix, node2.prefix) self.assertNotEquals(node1.network, node2.network) self.assertNotEquals(node1.family, node2.family) def test_18__iterator(self): tree = radix.Radix() prefixes = [ "::1/128", "2000::/16", "2000::/8", "dead:beef::/64", "ffff::/16", "10.0.0.0/8", "a00::/8", "255.255.0.0/16", "::/0", "0.0.0.0/0" ] prefixes.sort() for prefix in prefixes: tree.add(prefix) iterprefixes = [] for node in tree: iterprefixes.append(node.prefix) iterprefixes.sort() self.assertEqual(iterprefixes, prefixes) def test_19__iterate_on_empty(self): tree = radix.Radix() prefixes = [] for node in tree: prefixes.append(node.prefix) self.assertEqual(prefixes, []) def test_20__iterate_and_modify_tree(self): tree = radix.Radix() prefixes = [ "::1/128", "2000::/16", "2000::/8", "dead:beef::/64", "0.0.0.0/8", "127.0.0.1/32" ] prefixes.sort() for prefix in prefixes: tree.add(prefix) self.assertRaises(RuntimeWarning, map, lambda x: tree.delete(x.prefix), tree) def test_21__lots_of_prefixes(self): tree = radix.Radix() num_nodes_in = 0 for i in range(0,128): for j in range(0,128): k = ((i + j) % 8) + 24 node = tree.add("1.%d.%d.0" % (i, j), k) node.data["i"] = i node.data["j"] = j num_nodes_in += 1 num_nodes_del = 0 for i in range(0,128,5): for j in range(0,128,3): k = ((i + j) % 8) + 24 tree.delete("1.%d.%d.0" % (i, j), k) num_nodes_del += 1 num_nodes_out = 0 for node in tree: i = node.data["i"] j = node.data["j"] k = ((i + j) % 8) + 24 prefix = "1.%d.%d.0/%d" % (i, j, k) self.assertEquals(node.prefix, prefix) num_nodes_out += 1 self.assertEquals(num_nodes_in - num_nodes_del, num_nodes_out) self.assertEquals(num_nodes_in - num_nodes_del, len(tree.nodes())) def test_22__broken_sanitise(self): tree = radix.Radix() node = tree.add("255.255.255.255/15") self.assertEquals(node.prefix, "255.254.0.0/15") def test_21__pickle(self): tree = radix.Radix() num_nodes_in = 0 for i in range(0,128): for j in range(0,128): k = ((i + j) % 8) + 24 addr = "1.%d.%d.0" % (i, j) node = tree.add(addr, k) node.data["i"] = i node.data["j"] = j num_nodes_in += 1 tree_pickled = pickle.dumps(tree) del tree tree2 = pickle.loads(tree_pickled) for i in range(0,128): for j in range(0,128): k = ((i + j) % 8) + 24 addr = "1.%d.%d.0" % (i, j) node = tree2.search_exact(addr, k) self.assertNotEquals(node, None) self.assertEquals(node.data["i"], i) self.assertEquals(node.data["j"], j) node.data["j"] = j self.assertEquals(len(tree2.nodes()), num_nodes_in) def test_21__cpickle(self): tree = radix.Radix() num_nodes_in = 0 for i in range(0,128): for j in range(0,128): k = ((i + j) % 8) + 24 addr = "1.%d.%d.0" % (i, j) node = tree.add(addr, k) node.data["i"] = i node.data["j"] = j num_nodes_in += 1 tree_pickled = cPickle.dumps(tree) del tree tree2 = cPickle.loads(tree_pickled) for i in range(0,128): for j in range(0,128): k = ((i + j) % 8) + 24 addr = "1.%d.%d.0" % (i, j) node = tree2.search_exact(addr, k) self.assertNotEquals(node, None) self.assertEquals(node.data["i"], i) self.assertEquals(node.data["j"], j) node.data["j"] = j self.assertEquals(len(tree2.nodes()), num_nodes_in) def main(): unittest.main() if __name__ == '__main__': main()