RE: [GENERAL] big numbers
Hello Lorenzo,
PostgreSQL v6.4 which is, currently in Beta and scheduled for release
Oct. 1, is planned to have an IPV6 type (can't remember what it was
decided to call it). You might just want to grab the current snapshot
and see what it has to offer.
-DEJ
Show quoted text
Hello,
How big a number can be stored as a integer in the lateset version of
postgres. See i got some code to do IP management and since i am just
using IPV4 addresses right now i am able to math operations on the
octets w/o to much fuss, since each octet is just composed of 8 bits.
But of course IPV6 is composed of 4-32 bit parts , or 1 - 128 bit
number.-lorenzo
_________________________________________________________
DO YOU YAHOO!?
Get your free @yahoo.com address at http://mail.yahoo.com
Hello Lorenzo,
PostgreSQL v6.4 which is, currently in Beta and scheduled for release
Oct. 1, is planned to have an IPV6 type (can't remember what it was
decided to call it). You might just want to grab the current snapshot
and see what it has to offer.
-DEJ
The person who said they were going to work on the IP type has not
responded to my requests for a status, and nothing has been added in
that area in 6.4.
Not sure what to do about it yet.
--
Bruce Momjian | 830 Blythe Avenue
maillist@candle.pha.pa.us | Drexel Hill, Pennsylvania 19026
+ If your life is a hard drive, | (610) 353-9879(w)
+ Christ can be your backup. | (610) 853-3000(h)
Bruce Momjian <maillist@candle.pha.pa.us> writes:
The person who said they were going to work on the IP type has not
responded to my requests for a status, and nothing has been added in
that area in 6.4.
That would be me. What I said was that it looked like the thing to do
was to pick the best ideas the three implementations available, and
that I, at least, would be doing that for my local needs. I still
plan to do that, but having recently become a father for the first
time, and having lots of projects going at work, I haven't found time
yet. Unless someone beats me to it, I will be doing this soon, and
probably within the next couple of weeks, but obviously not in time
for the 6.4 beta period. Besides, I have no idea how to integrate a
new type as a built-in type, so someone else would have to do that.
-tih
--
Popularity is the hallmark of mediocrity. --Niles Crane, "Frasier"
Import Notes
Reply to msg id not found: BruceMomjiansmessageofThu3Sep1998210221-0400EDT
Bruce Momjian <maillist@candle.pha.pa.us> writes:
The person who said they were going to work on the IP type has not
responded to my requests for a status, and nothing has been added in
that area in 6.4.That would be me. What I said was that it looked like the thing to do
was to pick the best ideas the three implementations available, and
that I, at least, would be doing that for my local needs. I still
plan to do that, but having recently become a father for the first
time, and having lots of projects going at work, I haven't found time
yet. Unless someone beats me to it, I will be doing this soon, and
probably within the next couple of weeks, but obviously not in time
for the 6.4 beta period. Besides, I have no idea how to integrate a
new type as a built-in type, so someone else would have to do that.
I would be glad to help with integrating it. We need a decision,
people. Who do we want to do this, and how are we going to handle
integrating this into the beta, if we want to?
BTW, does pg_upgrade work for people. That may be a quick fix for the
beta people to get these new system types WITHOUT dump/reload.
--
Bruce Momjian | 830 Blythe Avenue
maillist@candle.pha.pa.us | Drexel Hill, Pennsylvania 19026
+ If your life is a hard drive, | (610) 353-9879(w)
+ Christ can be your backup. | (610) 853-3000(h)
I would be glad to help with integrating it. We need a decision,
people. Who do we want to do this, and how are we going to handle
integrating this into the beta, if we want to?BTW, does pg_upgrade work for people. That may be a quick fix for the
beta people to get these new system types WITHOUT dump/reload.
Better yet, they _should_ use pg_upgrade, so it gets some beta testing
too :)
I can help with making the types built-in, once there is code ready.
However, until we finish tracking down the indexing problems I'd like to
stay away from unrelated changes to the catalogs and backend code to
avoid confusing the issue.
Now that I think about it, that would have probably included the
snprintf changes, since now people trying to do debugging may not be
able to build with a new tree until that gets integrated...
- Tom
On Fri, 4 Sep 1998, Thomas G. Lockhart wrote:
I would be glad to help with integrating it. We need a decision,
people. Who do we want to do this, and how are we going to handle
integrating this into the beta, if we want to?BTW, does pg_upgrade work for people. That may be a quick fix for the
beta people to get these new system types WITHOUT dump/reload.Better yet, they _should_ use pg_upgrade, so it gets some beta testing
too :)I can help with making the types built-in, once there is code ready.
However, until we finish tracking down the indexing problems I'd like to
stay away from unrelated changes to the catalogs and backend code to
avoid confusing the issue.Now that I think about it, that would have probably included the
snprintf changes, since now people trying to do debugging may not be
able to build with a new tree until that gets integrated...
I'm going to be spending this afternoon on one of my Solaris 2.5.1
machines making sure that it does compile...I have to do an upgrade here
anyway :) Well, not "here", but at work...
I would be glad to help with integrating it. We need a decision,
people. Who do we want to do this, and how are we going to handle
integrating this into the beta, if we want to?BTW, does pg_upgrade work for people. That may be a quick fix for the
beta people to get these new system types WITHOUT dump/reload.Better yet, they _should_ use pg_upgrade, so it gets some beta testing
too :)I can help with making the types built-in, once there is code ready.
However, until we finish tracking down the indexing problems I'd like to
stay away from unrelated changes to the catalogs and backend code to
avoid confusing the issue.Now that I think about it, that would have probably included the
snprintf changes, since now people trying to do debugging may not be
able to build with a new tree until that gets integrated...
Yes, we are definately on hold until the indexing this is fixed.
--
Bruce Momjian | 830 Blythe Avenue
maillist@candle.pha.pa.us | Drexel Hill, Pennsylvania 19026
+ If your life is a hard drive, | (610) 353-9879(w)
+ Christ can be your backup. | (610) 853-3000(h)
Bruce Momjian <maillist@candle.pha.pa.us> writes:
The person who said they were going to work on the IP type has not
responded to my requests for a status, and nothing has been added in
that area in 6.4.That would be me. What I said was that it looked like the thing to do
was to pick the best ideas the three implementations available, and
that I, at least, would be doing that for my local needs. I still
plan to do that, but having recently become a father for the first
time, and having lots of projects going at work, I haven't found time
yet. Unless someone beats me to it, I will be doing this soon, and
probably within the next couple of weeks, but obviously not in time
for the 6.4 beta period. Besides, I have no idea how to integrate a
new type as a built-in type, so someone else would have to do that.
Tom, Paul Vixie has said he can work on it, and in fact he already sent
in his version a month ago. I just had a baby three months ago, so I
know how busy you must be.
Unfortunately, there are few people who can integrate Paul's stuff with
the current ip_and_mac code, to get the best of both. I would hate to
throw out the ip_and_mac stuff without knowing if it has certain
advantages over his in certain areas.
I can take care of putting the code into the main PostgreSQL system, but
I need someone to give me a definitive cidr/ip type that I can
integrate.
People really want this for 6.4. We are in beta, but we can add this
type IF we can get it integrated within the next week or ten days.
When do you think you can you look over both versions, and send me one
good version to work with?
I can take it from there, and Paul and I can work out any bugs in the
code.
Even if you just say, "Paul's is better, throw out ip_and_mac", that is
all we need.
--
Bruce Momjian | 830 Blythe Avenue
maillist@candle.pha.pa.us | Drexel Hill, Pennsylvania 19026
+ If your life is a hard drive, | (610) 353-9879(w)
+ Christ can be your backup. | (610) 853-3000(h)
I can take care of putting the code into the main PostgreSQL system, but
I need someone to give me a definitive cidr/ip type that I can
integrate.People really want this for 6.4. We are in beta, but we can add this
type IF we can get it integrated within the next week or ten days.When do you think you can you look over both versions, and send me one
good version to work with?I can take it from there, and Paul and I can work out any bugs in the
code.Even if you just say, "Paul's is better, throw out ip_and_mac", that is
all we need.
i don't think it's that clear cut. my type is intended for layer 3
applications (IP) where either hosts or networks (or subnets including
variable-width subnets) need to be used as keys. the ip_and_mac type
is host-only but has an unfortunate bridging between layer 3 and layer 2.
in my opinion these are separate types, and since cidr is ready to go in
we ought to defer ip_and_mac to 6.5, and refocus it on layer 2 applications.
consider an ARP or DHCP table, which maps between layer 3 and layer 2.
having a type optimized for layer 3 and a different type optimized for
layer 2 creates no ugliness in my mind, and in fact serves the only need
i can imagine better than a single multihedral type would do.
note that cidr as supplied is capable of holding ipv6, which is also a
layer 3 entity. changing the name from CIDR to INET would be ok by me,
but making a type which is capable of holding either a layer 3 entity
or a layer 2 one would create semantic tension in my mind about it.
On Fri, 4 Sep 1998, Bruce Momjian wrote:
Unfortunately, there are few people who can integrate Paul's stuff with
the current ip_and_mac code, to get the best of both. I would hate to
throw out the ip_and_mac stuff without knowing if it has certain
advantages over his in certain areas.
Can we integrate Paul's CIDR type to give us the base, leave the
ip_and_mac stuff in contrib and then once Tom has a spare moment between
feedings *grin* we can get what is in the ip_and_mac stuff integrated?
Even if you just say, "Paul's is better, throw out ip_and_mac", that is
all we need.
That woudn't be wise...Paul's stuff focuses on the ip aspect of
ip_and_mac, not the the _and_mac part, which I feel to be as valuable,
especially in a DHCP environment :)
On Fri, Sep 04, 1998 at 02:15:55PM -0400, Bruce Momjian wrote:
plan to do that, but having recently become a father for the first
Congrats Mark. How do you feel getting up each night? :-)
Tom, Paul Vixie has said he can work on it, and in fact he already sent
in his version a month ago. I just had a baby three months ago, so I
know how busy you must be.
Oops, missed that. Congrats to you also, Bruce.
Michael
--
Michael Meskes meskes@online-club.de, meskes@debian.org
Go SF49ers! Go Rhein Fire! Use Debian GNU/Linux!
Bruce Momjian wrote:
When do you think you can you look over both versions, and send me
one good version to work with?
I've got something working right now, by taking what I was using, and
changing it to work the way Paul's code does, with some enhancements:
My code is ready for storing both IPV4 and IPV6 at the same time with
variable length storage in the data base, and it's got Aleksei's index
integration in place. The type name is still "ipaddr" -- but that's
easy to change, of course.
Even if you just say, "Paul's is better, throw out ip_and_mac", that
is all we need.
I am definitely *not* going to say "I can do this better than Vixie".
The way I feel about this right now is that Paul's code is better than
what I originally submitted, and better than Aleksei's improvements on
that code. However, what I currently run has certain improvements
over all of those versions, and I kind of like the way it's going...
I'll append it below. Take a look, and let me know what you think.
Oh, and a correction: Paul Vixie wrote:
the ip_and_mac type is host-only but has an unfortunate bridging
between layer 3 and layer 2.
No, it was (and is) two different types, just packaged in the same
directory because I thought they conceptually belonged together.
Anyway, I'm appending a shar of what I've got right now. It's only
minimally tested so far, and I'm *not* a professional programmer.
I'm basically just having a lot of fun with this, while it is at the
same time useful for me in my work, and if what I write can be of use
to others, that's great! :-)
-tih
--
Popularity is the hallmark of mediocrity. --Niles Crane, "Frasier"
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# Makefile
# README
# inet_net_ntop.c
# inet_net_pton.c
# ip.c
# ip.sql.in
# mac.c
# mac.h
# mac.sql.in
# test.sql
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X# PostgreSQL types for IP and MAC addresses
X#
X# $Id: Makefile,v 1.4 1998/09/08 12:23:30 tih Exp $
X
XPGINST=/usr/local/pgsql
XTARGET=/u/tih/databases
X
Xall: ip.so mac.so ip.sql mac.sql
X
Xip.so: ip.o inet_net_ntop.o inet_net_pton.o
X ld -Bshareable -o ip.so ip.o inet_net_ntop.o inet_net_pton.o
X
Xip.o: ip.c
X cc -g -O -fPIC -I${PGINST}/include -c ip.c
X
Xinet_net_ntop.o: inet_net_ntop.c
X cc -g -O -fPIC -c inet_net_ntop.c
X
Xinet_net_pton.o: inet_net_pton.c
X cc -g -O -fPIC -c inet_net_pton.c
X
Xmac.so: mac.o
X ld -Bshareable -o mac.so mac.o
X
Xmac.o: mac.c mac.h
X cc -g -O -fPIC -I${PGINST}/include -c mac.c
X
Xip.sql: ip.sql.in
X cat ip.sql.in | sed s@TARGET@${TARGET}/modules@ > ip.sql
X
Xmac.sql: mac.sql.in
X cat mac.sql.in | sed s@TARGET@${TARGET}/modules@ > mac.sql
X
Xinstall: ip.so mac.so
X install -c ip.sql ip.so mac.sql mac.so ${TARGET}/modules
X
Xclean:
X rm -f *.o *.so ip.sql mac.sql
X
XFILES=Makefile README inet_net_ntop.c inet_net_pton.c \
X ip.c ip.sql.in mac.c mac.h mac.sql.in test.sql
X
Xip+mac.shar: ${FILES}
X shar ${FILES} > ip+mac.shar
X
X#
X# eof
X#
END-of-Makefile
echo x - README
sed 's/^X//' >README << 'END-of-README'
XPostgreSQL type extensions for IP and MAC addresses.
X---------------------------------------------------
X
X$Id: README,v 1.2 1998/09/08 12:10:22 tih Exp $
X
XI needed to record IP and MAC level ethernet addresses in a data
Xbase, and I really didn't want to store them as plain strings, with
Xno enforced error checking, so I put together the accompanying code
Xas my first experiment with adding a data type to PostgreSQL. I
Xthen thought that this might be useful to others, both directly and
Xas a very simple example of how to do this sort of thing, so I
Xsubmitted it to the PostgreSQL project for inclusion in the contrib
Xdirectory. Since then, that directory has been modified to contain
XAleksei Roudnev's implementation, which is based on mine.
X
XFor those who have seen my previous contribution of these types, note
Xthat much has changed: I've modified the IP address type to work the
Xway Paul Vixie did with his CIDR type. In fact, I've pretty much just
Xstolen his solution, modifying it into my framework in such a way as
Xto facilitate the addition of IPV6 handling code in the future. I've
Xpretty much ignored Aleksei's C code, but I've added his SQL code to
Xenter the necessary operators into the various system tables needed to
Xmake the types indexable.
X
XIP addresses are implemented as a struct of fixed in-memory length,
Xbut variable on-disk storage size. For IPV4, it contains the address
Xfamily (AF_INET), the CIDR prefix length and four byte address. For
XIPV6, the address family will be different, and the address longer.
X
XThe external representation of an IP address generally looks like
X'158.37.96.15/32'. This address happens to be part of a subnet where
XI work; '158.37.96.0/24', which itself is a part of the larger subnet
Xallocated to our site, which is '158.37.96.0/21', which again, if you
Xgo by the old book, is part of the class "B" net '158.37.0.0/16'.
X
XInput and output functions are supplied, along with the "normal" <,
X<=, =, >=, > and <> operators, which all do what you expect. In
Xaddition, there are operators to check for networks or addresses being
Xsubnets of or addresses contained within other networks. << tests
Xwhether the left operand is contained within the right, <<= includes
Xequality, >> and >>= do the same things the opposite way.
X
XThe input and output functions use routines from Paul Vixie's BIND,
Xand I've snarfed the source files inet_net_ntop.c and inet_net_pton.c
Xdirectly from a recent distribution of that code. They are included
Xhere to avoid the need to fetch and install the BIND libraries to be
Xable to use this code. IANAL, but it looks from the copyright
Xmessages in the files as if this should be acceptable. Read the
Xdocumentation in inet_net_pton.c to see the legal input formats.
X
XMAC level ethernet addresses are implemented as a 6 byte struct that
Xcontains the address as unsigned chars. Several input forms are
Xaccepted; the following are all the same address: '08002b:010203',
X'08002b-010203', '0800.2b01.0203', '08-00-2b-01-02-03' and
X'08:00:2b:01:02:03'. Upper and lower case is accepted for the digits
X'a' through 'f'. Output is always in the latter of the given forms.
X
XAs with IP addresses, input and output functions are supplied as well
Xas the "normal" operators, which do what you expect. As an extra
Xfeature, a function macaddr_manuf() is defined, which returns the name
Xof the manufacturer as a string. This is currently held in a
Xhard-coded struct internal to the C module -- it might be smarter to
Xput this information into an actual data base table, and look up the
Xmanufacturer there.
X
XMany thanks to Aleksei Roudnev and Paul Vixie for their fine work!
X
XI don't know what changes are needed to the Makefile for other systems
Xthan the one I'm running (NetBSD 1.3), but anyway: to install on a BSD
Xsystem: fix the path names in the Makefile if you need to, then make,
Xmake install, slurp the SQL files into psql or whatever, and you're
Xoff. Enjoy!
X
XBergen, Norway, 1998-08-09, Tom Ivar Helbekkmo (tih@nhh.no).
END-of-README
echo x - inet_net_ntop.c
sed 's/^X//' >inet_net_ntop.c << 'END-of-inet_net_ntop.c'
X/*
X * Copyright (c) 1996 by Internet Software Consortium.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
X * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
X * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic const char rcsid[] = "$Id: inet_net_ntop.c,v 8.2 1996/08/08 06:54:44 vixie Exp $";
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X
X#ifdef SPRINTF_CHAR
X# define SPRINTF(x) strlen(sprintf/**/x)
X#else
X# define SPRINTF(x) ((size_t)sprintf x)
X#endif
X
Xstatic char * inet_net_ntop_ipv4 __P((const u_char *src, int bits,
X char *dst, size_t size));
X
X/*
X * char *
X * inet_net_ntop(af, src, bits, dst, size)
X * convert network number from network to presentation format.
X * generates CIDR style result always.
X * return:
X * pointer to dst, or NULL if an error occurred (check errno).
X * author:
X * Paul Vixie (ISC), July 1996
X */
Xchar *
Xinet_net_ntop(af, src, bits, dst, size)
X int af;
X const void *src;
X int bits;
X char *dst;
X size_t size;
X{
X switch (af) {
X case AF_INET:
X return (inet_net_ntop_ipv4(src, bits, dst, size));
X default:
X errno = EAFNOSUPPORT;
X return (NULL);
X }
X}
X
X/*
X * static char *
X * inet_net_ntop_ipv4(src, bits, dst, size)
X * convert IPv4 network number from network to presentation format.
X * generates CIDR style result always.
X * return:
X * pointer to dst, or NULL if an error occurred (check errno).
X * note:
X * network byte order assumed. this means 192.5.5.240/28 has
X * 0x11110000 in its fourth octet.
X * author:
X * Paul Vixie (ISC), July 1996
X */
Xstatic char *
Xinet_net_ntop_ipv4(src, bits, dst, size)
X const u_char *src;
X int bits;
X char *dst;
X size_t size;
X{
X char *odst = dst;
X char *t;
X u_int m;
X int b;
X
X if (bits < 0 || bits > 32) {
X errno = EINVAL;
X return (NULL);
X }
X if (bits == 0) {
X if (size < sizeof "0")
X goto emsgsize;
X *dst++ = '0';
X *dst = '\0';
X }
X
X /* Format whole octets. */
X for (b = bits / 8; b > 0; b--) {
X if (size < sizeof "255.")
X goto emsgsize;
X t = dst;
X dst += SPRINTF((dst, "%u", *src++));
X if (b > 1) {
X *dst++ = '.';
X *dst = '\0';
X }
X size -= (size_t)(dst - t);
X }
X
X /* Format partial octet. */
X b = bits % 8;
X if (b > 0) {
X if (size < sizeof ".255")
X goto emsgsize;
X t = dst;
X if (dst != odst)
X *dst++ = '.';
X m = ((1 << b) - 1) << (8 - b);
X dst += SPRINTF((dst, "%u", *src & m));
X size -= (size_t)(dst - t);
X }
X
X /* Format CIDR /width. */
X if (size < sizeof "/32")
X goto emsgsize;
X dst += SPRINTF((dst, "/%u", bits));
X return (odst);
X
X emsgsize:
X errno = EMSGSIZE;
X return (NULL);
X}
END-of-inet_net_ntop.c
echo x - inet_net_pton.c
sed 's/^X//' >inet_net_pton.c << 'END-of-inet_net_pton.c'
X/*
X * Copyright (c) 1996 by Internet Software Consortium.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
X * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
X * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic const char rcsid[] = "$Id: inet_net_pton.c,v 8.3 1996/11/11 06:36:52 vixie Exp $";
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <assert.h>
X#include <ctype.h>
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X
X#ifdef SPRINTF_CHAR
X# define SPRINTF(x) strlen(sprintf/**/x)
X#else
X# define SPRINTF(x) ((size_t)sprintf x)
X#endif
X
Xstatic int inet_net_pton_ipv4 __P((const char *src, u_char *dst,
X size_t size));
X
X/*
X * static int
X * inet_net_pton(af, src, dst, size)
X * convert network number from presentation to network format.
X * accepts hex octets, hex strings, decimal octets, and /CIDR.
X * "size" is in bytes and describes "dst".
X * return:
X * number of bits, either imputed classfully or specified with /CIDR,
X * or -1 if some failure occurred (check errno). ENOENT means it was
X * not a valid network specification.
X * author:
X * Paul Vixie (ISC), June 1996
X */
Xint
Xinet_net_pton(af, src, dst, size)
X int af;
X const char *src;
X void *dst;
X size_t size;
X{
X switch (af) {
X case AF_INET:
X return (inet_net_pton_ipv4(src, dst, size));
X default:
X errno = EAFNOSUPPORT;
X return (-1);
X }
X}
X
X/*
X * static int
X * inet_net_pton_ipv4(src, dst, size)
X * convert IPv4 network number from presentation to network format.
X * accepts hex octets, hex strings, decimal octets, and /CIDR.
X * "size" is in bytes and describes "dst".
X * return:
X * number of bits, either imputed classfully or specified with /CIDR,
X * or -1 if some failure occurred (check errno). ENOENT means it was
X * not an IPv4 network specification.
X * note:
X * network byte order assumed. this means 192.5.5.240/28 has
X * 0x11110000 in its fourth octet.
X * author:
X * Paul Vixie (ISC), June 1996
X */
Xstatic int
Xinet_net_pton_ipv4(src, dst, size)
X const char *src;
X u_char *dst;
X size_t size;
X{
X static const char
X xdigits[] = "0123456789abcdef",
X digits[] = "0123456789";
X int n, ch, tmp, dirty, bits;
X const u_char *odst = dst;
X
X ch = *src++;
X if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
X && isascii(src[1]) && isxdigit(src[1])) {
X /* Hexadecimal: Eat nybble string. */
X if (size <= 0)
X goto emsgsize;
X *dst = 0, dirty = 0;
X src++; /* skip x or X. */
X while ((ch = *src++) != '\0' &&
X isascii(ch) && isxdigit(ch)) {
X if (isupper(ch))
X ch = tolower(ch);
X n = strchr(xdigits, ch) - xdigits;
X assert(n >= 0 && n <= 15);
X *dst |= n;
X if (!dirty++)
X *dst <<= 4;
X else if (size-- > 0)
X *++dst = 0, dirty = 0;
X else
X goto emsgsize;
X }
X if (dirty)
X size--;
X } else if (isascii(ch) && isdigit(ch)) {
X /* Decimal: eat dotted digit string. */
X for (;;) {
X tmp = 0;
X do {
X n = strchr(digits, ch) - digits;
X assert(n >= 0 && n <= 9);
X tmp *= 10;
X tmp += n;
X if (tmp > 255)
X goto enoent;
X } while ((ch = *src++) != '\0' &&
X isascii(ch) && isdigit(ch));
X if (size-- <= 0)
X goto emsgsize;
X *dst++ = (u_char) tmp;
X if (ch == '\0' || ch == '/')
X break;
X if (ch != '.')
X goto enoent;
X ch = *src++;
X if (!isascii(ch) || !isdigit(ch))
X goto enoent;
X }
X } else
X goto enoent;
X
X bits = -1;
X if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
X /* CIDR width specifier. Nothing can follow it. */
X ch = *src++; /* Skip over the /. */
X bits = 0;
X do {
X n = strchr(digits, ch) - digits;
X assert(n >= 0 && n <= 9);
X bits *= 10;
X bits += n;
X } while ((ch = *src++) != '\0' &&
X isascii(ch) && isdigit(ch));
X if (ch != '\0')
X goto enoent;
X if (bits > 32)
X goto emsgsize;
X }
X
X /* Firey death and destruction unless we prefetched EOS. */
X if (ch != '\0')
X goto enoent;
X
X /* If nothing was written to the destination, we found no address. */
X if (dst == odst)
X goto enoent;
X /* If no CIDR spec was given, infer width from net class. */
X if (bits == -1) {
X if (*odst >= 240) /* Class E */
X bits = 32;
X else if (*odst >= 224) /* Class D */
X bits = 4;
X else if (*odst >= 192) /* Class C */
X bits = 24;
X else if (*odst >= 128) /* Class B */
X bits = 16;
X else /* Class A */
X bits = 8;
X /* If imputed mask is narrower than specified octets, widen. */
X if (bits >= 8 && bits < ((dst - odst) * 8))
X bits = (dst - odst) * 8;
X }
X /* Extend network to cover the actual mask. */
X while (bits > ((dst - odst) * 8)) {
X if (size-- <= 0)
X goto emsgsize;
X *dst++ = '\0';
X }
X return (bits);
X
X enoent:
X errno = ENOENT;
X return (-1);
X
X emsgsize:
X errno = EMSGSIZE;
X return (-1);
X}
END-of-inet_net_pton.c
echo x - ip.c
sed 's/^X//' >ip.c << 'END-of-ip.c'
X/*
X * PostgreSQL type definitions for IP addresses. This
X * is for IP V4 CIDR notation, but prepared for V6: just
X * add the necessary bits where the comments indicate.
X *
X * $Id: ip.c,v 1.2 1998/09/08 12:10:36 tih Exp $
X */
X
X#include <sys/types.h>
X#include <sys/socket.h>
X
X#include <stdio.h>
X#include <errno.h>
X
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <postgres.h>
X#include <utils/palloc.h>
X
X/*
X * This is the internal storage format for IP addresses:
X */
X
Xtypedef struct {
X unsigned char family;
X unsigned char bits;
X union {
X u_int32_t ipv4_addr; /* network byte order */
X /* add IPV6 address type here */
X } addr;
X} ipaddr_struct;
X
Xtypedef struct varlena ipaddr;
X
X/*
X * Access macros. Add IPV6 support.
X */
X
X#define ip_addrsize(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->family == AF_INET ? 4 : -1)
X
X#define ip_family(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->family)
X
X#define ip_bits(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->bits)
X
X#define ip_v4addr(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->addr.ipv4_addr)
X
X/*
X * Various forward declarations:
X */
X
Xipaddr *ipaddr_in(char *str);
Xchar *ipaddr_out(ipaddr *addr);
X
Xbool ipaddr_lt(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_le(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_eq(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_ge(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_gt(ipaddr *a1, ipaddr *a2);
X
Xbool ipaddr_ne(ipaddr *a1, ipaddr *a2);
X
Xbool ipaddr_sub(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_subeq(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_sup(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_supeq(ipaddr *a1, ipaddr *a2);
X
Xint4 ipaddr_cmp(ipaddr *a1, ipaddr *a2);
X
Xint v4bitncmp(u_int32_t a1, u_int32_t a2, int bits);
X
X/*
X * IP address reader.
X */
X
Xipaddr *ipaddr_in(char *src) {
X int bits;
X ipaddr *dst;
X
X dst = palloc(VARHDRSZ + sizeof(ipaddr_struct));
X if (dst == NULL) {
X elog(ERROR, "unable to allocate memory in ipaddr_in()");
X return(NULL);
X }
X /* First, try for an IP V4 address: */
X ip_family(dst) = AF_INET;
X bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst), ip_addrsize(dst));
X if ((bits < 0) || (bits > 32)) {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "could not parse \"%s\"", src);
X pfree(dst);
X return(NULL);
X }
X VARSIZE(dst) = VARHDRSZ
X + ((char *)&ip_v4addr(dst) - (char *)VARDATA(dst))
X + ip_addrsize(dst);
X ip_bits(dst) = bits;
X return(dst);
X}
X
X/*
X * IP address output function.
X */
X
Xchar *ipaddr_out(ipaddr *src) {
X char *dst, tmp[sizeof("255.255.255.255/32")];
X
X if (ip_family(src) == AF_INET) {
X /* It's an IP V4 address: */
X if (inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src),
X tmp, sizeof(tmp)) < 0) {
X elog(ERROR, "unable to print address (%s)", strerror(errno));
X return(NULL);
X }
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "unknown address family (%d)", ip_family(src));
X return(NULL);
X }
X dst = palloc(strlen(tmp) + 1);
X if (dst == NULL) {
X elog(ERROR, "unable to allocate memory in ipaddr_out()");
X return(NULL);
X }
X strcpy(dst, tmp);
X return(dst);
X}
X
X/*
X * Boolean tests for magnitude. Add V4/V6 testing!
X */
X
Xbool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
X return((order < 0) || ((order == 0) && (ip_bits(a1) < ip_bits(a2))));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_le(ipaddr *a1, ipaddr *a2) {
X return(ipaddr_lt(a1, a2) || ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) == ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
X return(ipaddr_gt(a1, a2) || ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
X return((order > 0) || ((order == 0) && (ip_bits(a1) > ip_bits(a2))));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
X return(!ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_sub(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) > ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_subeq(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) >= ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_sup(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) < ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_supeq(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) <= ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
X/*
X * Comparison function for sorting. Add V4/V6 testing!
X */
X
Xint4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
X if (ntohl(ip_v4addr(a1)) < ntohl(ip_v4addr(a2))) {
X return(-1);
X } else if (ntohl(ip_v4addr(a1)) > ntohl(ip_v4addr(a2))) {
X return(1);
X }
X return 0;
X}
X
X/*
X * Bitwise comparison for V4 addresses. Add V6 implementation!
X */
X
Xint v4bitncmp(u_int32_t a1, u_int32_t a2, int bits) {
X unsigned long mask = 0;
X int i;
X for (i = 0; i < bits; i++) {
X mask = (mask >> 1) | 0x80000000;
X }
X a1 = ntohl(a1);
X a2 = ntohl(a2);
X if ((a1 & mask) < (a2 & mask)) {
X return(-1);
X } else if ((a1 & mask) > (a2 & mask)) {
X return(1);
X }
X return(0);
X}
X
X/*
X * eof
X */
END-of-ip.c
echo x - ip.sql.in
sed 's/^X//' >ip.sql.in << 'END-of-ip.sql.in'
X--
X-- PostgreSQL code for IP addresses.
X--
X-- $Id: ip.sql.in,v 1.2 1998/09/08 12:10:45 tih Exp $
X--
X
Xload 'TARGET/ip.so';
X
X--
X-- Input and output functions and the type itself:
X--
X
Xcreate function ipaddr_in(opaque)
X returns opaque
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_out(opaque)
X returns opaque
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate type ipaddr (
X internallength = variable,
X externallength = variable,
X input = ipaddr_in,
X output = ipaddr_out
X);
X
X--
X-- The various boolean tests:
X--
X
Xcreate function ipaddr_lt(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_le(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_eq(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_ge(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_gt(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_ne(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_sub(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_subeq(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_sup(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_supeq(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_cmp(ipaddr, ipaddr)
X returns int4
X as 'TARGET/ip.so'
X language 'c';
X
X--
X-- Now the operators. Note how some of the parameters to some
X-- of the 'create operator' commands are commented out. This
X-- is because they reference as yet undefined operators, and
X-- will be implicitly defined when those are, further down.
X--
X
Xcreate operator < (
X leftarg = ipaddr,
X rightarg = ipaddr,
X-- negator = >=,
X restrict = intltsel,
X join = intltjoinsel,
X procedure = ipaddr_lt
X);
X
Xcreate operator <= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X-- negator = >,
X restrict = intltsel,
X join = intltjoinsel,
X procedure = ipaddr_le
X);
X
Xcreate operator = (
X leftarg = ipaddr,
X rightarg = ipaddr,
X commutator = =,
X-- negator = <>,
X restrict = eqsel,
X join = eqjoinsel,
X procedure = ipaddr_eq
X);
X
Xcreate operator >= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X negator = <,
X restrict = intgtsel,
X join = intgtjoinsel,
X procedure = ipaddr_ge
X);
X
Xcreate operator > (
X leftarg = ipaddr,
X rightarg = ipaddr,
X negator = <=,
X restrict = intgtsel,
X join = intgtjoinsel,
X procedure = ipaddr_gt
X);
X
Xcreate operator <> (
X leftarg = ipaddr,
X rightarg = ipaddr,
X negator = =,
X restrict = neqsel,
X join = neqjoinsel,
X procedure = ipaddr_ne
X);
X
Xcreate operator << (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_sub
X);
X
Xcreate operator <<= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_subeq
X);
X
Xcreate operator >> (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_sup
X);
X
Xcreate operator >>= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_supeq
X);
X
X--
X-- Finally, make it possible to do btree indexing on IP addresses.
X--
X
Xbegin;
X
Xcreate table tmp_op (oprname name, opi int2);
X
Xinsert into tmp_op values ('<', 1);
Xinsert into tmp_op values ('<=', 2);
Xinsert into tmp_op values ('=', 3);
Xinsert into tmp_op values ('>=', 4);
Xinsert into tmp_op values ('>', 5);
X
Xdelete from pg_opclass
X where opcname = 'ipaddr_ops';
X
Xinsert into pg_opclass (opcname, opcdeftype)
X select 'ipaddr_ops', oid from pg_type
X where typname = 'ipaddr';
X
Xselect o.oid as opoid, o.oprname
X into table ipaddr_tmp
X from pg_operator o, pg_type t
X where o.oprleft = t.oid and
X o.oprright = t.oid and
X t.typname = 'ipaddr';
X
Xinsert into pg_amop (amopid, amopclaid, amopopr, amopstrategy,
X amopselect, amopnpages)
X select am.oid, opcl.oid, c.opoid, t.opi,
X 'btreesel'::regproc, 'btreenpage'::regproc
X from pg_am am, pg_opclass opcl, ipaddr_tmp c, tmp_op t
X where t.oprname = c.oprname and
X amname = 'btree' and opcname = 'ipaddr_ops';
X
Xinsert into pg_amproc (amid, amopclaid, amproc, amprocnum)
X select pgam.oid, opc.oid, prc.oid, '1'::int2
X from pg_am pgam, pg_opclass opc, pg_proc prc
X where prc.proname = 'ipaddr_cmp' and
X pgam.amname = 'btree' and
X opc.opcname = 'ipaddr_ops';
X
Xdrop table tmp_op;
Xdrop table ipaddr_tmp;
X
Xcommit;
X
X--
X-- eof
X--
END-of-ip.sql.in
echo x - mac.c
sed 's/^X//' >mac.c << 'END-of-mac.c'
X/*
X * PostgreSQL type definitions for MAC addresses.
X *
X * $Id: mac.c,v 1.1 1998/09/07 12:31:18 tih Exp $
X */
X
X#include <stdio.h>
X
X#include <postgres.h>
X#include <utils/palloc.h>
X
X#include "mac.h"
X
X/*
X * This is the internal storage format for MAC addresses:
X */
X
Xtypedef struct macaddr {
X unsigned char a;
X unsigned char b;
X unsigned char c;
X unsigned char d;
X unsigned char e;
X unsigned char f;
X} macaddr;
X
X/*
X * Various forward declarations:
X */
X
Xmacaddr *macaddr_in(char *str);
Xchar *macaddr_out(macaddr *addr);
X
Xbool macaddr_lt(macaddr *a1, macaddr *a2);
Xbool macaddr_le(macaddr *a1, macaddr *a2);
Xbool macaddr_eq(macaddr *a1, macaddr *a2);
Xbool macaddr_ge(macaddr *a1, macaddr *a2);
Xbool macaddr_gt(macaddr *a1, macaddr *a2);
X
Xbool macaddr_ne(macaddr *a1, macaddr *a2);
X
Xint4 macaddr_cmp(macaddr *a1, macaddr *a2);
X
Xtext *macaddr_manuf(macaddr *addr);
X
X/*
X * Utility macros used for sorting and comparing:
X */
X
X#define hibits(addr) \
X ((unsigned long)((addr->a<<16)|(addr->b<<8)|(addr->c)))
X
X#define lobits(addr) \
X ((unsigned long)((addr->c<<16)|(addr->e<<8)|(addr->f)))
X
X/*
X * MAC address reader. Accepts several common notations.
X */
X
Xmacaddr *macaddr_in(char *str) {
X int a, b, c, d, e, f;
X macaddr *result;
X int count;
X
X if (strlen(str) > 0) {
X
X count = sscanf(str, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%x-%x-%x-%x-%x-%x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%2x%2x%2x:%2x%2x%2x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%2x%2x%2x-%2x%2x%2x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x", &a, &b, &c, &d, &e, &f);
X
X if (count != 6) {
X elog(ERROR, "macaddr_in: error in parsing \"%s\"", str);
X return(NULL);
X }
X
X if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
X (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
X (e < 0) || (e > 255) || (f < 0) || (f > 255)) {
X elog(ERROR, "macaddr_in: illegal address \"%s\"", str);
X return(NULL);
X }
X } else {
X a = b = c = d = e = f = 0; /* special case for missing address */
X }
X
X result = (macaddr *)palloc(sizeof(macaddr));
X
X result->a = a;
X result->b = b;
X result->c = c;
X result->d = d;
X result->e = e;
X result->f = f;
X
X return(result);
X}
X
X/*
X * MAC address output function. Fixed format.
X */
X
Xchar *macaddr_out(macaddr *addr) {
X char *result;
X
X if (addr == NULL)
X return(NULL);
X
X result = (char *)palloc(32);
X
X if ((hibits(addr) > 0) || (lobits(addr) > 0)) {
X sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x",
X addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
X } else {
X result[0] = 0; /* special case for missing address */
X }
X return(result);
X}
X
X/*
X * Boolean tests.
X */
X
Xbool macaddr_lt(macaddr *a1, macaddr *a2) {
X return((hibits(a1) < hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) < lobits(a2)));
X};
X
Xbool macaddr_le(macaddr *a1, macaddr *a2) {
X return((hibits(a1) < hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) <= lobits(a2)));
X};
X
Xbool macaddr_eq(macaddr *a1, macaddr *a2) {
X return ((hibits(a1) == hibits(a2)) && (lobits(a1) == lobits(a2)));
X};
X
Xbool macaddr_ge(macaddr *a1, macaddr *a2) {
X return((hibits(a1) > hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) >= lobits(a2)));
X};
X
Xbool macaddr_gt(macaddr *a1, macaddr *a2) {
X return((hibits(a1) > hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) > lobits(a2)));
X};
X
Xbool macaddr_ne(macaddr *a1, macaddr *a2) {
X return ((hibits(a1) != hibits(a2)) || (lobits(a1) != lobits(a2)));
X};
X
X/*
X * Comparison function for sorting:
X */
X
Xint4 macaddr_cmp(macaddr *a1, macaddr *a2) {
X if (hibits(a1) < hibits(a2))
X return -1;
X else if (hibits(a1) > hibits(a2))
X return 1;
X else if (lobits(a1) < lobits(a2))
X return -1;
X else if (lobits(a1) > lobits(a2))
X return 1;
X else
X return 0;
X}
X
X/*
X * The special manufacturer fetching function. See "mac.h".
X */
X
Xtext *macaddr_manuf(macaddr *addr) {
X manufacturer *manuf;
X int length;
X text *result;
X
X for (manuf = manufacturers; manuf->name != NULL; manuf++) {
X if ((manuf->a == addr->a) &&
X (manuf->b == addr->b) &&
X (manuf->c == addr->c))
X break;
X }
X if (manuf->name == NULL) {
X result = palloc(VARHDRSZ + 1);
X memset(result, 0, VARHDRSZ + 1);
X VARSIZE(result) = VARHDRSZ + 1;
X } else {
X length = strlen(manuf->name) + 1;
X result = palloc(length + VARHDRSZ);
X memset(result, 0, length + VARHDRSZ);
X VARSIZE(result) = length + VARHDRSZ;
X memcpy(VARDATA(result), manuf->name, length);
X }
X return result;
X}
X
X/*
X * eof
X */
END-of-mac.c
echo x - mac.h
sed 's/^X//' >mac.h << 'END-of-mac.h'
X/*
X * PostgreSQL type definitions for MAC addresses.
X *
X * $Id: mac.h,v 1.1 1998/09/07 12:31:22 tih Exp $
X */
X
Xtypedef struct manufacturer {
X unsigned char a;
X unsigned char b;
X unsigned char c;
X char *name;
X} manufacturer;
X
Xmanufacturer manufacturers[] = {
X {0x00, 0x00, 0x0C, "Cisco"},
X {0x00, 0x00, 0x0E, "Fujitsu"},
X {0x00, 0x00, 0x0F, "NeXT"},
X {0x00, 0x00, 0x10, "Sytek"},
X {0x00, 0x00, 0x1D, "Cabletron"},
X {0x00, 0x00, 0x20, "DIAB"},
X {0x00, 0x00, 0x22, "Visual Technology"},
X {0x00, 0x00, 0x2A, "TRW"},
X {0x00, 0x00, 0x32, "GPT Limited"},
X {0x00, 0x00, 0x5A, "S & Koch"},
X {0x00, 0x00, 0x5E, "IANA"},
X {0x00, 0x00, 0x65, "Network General"},
X {0x00, 0x00, 0x6B, "MIPS"},
X {0x00, 0x00, 0x77, "MIPS"},
X {0x00, 0x00, 0x7A, "Ardent"},
X {0x00, 0x00, 0x89, "Cayman Systems"},
X {0x00, 0x00, 0x93, "Proteon"},
X {0x00, 0x00, 0x9F, "Ameristar Technology"},
X {0x00, 0x00, 0xA2, "Wellfleet"},
X {0x00, 0x00, 0xA3, "Network Application Technology"},
X {0x00, 0x00, 0xA6, "Network General"},
X {0x00, 0x00, 0xA7, "NCD"},
X {0x00, 0x00, 0xA9, "Network Systems"},
X {0x00, 0x00, 0xAA, "Xerox"},
X {0x00, 0x00, 0xB3, "CIMLinc"},
X {0x00, 0x00, 0xB7, "Dove Fastnet"},
X {0x00, 0x00, 0xBC, "Allen-Bradley"},
X {0x00, 0x00, 0xC0, "Western Digital"},
X {0x00, 0x00, 0xC5, "Farallon"},
X {0x00, 0x00, 0xC6, "Hewlett-Packard"},
X {0x00, 0x00, 0xC8, "Altos"},
X {0x00, 0x00, 0xC9, "Emulex"},
X {0x00, 0x00, 0xD7, "Dartmouth College"},
X {0x00, 0x00, 0xD8, "3Com (?)"},
X {0x00, 0x00, 0xDD, "Gould"},
X {0x00, 0x00, 0xDE, "Unigraph"},
X {0x00, 0x00, 0xE2, "Acer Counterpoint"},
X {0x00, 0x00, 0xEF, "Alantec"},
X {0x00, 0x00, 0xFD, "High Level Hardware"},
X {0x00, 0x01, 0x02, "BBN internal usage"},
X {0x00, 0x20, 0xAF, "3Com"},
X {0x00, 0x17, 0x00, "Kabel"},
X {0x00, 0x80, 0x64, "Wyse Technology"},
X {0x00, 0x80, 0x2B, "IMAC (?)"},
X {0x00, 0x80, 0x2D, "Xylogics, Inc."},
X {0x00, 0x80, 0x8C, "Frontier Software Development"},
X {0x00, 0x80, 0xC2, "IEEE 802.1 Committee"},
X {0x00, 0x80, 0xD3, "Shiva"},
X {0x00, 0xAA, 0x00, "Intel"},
X {0x00, 0xDD, 0x00, "Ungermann-Bass"},
X {0x00, 0xDD, 0x01, "Ungermann-Bass"},
X {0x02, 0x07, 0x01, "Racal InterLan"},
X {0x02, 0x04, 0x06, "BBN internal usage"},
X {0x02, 0x60, 0x86, "Satelcom MegaPac"},
X {0x02, 0x60, 0x8C, "3Com"},
X {0x02, 0xCF, 0x1F, "CMC"},
X {0x08, 0x00, 0x02, "3Com"},
X {0x08, 0x00, 0x03, "ACC"},
X {0x08, 0x00, 0x05, "Symbolics"},
X {0x08, 0x00, 0x08, "BBN"},
X {0x08, 0x00, 0x09, "Hewlett-Packard"},
X {0x08, 0x00, 0x0A, "Nestar Systems"},
X {0x08, 0x00, 0x0B, "Unisys"},
X {0x08, 0x00, 0x11, "Tektronix"},
X {0x08, 0x00, 0x14, "Excelan"},
X {0x08, 0x00, 0x17, "NSC"},
X {0x08, 0x00, 0x1A, "Data General"},
X {0x08, 0x00, 0x1B, "Data General"},
X {0x08, 0x00, 0x1E, "Apollo"},
X {0x08, 0x00, 0x20, "Sun"},
X {0x08, 0x00, 0x22, "NBI"},
X {0x08, 0x00, 0x25, "CDC"},
X {0x08, 0x00, 0x26, "Norsk Data"},
X {0x08, 0x00, 0x27, "PCS Computer Systems GmbH"},
X {0x08, 0x00, 0x28, "Texas Instruments"},
X {0x08, 0x00, 0x2B, "DEC"},
X {0x08, 0x00, 0x2E, "Metaphor"},
X {0x08, 0x00, 0x2F, "Prime Computer"},
X {0x08, 0x00, 0x36, "Intergraph"},
X {0x08, 0x00, 0x37, "Fujitsu-Xerox"},
X {0x08, 0x00, 0x38, "Bull"},
X {0x08, 0x00, 0x39, "Spider Systems"},
X {0x08, 0x00, 0x41, "DCA Digital Comm. Assoc."},
X {0x08, 0x00, 0x45, "Xylogics (?)"},
X {0x08, 0x00, 0x46, "Sony"},
X {0x08, 0x00, 0x47, "Sequent"},
X {0x08, 0x00, 0x49, "Univation"},
X {0x08, 0x00, 0x4C, "Encore"},
X {0x08, 0x00, 0x4E, "BICC"},
X {0x08, 0x00, 0x56, "Stanford University"},
X {0x08, 0x00, 0x58, "DECsystem 20 (?)"},
X {0x08, 0x00, 0x5A, "IBM"},
X {0x08, 0x00, 0x67, "Comdesign"},
X {0x08, 0x00, 0x68, "Ridge"},
X {0x08, 0x00, 0x69, "Silicon Graphics"},
X {0x08, 0x00, 0x6E, "Concurrent"},
X {0x08, 0x00, 0x75, "DDE"},
X {0x08, 0x00, 0x7C, "Vitalink"},
X {0x08, 0x00, 0x80, "XIOS"},
X {0x08, 0x00, 0x86, "Imagen/QMS"},
X {0x08, 0x00, 0x87, "Xyplex"},
X {0x08, 0x00, 0x89, "Kinetics"},
X {0x08, 0x00, 0x8B, "Pyramid"},
X {0x08, 0x00, 0x8D, "XyVision"},
X {0x08, 0x00, 0x90, "Retix Inc"},
X {0x48, 0x44, 0x53, "HDS (?)"},
X {0x80, 0x00, 0x10, "AT&T"},
X {0xAA, 0x00, 0x00, "DEC"},
X {0xAA, 0x00, 0x01, "DEC"},
X {0xAA, 0x00, 0x02, "DEC"},
X {0xAA, 0x00, 0x03, "DEC"},
X {0xAA, 0x00, 0x04, "DEC"},
X {0x00, 0x00, 0x00, NULL}
X};
X
X/*
X * eof
X */
END-of-mac.h
echo x - mac.sql.in
sed 's/^X//' >mac.sql.in << 'END-of-mac.sql.in'
X--
X-- PostgreSQL code for MAC addresses.
X--
X-- $Id: mac.sql.in,v 1.2 1998/09/08 12:11:18 tih Exp $
X--
X
Xload 'TARGET/mac.so';
X
X--
X-- Input and output functions and the type itself:
X--
X
Xcreate function macaddr_in(opaque)
X returns opaque
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_out(opaque)
X returns opaque
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate type macaddr (
X internallength = 6,
X externallength = variable,
X input = macaddr_in,
X output = macaddr_out
X);
X
X--
X-- The boolean tests:
X--
X
Xcreate function macaddr_lt(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_le(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_eq(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_ge(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_gt(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_ne(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_cmp(macaddr, macaddr)
X returns int4
X as 'TARGET/mac.so'
X language 'c';
X
X--
X-- Now the operators. Note how some of the parameters to some
X-- of the 'create operator' commands are commented out. This
X-- is because they reference as yet undefined operators, and
X-- will be implicitly defined when those are, further down.
X--
X
Xcreate operator < (
X leftarg = macaddr,
X rightarg = macaddr,
X-- negator = >=,
X procedure = macaddr_lt
X);
X
Xcreate operator <= (
X leftarg = macaddr,
X rightarg = macaddr,
X-- negator = >,
X procedure = macaddr_le
X);
X
Xcreate operator = (
X leftarg = macaddr,
X rightarg = macaddr,
X commutator = =,
X-- negator = <>,
X procedure = macaddr_eq
X);
X
Xcreate operator >= (
X leftarg = macaddr,
X rightarg = macaddr,
X negator = <,
X procedure = macaddr_ge
X);
X
Xcreate operator > (
X leftarg = macaddr,
X rightarg = macaddr,
X negator = <=,
X procedure = macaddr_gt
X);
X
Xcreate operator <> (
X leftarg = macaddr,
X rightarg = macaddr,
X negator = =,
X procedure = macaddr_ne
X);
X
X--
X-- Finally, the special manufacurer matching function:
X--
X
Xcreate function macaddr_manuf(macaddr)
X returns text
X as 'TARGET/mac.so'
X language 'c';
X
X--
X-- Finally, make it possible to do btree indexing on MAC addresses.
X--
X
Xbegin;
X
Xcreate table tmp_op (oprname name, opi int2);
X
Xinsert into tmp_op values ('<', 1);
Xinsert into tmp_op values ('<=', 2);
Xinsert into tmp_op values ('=', 3);
Xinsert into tmp_op values ('>=', 4);
Xinsert into tmp_op values ('>', 5);
X
Xdelete from pg_opclass
X where opcname = 'macaddr_ops';
X
Xinsert into pg_opclass (opcname, opcdeftype)
X select 'macaddr_ops', oid from pg_type
X where typname = 'macaddr';
X
Xselect o.oid as opoid, o.oprname
X into table macaddr_tmp
X from pg_operator o, pg_type t
X where o.oprleft = t.oid and
X o.oprright = t.oid and
X t.typname = 'macaddr';
X
Xinsert into pg_amop (amopid, amopclaid, amopopr, amopstrategy,
X amopselect, amopnpages)
X select am.oid, opcl.oid, c.opoid, t.opi,
X 'btreesel'::regproc, 'btreenpage'::regproc
X from pg_am am, pg_opclass opcl, macaddr_tmp c, tmp_op t
X where t.oprname = c.oprname and
X amname = 'btree' and opcname = 'macaddr_ops';
X
Xinsert into pg_amproc (amid, amopclaid, amproc, amprocnum)
X select pgam.oid, opc.oid, prc.oid, '1'::int2
X from pg_am pgam, pg_opclass opc, pg_proc prc
X where prc.proname = 'macaddr_cmp' and
X pgam.amname = 'btree' and
X opc.opcname = 'macaddr_ops';
X
Xdrop table tmp_op;
Xdrop table macaddr_tmp;
X
Xcommit;
X
X--
X-- eof
X--
END-of-mac.sql.in
echo x - test.sql
sed 's/^X//' >test.sql << 'END-of-test.sql'
X--
X-- A quick test of the IP address code
X--
X-- $Id: test.sql,v 1.2 1998/09/08 12:11:34 tih Exp $
X--
X
X-- temporary table:
Xcreate table addresses (address ipaddr);
X
X-- sample data from two subnets:
Xinsert into addresses values ('158.37.96.15');
Xinsert into addresses values ('158.37.96.16');
Xinsert into addresses values ('158.37.96.17');
Xinsert into addresses values ('158.37.97.15');
Xinsert into addresses values ('158.37.97.16');
Xinsert into addresses values ('158.37.97.17');
Xinsert into addresses values ('158.37.98.15');
Xinsert into addresses values ('158.37.98.16');
Xinsert into addresses values ('158.37.98.17');
Xinsert into addresses values ('158.37.96.150');
Xinsert into addresses values ('158.37.96.160');
Xinsert into addresses values ('158.37.96.170');
Xinsert into addresses values ('158.37.97.150');
Xinsert into addresses values ('158.37.97.160');
Xinsert into addresses values ('158.37.97.170');
Xinsert into addresses values ('158.37.98.150');
Xinsert into addresses values ('158.37.98.160');
Xinsert into addresses values ('158.37.98.170');
X
X-- show them all:
Xselect * from addresses;
X
X-- select the ones in subnet 96:
Xselect * from addresses where address << '158.37.96.0/24';
X
X-- select the ones not in subnet 96:
Xselect * from addresses where not address << '158.37.96.0/24';
X
X-- select the ones in subnet 97:
Xselect * from addresses where address << '158.37.97.0/24';
X
X-- select the ones not in subnet 97:
Xselect * from addresses where not address << '158.37.97.0/24';
X
X-- select the ones in subnet 96 or 97, sorted:
Xselect * from addresses where address << '158.37.96.0/23'
X order by address;
X
X-- now some networks:
Xcreate table networks (network ipaddr);
X
X-- now the subnets mentioned above:
Xinsert into networks values ('158.37.96.0/24');
Xinsert into networks values ('158.37.97.0/24');
Xinsert into networks values ('158.37.98.0/24');
X
X-- select matching pairs of addresses and containing nets:
Xselect address, network from addresses, networks
X where address << network;
X
X-- tidy up:
Xdrop table addresses;
Xdrop table networks;
X
X--
X-- eof
X--
END-of-test.sql
exit
Import Notes
Reply to msg id not found: BruceMomjiansmessageofFri4Sep1998141555-0400EDT
Good. Keep going and let me know when you want it added. We need to
fix the other problems first before merging your stuff in, so we have
some time. I assume you are merging Paul's code with your own, to make
a "best of both worlds" group of types.
Bruce Momjian wrote:
When do you think you can you look over both versions, and send me
one good version to work with?I've got something working right now, by taking what I was using, and
changing it to work the way Paul's code does, with some enhancements:
My code is ready for storing both IPV4 and IPV6 at the same time with
variable length storage in the data base, and it's got Aleksei's index
integration in place. The type name is still "ipaddr" -- but that's
easy to change, of course.Even if you just say, "Paul's is better, throw out ip_and_mac", that
is all we need.I am definitely *not* going to say "I can do this better than Vixie".
The way I feel about this right now is that Paul's code is better than
what I originally submitted, and better than Aleksei's improvements on
that code. However, what I currently run has certain improvements
over all of those versions, and I kind of like the way it's going...I'll append it below. Take a look, and let me know what you think.
Oh, and a correction: Paul Vixie wrote:
the ip_and_mac type is host-only but has an unfortunate bridging
between layer 3 and layer 2.No, it was (and is) two different types, just packaged in the same
directory because I thought they conceptually belonged together.Anyway, I'm appending a shar of what I've got right now. It's only
minimally tested so far, and I'm *not* a professional programmer.
I'm basically just having a lot of fun with this, while it is at the
same time useful for me in my work, and if what I write can be of use
to others, that's great! :-)-tih
--
Popularity is the hallmark of mediocrity. --Niles Crane, "Frasier"# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# Makefile
# README
# inet_net_ntop.c
# inet_net_pton.c
# ip.c
# ip.sql.in
# mac.c
# mac.h
# mac.sql.in
# test.sql
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X# PostgreSQL types for IP and MAC addresses
X#
X# $Id: Makefile,v 1.4 1998/09/08 12:23:30 tih Exp $
X
XPGINST=/usr/local/pgsql
XTARGET=/u/tih/databases
X
Xall: ip.so mac.so ip.sql mac.sql
X
Xip.so: ip.o inet_net_ntop.o inet_net_pton.o
X ld -Bshareable -o ip.so ip.o inet_net_ntop.o inet_net_pton.o
X
Xip.o: ip.c
X cc -g -O -fPIC -I${PGINST}/include -c ip.c
X
Xinet_net_ntop.o: inet_net_ntop.c
X cc -g -O -fPIC -c inet_net_ntop.c
X
Xinet_net_pton.o: inet_net_pton.c
X cc -g -O -fPIC -c inet_net_pton.c
X
Xmac.so: mac.o
X ld -Bshareable -o mac.so mac.o
X
Xmac.o: mac.c mac.h
X cc -g -O -fPIC -I${PGINST}/include -c mac.c
X
Xip.sql: ip.sql.in
X cat ip.sql.in | sed s@TARGET@${TARGET}/modules@ > ip.sql
X
Xmac.sql: mac.sql.in
X cat mac.sql.in | sed s@TARGET@${TARGET}/modules@ > mac.sql
X
Xinstall: ip.so mac.so
X install -c ip.sql ip.so mac.sql mac.so ${TARGET}/modules
X
Xclean:
X rm -f *.o *.so ip.sql mac.sql
X
XFILES=Makefile README inet_net_ntop.c inet_net_pton.c \
X ip.c ip.sql.in mac.c mac.h mac.sql.in test.sql
X
Xip+mac.shar: ${FILES}
X shar ${FILES} > ip+mac.shar
X
X#
X# eof
X#
END-of-Makefile
echo x - README
sed 's/^X//' >README << 'END-of-README'
XPostgreSQL type extensions for IP and MAC addresses.
X---------------------------------------------------
X
X$Id: README,v 1.2 1998/09/08 12:10:22 tih Exp $
X
XI needed to record IP and MAC level ethernet addresses in a data
Xbase, and I really didn't want to store them as plain strings, with
Xno enforced error checking, so I put together the accompanying code
Xas my first experiment with adding a data type to PostgreSQL. I
Xthen thought that this might be useful to others, both directly and
Xas a very simple example of how to do this sort of thing, so I
Xsubmitted it to the PostgreSQL project for inclusion in the contrib
Xdirectory. Since then, that directory has been modified to contain
XAleksei Roudnev's implementation, which is based on mine.
X
XFor those who have seen my previous contribution of these types, note
Xthat much has changed: I've modified the IP address type to work the
Xway Paul Vixie did with his CIDR type. In fact, I've pretty much just
Xstolen his solution, modifying it into my framework in such a way as
Xto facilitate the addition of IPV6 handling code in the future. I've
Xpretty much ignored Aleksei's C code, but I've added his SQL code to
Xenter the necessary operators into the various system tables needed to
Xmake the types indexable.
X
XIP addresses are implemented as a struct of fixed in-memory length,
Xbut variable on-disk storage size. For IPV4, it contains the address
Xfamily (AF_INET), the CIDR prefix length and four byte address. For
XIPV6, the address family will be different, and the address longer.
X
XThe external representation of an IP address generally looks like
X'158.37.96.15/32'. This address happens to be part of a subnet where
XI work; '158.37.96.0/24', which itself is a part of the larger subnet
Xallocated to our site, which is '158.37.96.0/21', which again, if you
Xgo by the old book, is part of the class "B" net '158.37.0.0/16'.
X
XInput and output functions are supplied, along with the "normal" <,
X<=, =, >=, > and <> operators, which all do what you expect. In
Xaddition, there are operators to check for networks or addresses being
Xsubnets of or addresses contained within other networks. << tests
Xwhether the left operand is contained within the right, <<= includes
Xequality, >> and >>= do the same things the opposite way.
X
XThe input and output functions use routines from Paul Vixie's BIND,
Xand I've snarfed the source files inet_net_ntop.c and inet_net_pton.c
Xdirectly from a recent distribution of that code. They are included
Xhere to avoid the need to fetch and install the BIND libraries to be
Xable to use this code. IANAL, but it looks from the copyright
Xmessages in the files as if this should be acceptable. Read the
Xdocumentation in inet_net_pton.c to see the legal input formats.
X
XMAC level ethernet addresses are implemented as a 6 byte struct that
Xcontains the address as unsigned chars. Several input forms are
Xaccepted; the following are all the same address: '08002b:010203',
X'08002b-010203', '0800.2b01.0203', '08-00-2b-01-02-03' and
X'08:00:2b:01:02:03'. Upper and lower case is accepted for the digits
X'a' through 'f'. Output is always in the latter of the given forms.
X
XAs with IP addresses, input and output functions are supplied as well
Xas the "normal" operators, which do what you expect. As an extra
Xfeature, a function macaddr_manuf() is defined, which returns the name
Xof the manufacturer as a string. This is currently held in a
Xhard-coded struct internal to the C module -- it might be smarter to
Xput this information into an actual data base table, and look up the
Xmanufacturer there.
X
XMany thanks to Aleksei Roudnev and Paul Vixie for their fine work!
X
XI don't know what changes are needed to the Makefile for other systems
Xthan the one I'm running (NetBSD 1.3), but anyway: to install on a BSD
Xsystem: fix the path names in the Makefile if you need to, then make,
Xmake install, slurp the SQL files into psql or whatever, and you're
Xoff. Enjoy!
X
XBergen, Norway, 1998-08-09, Tom Ivar Helbekkmo (tih@nhh.no).
END-of-README
echo x - inet_net_ntop.c
sed 's/^X//' >inet_net_ntop.c << 'END-of-inet_net_ntop.c'
X/*
X * Copyright (c) 1996 by Internet Software Consortium.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
X * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
X * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic const char rcsid[] = "$Id: inet_net_ntop.c,v 8.2 1996/08/08 06:54:44 vixie Exp $";
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X
X#ifdef SPRINTF_CHAR
X# define SPRINTF(x) strlen(sprintf/**/x)
X#else
X# define SPRINTF(x) ((size_t)sprintf x)
X#endif
X
Xstatic char * inet_net_ntop_ipv4 __P((const u_char *src, int bits,
X char *dst, size_t size));
X
X/*
X * char *
X * inet_net_ntop(af, src, bits, dst, size)
X * convert network number from network to presentation format.
X * generates CIDR style result always.
X * return:
X * pointer to dst, or NULL if an error occurred (check errno).
X * author:
X * Paul Vixie (ISC), July 1996
X */
Xchar *
Xinet_net_ntop(af, src, bits, dst, size)
X int af;
X const void *src;
X int bits;
X char *dst;
X size_t size;
X{
X switch (af) {
X case AF_INET:
X return (inet_net_ntop_ipv4(src, bits, dst, size));
X default:
X errno = EAFNOSUPPORT;
X return (NULL);
X }
X}
X
X/*
X * static char *
X * inet_net_ntop_ipv4(src, bits, dst, size)
X * convert IPv4 network number from network to presentation format.
X * generates CIDR style result always.
X * return:
X * pointer to dst, or NULL if an error occurred (check errno).
X * note:
X * network byte order assumed. this means 192.5.5.240/28 has
X * 0x11110000 in its fourth octet.
X * author:
X * Paul Vixie (ISC), July 1996
X */
Xstatic char *
Xinet_net_ntop_ipv4(src, bits, dst, size)
X const u_char *src;
X int bits;
X char *dst;
X size_t size;
X{
X char *odst = dst;
X char *t;
X u_int m;
X int b;
X
X if (bits < 0 || bits > 32) {
X errno = EINVAL;
X return (NULL);
X }
X if (bits == 0) {
X if (size < sizeof "0")
X goto emsgsize;
X *dst++ = '0';
X *dst = '\0';
X }
X
X /* Format whole octets. */
X for (b = bits / 8; b > 0; b--) {
X if (size < sizeof "255.")
X goto emsgsize;
X t = dst;
X dst += SPRINTF((dst, "%u", *src++));
X if (b > 1) {
X *dst++ = '.';
X *dst = '\0';
X }
X size -= (size_t)(dst - t);
X }
X
X /* Format partial octet. */
X b = bits % 8;
X if (b > 0) {
X if (size < sizeof ".255")
X goto emsgsize;
X t = dst;
X if (dst != odst)
X *dst++ = '.';
X m = ((1 << b) - 1) << (8 - b);
X dst += SPRINTF((dst, "%u", *src & m));
X size -= (size_t)(dst - t);
X }
X
X /* Format CIDR /width. */
X if (size < sizeof "/32")
X goto emsgsize;
X dst += SPRINTF((dst, "/%u", bits));
X return (odst);
X
X emsgsize:
X errno = EMSGSIZE;
X return (NULL);
X}
END-of-inet_net_ntop.c
echo x - inet_net_pton.c
sed 's/^X//' >inet_net_pton.c << 'END-of-inet_net_pton.c'
X/*
X * Copyright (c) 1996 by Internet Software Consortium.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
X * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
X * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic const char rcsid[] = "$Id: inet_net_pton.c,v 8.3 1996/11/11 06:36:52 vixie Exp $";
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <assert.h>
X#include <ctype.h>
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X
X#ifdef SPRINTF_CHAR
X# define SPRINTF(x) strlen(sprintf/**/x)
X#else
X# define SPRINTF(x) ((size_t)sprintf x)
X#endif
X
Xstatic int inet_net_pton_ipv4 __P((const char *src, u_char *dst,
X size_t size));
X
X/*
X * static int
X * inet_net_pton(af, src, dst, size)
X * convert network number from presentation to network format.
X * accepts hex octets, hex strings, decimal octets, and /CIDR.
X * "size" is in bytes and describes "dst".
X * return:
X * number of bits, either imputed classfully or specified with /CIDR,
X * or -1 if some failure occurred (check errno). ENOENT means it was
X * not a valid network specification.
X * author:
X * Paul Vixie (ISC), June 1996
X */
Xint
Xinet_net_pton(af, src, dst, size)
X int af;
X const char *src;
X void *dst;
X size_t size;
X{
X switch (af) {
X case AF_INET:
X return (inet_net_pton_ipv4(src, dst, size));
X default:
X errno = EAFNOSUPPORT;
X return (-1);
X }
X}
X
X/*
X * static int
X * inet_net_pton_ipv4(src, dst, size)
X * convert IPv4 network number from presentation to network format.
X * accepts hex octets, hex strings, decimal octets, and /CIDR.
X * "size" is in bytes and describes "dst".
X * return:
X * number of bits, either imputed classfully or specified with /CIDR,
X * or -1 if some failure occurred (check errno). ENOENT means it was
X * not an IPv4 network specification.
X * note:
X * network byte order assumed. this means 192.5.5.240/28 has
X * 0x11110000 in its fourth octet.
X * author:
X * Paul Vixie (ISC), June 1996
X */
Xstatic int
Xinet_net_pton_ipv4(src, dst, size)
X const char *src;
X u_char *dst;
X size_t size;
X{
X static const char
X xdigits[] = "0123456789abcdef",
X digits[] = "0123456789";
X int n, ch, tmp, dirty, bits;
X const u_char *odst = dst;
X
X ch = *src++;
X if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
X && isascii(src[1]) && isxdigit(src[1])) {
X /* Hexadecimal: Eat nybble string. */
X if (size <= 0)
X goto emsgsize;
X *dst = 0, dirty = 0;
X src++; /* skip x or X. */
X while ((ch = *src++) != '\0' &&
X isascii(ch) && isxdigit(ch)) {
X if (isupper(ch))
X ch = tolower(ch);
X n = strchr(xdigits, ch) - xdigits;
X assert(n >= 0 && n <= 15);
X *dst |= n;
X if (!dirty++)
X *dst <<= 4;
X else if (size-- > 0)
X *++dst = 0, dirty = 0;
X else
X goto emsgsize;
X }
X if (dirty)
X size--;
X } else if (isascii(ch) && isdigit(ch)) {
X /* Decimal: eat dotted digit string. */
X for (;;) {
X tmp = 0;
X do {
X n = strchr(digits, ch) - digits;
X assert(n >= 0 && n <= 9);
X tmp *= 10;
X tmp += n;
X if (tmp > 255)
X goto enoent;
X } while ((ch = *src++) != '\0' &&
X isascii(ch) && isdigit(ch));
X if (size-- <= 0)
X goto emsgsize;
X *dst++ = (u_char) tmp;
X if (ch == '\0' || ch == '/')
X break;
X if (ch != '.')
X goto enoent;
X ch = *src++;
X if (!isascii(ch) || !isdigit(ch))
X goto enoent;
X }
X } else
X goto enoent;
X
X bits = -1;
X if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
X /* CIDR width specifier. Nothing can follow it. */
X ch = *src++; /* Skip over the /. */
X bits = 0;
X do {
X n = strchr(digits, ch) - digits;
X assert(n >= 0 && n <= 9);
X bits *= 10;
X bits += n;
X } while ((ch = *src++) != '\0' &&
X isascii(ch) && isdigit(ch));
X if (ch != '\0')
X goto enoent;
X if (bits > 32)
X goto emsgsize;
X }
X
X /* Firey death and destruction unless we prefetched EOS. */
X if (ch != '\0')
X goto enoent;
X
X /* If nothing was written to the destination, we found no address. */
X if (dst == odst)
X goto enoent;
X /* If no CIDR spec was given, infer width from net class. */
X if (bits == -1) {
X if (*odst >= 240) /* Class E */
X bits = 32;
X else if (*odst >= 224) /* Class D */
X bits = 4;
X else if (*odst >= 192) /* Class C */
X bits = 24;
X else if (*odst >= 128) /* Class B */
X bits = 16;
X else /* Class A */
X bits = 8;
X /* If imputed mask is narrower than specified octets, widen. */
X if (bits >= 8 && bits < ((dst - odst) * 8))
X bits = (dst - odst) * 8;
X }
X /* Extend network to cover the actual mask. */
X while (bits > ((dst - odst) * 8)) {
X if (size-- <= 0)
X goto emsgsize;
X *dst++ = '\0';
X }
X return (bits);
X
X enoent:
X errno = ENOENT;
X return (-1);
X
X emsgsize:
X errno = EMSGSIZE;
X return (-1);
X}
END-of-inet_net_pton.c
echo x - ip.c
sed 's/^X//' >ip.c << 'END-of-ip.c'
X/*
X * PostgreSQL type definitions for IP addresses. This
X * is for IP V4 CIDR notation, but prepared for V6: just
X * add the necessary bits where the comments indicate.
X *
X * $Id: ip.c,v 1.2 1998/09/08 12:10:36 tih Exp $
X */
X
X#include <sys/types.h>
X#include <sys/socket.h>
X
X#include <stdio.h>
X#include <errno.h>
X
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <postgres.h>
X#include <utils/palloc.h>
X
X/*
X * This is the internal storage format for IP addresses:
X */
X
Xtypedef struct {
X unsigned char family;
X unsigned char bits;
X union {
X u_int32_t ipv4_addr; /* network byte order */
X /* add IPV6 address type here */
X } addr;
X} ipaddr_struct;
X
Xtypedef struct varlena ipaddr;
X
X/*
X * Access macros. Add IPV6 support.
X */
X
X#define ip_addrsize(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->family == AF_INET ? 4 : -1)
X
X#define ip_family(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->family)
X
X#define ip_bits(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->bits)
X
X#define ip_v4addr(ipaddrptr) \
X (((ipaddr_struct *)VARDATA(ipaddrptr))->addr.ipv4_addr)
X
X/*
X * Various forward declarations:
X */
X
Xipaddr *ipaddr_in(char *str);
Xchar *ipaddr_out(ipaddr *addr);
X
Xbool ipaddr_lt(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_le(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_eq(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_ge(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_gt(ipaddr *a1, ipaddr *a2);
X
Xbool ipaddr_ne(ipaddr *a1, ipaddr *a2);
X
Xbool ipaddr_sub(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_subeq(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_sup(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_supeq(ipaddr *a1, ipaddr *a2);
X
Xint4 ipaddr_cmp(ipaddr *a1, ipaddr *a2);
X
Xint v4bitncmp(u_int32_t a1, u_int32_t a2, int bits);
X
X/*
X * IP address reader.
X */
X
Xipaddr *ipaddr_in(char *src) {
X int bits;
X ipaddr *dst;
X
X dst = palloc(VARHDRSZ + sizeof(ipaddr_struct));
X if (dst == NULL) {
X elog(ERROR, "unable to allocate memory in ipaddr_in()");
X return(NULL);
X }
X /* First, try for an IP V4 address: */
X ip_family(dst) = AF_INET;
X bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst), ip_addrsize(dst));
X if ((bits < 0) || (bits > 32)) {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "could not parse \"%s\"", src);
X pfree(dst);
X return(NULL);
X }
X VARSIZE(dst) = VARHDRSZ
X + ((char *)&ip_v4addr(dst) - (char *)VARDATA(dst))
X + ip_addrsize(dst);
X ip_bits(dst) = bits;
X return(dst);
X}
X
X/*
X * IP address output function.
X */
X
Xchar *ipaddr_out(ipaddr *src) {
X char *dst, tmp[sizeof("255.255.255.255/32")];
X
X if (ip_family(src) == AF_INET) {
X /* It's an IP V4 address: */
X if (inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src),
X tmp, sizeof(tmp)) < 0) {
X elog(ERROR, "unable to print address (%s)", strerror(errno));
X return(NULL);
X }
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "unknown address family (%d)", ip_family(src));
X return(NULL);
X }
X dst = palloc(strlen(tmp) + 1);
X if (dst == NULL) {
X elog(ERROR, "unable to allocate memory in ipaddr_out()");
X return(NULL);
X }
X strcpy(dst, tmp);
X return(dst);
X}
X
X/*
X * Boolean tests for magnitude. Add V4/V6 testing!
X */
X
Xbool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
X return((order < 0) || ((order == 0) && (ip_bits(a1) < ip_bits(a2))));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_le(ipaddr *a1, ipaddr *a2) {
X return(ipaddr_lt(a1, a2) || ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) == ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
X return(ipaddr_gt(a1, a2) || ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
X return((order > 0) || ((order == 0) && (ip_bits(a1) > ip_bits(a2))));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
X return(!ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_sub(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) > ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_subeq(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) >= ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_sup(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) < ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
Xbool ipaddr_supeq(ipaddr *a1, ipaddr *a2) {
X if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X return((ip_bits(a1) <= ip_bits(a2))
X && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X } else {
X /* Go for an IPV6 address here, before faulting out: */
X elog(ERROR, "cannot compare address families %d and %d",
X ip_family(a1), ip_family(a2));
X return(FALSE);
X }
X}
X
X/*
X * Comparison function for sorting. Add V4/V6 testing!
X */
X
Xint4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
X if (ntohl(ip_v4addr(a1)) < ntohl(ip_v4addr(a2))) {
X return(-1);
X } else if (ntohl(ip_v4addr(a1)) > ntohl(ip_v4addr(a2))) {
X return(1);
X }
X return 0;
X}
X
X/*
X * Bitwise comparison for V4 addresses. Add V6 implementation!
X */
X
Xint v4bitncmp(u_int32_t a1, u_int32_t a2, int bits) {
X unsigned long mask = 0;
X int i;
X for (i = 0; i < bits; i++) {
X mask = (mask >> 1) | 0x80000000;
X }
X a1 = ntohl(a1);
X a2 = ntohl(a2);
X if ((a1 & mask) < (a2 & mask)) {
X return(-1);
X } else if ((a1 & mask) > (a2 & mask)) {
X return(1);
X }
X return(0);
X}
X
X/*
X * eof
X */
END-of-ip.c
echo x - ip.sql.in
sed 's/^X//' >ip.sql.in << 'END-of-ip.sql.in'
X--
X-- PostgreSQL code for IP addresses.
X--
X-- $Id: ip.sql.in,v 1.2 1998/09/08 12:10:45 tih Exp $
X--
X
Xload 'TARGET/ip.so';
X
X--
X-- Input and output functions and the type itself:
X--
X
Xcreate function ipaddr_in(opaque)
X returns opaque
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_out(opaque)
X returns opaque
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate type ipaddr (
X internallength = variable,
X externallength = variable,
X input = ipaddr_in,
X output = ipaddr_out
X);
X
X--
X-- The various boolean tests:
X--
X
Xcreate function ipaddr_lt(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_le(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_eq(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_ge(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_gt(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_ne(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_sub(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_subeq(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_sup(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_supeq(ipaddr, ipaddr)
X returns bool
X as 'TARGET/ip.so'
X language 'c';
X
Xcreate function ipaddr_cmp(ipaddr, ipaddr)
X returns int4
X as 'TARGET/ip.so'
X language 'c';
X
X--
X-- Now the operators. Note how some of the parameters to some
X-- of the 'create operator' commands are commented out. This
X-- is because they reference as yet undefined operators, and
X-- will be implicitly defined when those are, further down.
X--
X
Xcreate operator < (
X leftarg = ipaddr,
X rightarg = ipaddr,
X-- negator = >=,
X restrict = intltsel,
X join = intltjoinsel,
X procedure = ipaddr_lt
X);
X
Xcreate operator <= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X-- negator = >,
X restrict = intltsel,
X join = intltjoinsel,
X procedure = ipaddr_le
X);
X
Xcreate operator = (
X leftarg = ipaddr,
X rightarg = ipaddr,
X commutator = =,
X-- negator = <>,
X restrict = eqsel,
X join = eqjoinsel,
X procedure = ipaddr_eq
X);
X
Xcreate operator >= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X negator = <,
X restrict = intgtsel,
X join = intgtjoinsel,
X procedure = ipaddr_ge
X);
X
Xcreate operator > (
X leftarg = ipaddr,
X rightarg = ipaddr,
X negator = <=,
X restrict = intgtsel,
X join = intgtjoinsel,
X procedure = ipaddr_gt
X);
X
Xcreate operator <> (
X leftarg = ipaddr,
X rightarg = ipaddr,
X negator = =,
X restrict = neqsel,
X join = neqjoinsel,
X procedure = ipaddr_ne
X);
X
Xcreate operator << (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_sub
X);
X
Xcreate operator <<= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_subeq
X);
X
Xcreate operator >> (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_sup
X);
X
Xcreate operator >>= (
X leftarg = ipaddr,
X rightarg = ipaddr,
X procedure = ipaddr_supeq
X);
X
X--
X-- Finally, make it possible to do btree indexing on IP addresses.
X--
X
Xbegin;
X
Xcreate table tmp_op (oprname name, opi int2);
X
Xinsert into tmp_op values ('<', 1);
Xinsert into tmp_op values ('<=', 2);
Xinsert into tmp_op values ('=', 3);
Xinsert into tmp_op values ('>=', 4);
Xinsert into tmp_op values ('>', 5);
X
Xdelete from pg_opclass
X where opcname = 'ipaddr_ops';
X
Xinsert into pg_opclass (opcname, opcdeftype)
X select 'ipaddr_ops', oid from pg_type
X where typname = 'ipaddr';
X
Xselect o.oid as opoid, o.oprname
X into table ipaddr_tmp
X from pg_operator o, pg_type t
X where o.oprleft = t.oid and
X o.oprright = t.oid and
X t.typname = 'ipaddr';
X
Xinsert into pg_amop (amopid, amopclaid, amopopr, amopstrategy,
X amopselect, amopnpages)
X select am.oid, opcl.oid, c.opoid, t.opi,
X 'btreesel'::regproc, 'btreenpage'::regproc
X from pg_am am, pg_opclass opcl, ipaddr_tmp c, tmp_op t
X where t.oprname = c.oprname and
X amname = 'btree' and opcname = 'ipaddr_ops';
X
Xinsert into pg_amproc (amid, amopclaid, amproc, amprocnum)
X select pgam.oid, opc.oid, prc.oid, '1'::int2
X from pg_am pgam, pg_opclass opc, pg_proc prc
X where prc.proname = 'ipaddr_cmp' and
X pgam.amname = 'btree' and
X opc.opcname = 'ipaddr_ops';
X
Xdrop table tmp_op;
Xdrop table ipaddr_tmp;
X
Xcommit;
X
X--
X-- eof
X--
END-of-ip.sql.in
echo x - mac.c
sed 's/^X//' >mac.c << 'END-of-mac.c'
X/*
X * PostgreSQL type definitions for MAC addresses.
X *
X * $Id: mac.c,v 1.1 1998/09/07 12:31:18 tih Exp $
X */
X
X#include <stdio.h>
X
X#include <postgres.h>
X#include <utils/palloc.h>
X
X#include "mac.h"
X
X/*
X * This is the internal storage format for MAC addresses:
X */
X
Xtypedef struct macaddr {
X unsigned char a;
X unsigned char b;
X unsigned char c;
X unsigned char d;
X unsigned char e;
X unsigned char f;
X} macaddr;
X
X/*
X * Various forward declarations:
X */
X
Xmacaddr *macaddr_in(char *str);
Xchar *macaddr_out(macaddr *addr);
X
Xbool macaddr_lt(macaddr *a1, macaddr *a2);
Xbool macaddr_le(macaddr *a1, macaddr *a2);
Xbool macaddr_eq(macaddr *a1, macaddr *a2);
Xbool macaddr_ge(macaddr *a1, macaddr *a2);
Xbool macaddr_gt(macaddr *a1, macaddr *a2);
X
Xbool macaddr_ne(macaddr *a1, macaddr *a2);
X
Xint4 macaddr_cmp(macaddr *a1, macaddr *a2);
X
Xtext *macaddr_manuf(macaddr *addr);
X
X/*
X * Utility macros used for sorting and comparing:
X */
X
X#define hibits(addr) \
X ((unsigned long)((addr->a<<16)|(addr->b<<8)|(addr->c)))
X
X#define lobits(addr) \
X ((unsigned long)((addr->c<<16)|(addr->e<<8)|(addr->f)))
X
X/*
X * MAC address reader. Accepts several common notations.
X */
X
Xmacaddr *macaddr_in(char *str) {
X int a, b, c, d, e, f;
X macaddr *result;
X int count;
X
X if (strlen(str) > 0) {
X
X count = sscanf(str, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%x-%x-%x-%x-%x-%x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%2x%2x%2x:%2x%2x%2x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%2x%2x%2x-%2x%2x%2x", &a, &b, &c, &d, &e, &f);
X if (count != 6)
X count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x", &a, &b, &c, &d, &e, &f);
X
X if (count != 6) {
X elog(ERROR, "macaddr_in: error in parsing \"%s\"", str);
X return(NULL);
X }
X
X if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
X (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
X (e < 0) || (e > 255) || (f < 0) || (f > 255)) {
X elog(ERROR, "macaddr_in: illegal address \"%s\"", str);
X return(NULL);
X }
X } else {
X a = b = c = d = e = f = 0; /* special case for missing address */
X }
X
X result = (macaddr *)palloc(sizeof(macaddr));
X
X result->a = a;
X result->b = b;
X result->c = c;
X result->d = d;
X result->e = e;
X result->f = f;
X
X return(result);
X}
X
X/*
X * MAC address output function. Fixed format.
X */
X
Xchar *macaddr_out(macaddr *addr) {
X char *result;
X
X if (addr == NULL)
X return(NULL);
X
X result = (char *)palloc(32);
X
X if ((hibits(addr) > 0) || (lobits(addr) > 0)) {
X sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x",
X addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
X } else {
X result[0] = 0; /* special case for missing address */
X }
X return(result);
X}
X
X/*
X * Boolean tests.
X */
X
Xbool macaddr_lt(macaddr *a1, macaddr *a2) {
X return((hibits(a1) < hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) < lobits(a2)));
X};
X
Xbool macaddr_le(macaddr *a1, macaddr *a2) {
X return((hibits(a1) < hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) <= lobits(a2)));
X};
X
Xbool macaddr_eq(macaddr *a1, macaddr *a2) {
X return ((hibits(a1) == hibits(a2)) && (lobits(a1) == lobits(a2)));
X};
X
Xbool macaddr_ge(macaddr *a1, macaddr *a2) {
X return((hibits(a1) > hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) >= lobits(a2)));
X};
X
Xbool macaddr_gt(macaddr *a1, macaddr *a2) {
X return((hibits(a1) > hibits(a2)) ||
X ((hibits(a1) == hibits(a2)) && lobits(a1) > lobits(a2)));
X};
X
Xbool macaddr_ne(macaddr *a1, macaddr *a2) {
X return ((hibits(a1) != hibits(a2)) || (lobits(a1) != lobits(a2)));
X};
X
X/*
X * Comparison function for sorting:
X */
X
Xint4 macaddr_cmp(macaddr *a1, macaddr *a2) {
X if (hibits(a1) < hibits(a2))
X return -1;
X else if (hibits(a1) > hibits(a2))
X return 1;
X else if (lobits(a1) < lobits(a2))
X return -1;
X else if (lobits(a1) > lobits(a2))
X return 1;
X else
X return 0;
X}
X
X/*
X * The special manufacturer fetching function. See "mac.h".
X */
X
Xtext *macaddr_manuf(macaddr *addr) {
X manufacturer *manuf;
X int length;
X text *result;
X
X for (manuf = manufacturers; manuf->name != NULL; manuf++) {
X if ((manuf->a == addr->a) &&
X (manuf->b == addr->b) &&
X (manuf->c == addr->c))
X break;
X }
X if (manuf->name == NULL) {
X result = palloc(VARHDRSZ + 1);
X memset(result, 0, VARHDRSZ + 1);
X VARSIZE(result) = VARHDRSZ + 1;
X } else {
X length = strlen(manuf->name) + 1;
X result = palloc(length + VARHDRSZ);
X memset(result, 0, length + VARHDRSZ);
X VARSIZE(result) = length + VARHDRSZ;
X memcpy(VARDATA(result), manuf->name, length);
X }
X return result;
X}
X
X/*
X * eof
X */
END-of-mac.c
echo x - mac.h
sed 's/^X//' >mac.h << 'END-of-mac.h'
X/*
X * PostgreSQL type definitions for MAC addresses.
X *
X * $Id: mac.h,v 1.1 1998/09/07 12:31:22 tih Exp $
X */
X
Xtypedef struct manufacturer {
X unsigned char a;
X unsigned char b;
X unsigned char c;
X char *name;
X} manufacturer;
X
Xmanufacturer manufacturers[] = {
X {0x00, 0x00, 0x0C, "Cisco"},
X {0x00, 0x00, 0x0E, "Fujitsu"},
X {0x00, 0x00, 0x0F, "NeXT"},
X {0x00, 0x00, 0x10, "Sytek"},
X {0x00, 0x00, 0x1D, "Cabletron"},
X {0x00, 0x00, 0x20, "DIAB"},
X {0x00, 0x00, 0x22, "Visual Technology"},
X {0x00, 0x00, 0x2A, "TRW"},
X {0x00, 0x00, 0x32, "GPT Limited"},
X {0x00, 0x00, 0x5A, "S & Koch"},
X {0x00, 0x00, 0x5E, "IANA"},
X {0x00, 0x00, 0x65, "Network General"},
X {0x00, 0x00, 0x6B, "MIPS"},
X {0x00, 0x00, 0x77, "MIPS"},
X {0x00, 0x00, 0x7A, "Ardent"},
X {0x00, 0x00, 0x89, "Cayman Systems"},
X {0x00, 0x00, 0x93, "Proteon"},
X {0x00, 0x00, 0x9F, "Ameristar Technology"},
X {0x00, 0x00, 0xA2, "Wellfleet"},
X {0x00, 0x00, 0xA3, "Network Application Technology"},
X {0x00, 0x00, 0xA6, "Network General"},
X {0x00, 0x00, 0xA7, "NCD"},
X {0x00, 0x00, 0xA9, "Network Systems"},
X {0x00, 0x00, 0xAA, "Xerox"},
X {0x00, 0x00, 0xB3, "CIMLinc"},
X {0x00, 0x00, 0xB7, "Dove Fastnet"},
X {0x00, 0x00, 0xBC, "Allen-Bradley"},
X {0x00, 0x00, 0xC0, "Western Digital"},
X {0x00, 0x00, 0xC5, "Farallon"},
X {0x00, 0x00, 0xC6, "Hewlett-Packard"},
X {0x00, 0x00, 0xC8, "Altos"},
X {0x00, 0x00, 0xC9, "Emulex"},
X {0x00, 0x00, 0xD7, "Dartmouth College"},
X {0x00, 0x00, 0xD8, "3Com (?)"},
X {0x00, 0x00, 0xDD, "Gould"},
X {0x00, 0x00, 0xDE, "Unigraph"},
X {0x00, 0x00, 0xE2, "Acer Counterpoint"},
X {0x00, 0x00, 0xEF, "Alantec"},
X {0x00, 0x00, 0xFD, "High Level Hardware"},
X {0x00, 0x01, 0x02, "BBN internal usage"},
X {0x00, 0x20, 0xAF, "3Com"},
X {0x00, 0x17, 0x00, "Kabel"},
X {0x00, 0x80, 0x64, "Wyse Technology"},
X {0x00, 0x80, 0x2B, "IMAC (?)"},
X {0x00, 0x80, 0x2D, "Xylogics, Inc."},
X {0x00, 0x80, 0x8C, "Frontier Software Development"},
X {0x00, 0x80, 0xC2, "IEEE 802.1 Committee"},
X {0x00, 0x80, 0xD3, "Shiva"},
X {0x00, 0xAA, 0x00, "Intel"},
X {0x00, 0xDD, 0x00, "Ungermann-Bass"},
X {0x00, 0xDD, 0x01, "Ungermann-Bass"},
X {0x02, 0x07, 0x01, "Racal InterLan"},
X {0x02, 0x04, 0x06, "BBN internal usage"},
X {0x02, 0x60, 0x86, "Satelcom MegaPac"},
X {0x02, 0x60, 0x8C, "3Com"},
X {0x02, 0xCF, 0x1F, "CMC"},
X {0x08, 0x00, 0x02, "3Com"},
X {0x08, 0x00, 0x03, "ACC"},
X {0x08, 0x00, 0x05, "Symbolics"},
X {0x08, 0x00, 0x08, "BBN"},
X {0x08, 0x00, 0x09, "Hewlett-Packard"},
X {0x08, 0x00, 0x0A, "Nestar Systems"},
X {0x08, 0x00, 0x0B, "Unisys"},
X {0x08, 0x00, 0x11, "Tektronix"},
X {0x08, 0x00, 0x14, "Excelan"},
X {0x08, 0x00, 0x17, "NSC"},
X {0x08, 0x00, 0x1A, "Data General"},
X {0x08, 0x00, 0x1B, "Data General"},
X {0x08, 0x00, 0x1E, "Apollo"},
X {0x08, 0x00, 0x20, "Sun"},
X {0x08, 0x00, 0x22, "NBI"},
X {0x08, 0x00, 0x25, "CDC"},
X {0x08, 0x00, 0x26, "Norsk Data"},
X {0x08, 0x00, 0x27, "PCS Computer Systems GmbH"},
X {0x08, 0x00, 0x28, "Texas Instruments"},
X {0x08, 0x00, 0x2B, "DEC"},
X {0x08, 0x00, 0x2E, "Metaphor"},
X {0x08, 0x00, 0x2F, "Prime Computer"},
X {0x08, 0x00, 0x36, "Intergraph"},
X {0x08, 0x00, 0x37, "Fujitsu-Xerox"},
X {0x08, 0x00, 0x38, "Bull"},
X {0x08, 0x00, 0x39, "Spider Systems"},
X {0x08, 0x00, 0x41, "DCA Digital Comm. Assoc."},
X {0x08, 0x00, 0x45, "Xylogics (?)"},
X {0x08, 0x00, 0x46, "Sony"},
X {0x08, 0x00, 0x47, "Sequent"},
X {0x08, 0x00, 0x49, "Univation"},
X {0x08, 0x00, 0x4C, "Encore"},
X {0x08, 0x00, 0x4E, "BICC"},
X {0x08, 0x00, 0x56, "Stanford University"},
X {0x08, 0x00, 0x58, "DECsystem 20 (?)"},
X {0x08, 0x00, 0x5A, "IBM"},
X {0x08, 0x00, 0x67, "Comdesign"},
X {0x08, 0x00, 0x68, "Ridge"},
X {0x08, 0x00, 0x69, "Silicon Graphics"},
X {0x08, 0x00, 0x6E, "Concurrent"},
X {0x08, 0x00, 0x75, "DDE"},
X {0x08, 0x00, 0x7C, "Vitalink"},
X {0x08, 0x00, 0x80, "XIOS"},
X {0x08, 0x00, 0x86, "Imagen/QMS"},
X {0x08, 0x00, 0x87, "Xyplex"},
X {0x08, 0x00, 0x89, "Kinetics"},
X {0x08, 0x00, 0x8B, "Pyramid"},
X {0x08, 0x00, 0x8D, "XyVision"},
X {0x08, 0x00, 0x90, "Retix Inc"},
X {0x48, 0x44, 0x53, "HDS (?)"},
X {0x80, 0x00, 0x10, "AT&T"},
X {0xAA, 0x00, 0x00, "DEC"},
X {0xAA, 0x00, 0x01, "DEC"},
X {0xAA, 0x00, 0x02, "DEC"},
X {0xAA, 0x00, 0x03, "DEC"},
X {0xAA, 0x00, 0x04, "DEC"},
X {0x00, 0x00, 0x00, NULL}
X};
X
X/*
X * eof
X */
END-of-mac.h
echo x - mac.sql.in
sed 's/^X//' >mac.sql.in << 'END-of-mac.sql.in'
X--
X-- PostgreSQL code for MAC addresses.
X--
X-- $Id: mac.sql.in,v 1.2 1998/09/08 12:11:18 tih Exp $
X--
X
Xload 'TARGET/mac.so';
X
X--
X-- Input and output functions and the type itself:
X--
X
Xcreate function macaddr_in(opaque)
X returns opaque
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_out(opaque)
X returns opaque
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate type macaddr (
X internallength = 6,
X externallength = variable,
X input = macaddr_in,
X output = macaddr_out
X);
X
X--
X-- The boolean tests:
X--
X
Xcreate function macaddr_lt(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_le(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_eq(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_ge(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_gt(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_ne(macaddr, macaddr)
X returns bool
X as 'TARGET/mac.so'
X language 'c';
X
Xcreate function macaddr_cmp(macaddr, macaddr)
X returns int4
X as 'TARGET/mac.so'
X language 'c';
X
X--
X-- Now the operators. Note how some of the parameters to some
X-- of the 'create operator' commands are commented out. This
X-- is because they reference as yet undefined operators, and
X-- will be implicitly defined when those are, further down.
X--
X
Xcreate operator < (
X leftarg = macaddr,
X rightarg = macaddr,
X-- negator = >=,
X procedure = macaddr_lt
X);
X
Xcreate operator <= (
X leftarg = macaddr,
X rightarg = macaddr,
X-- negator = >,
X procedure = macaddr_le
X);
X
Xcreate operator = (
X leftarg = macaddr,
X rightarg = macaddr,
X commutator = =,
X-- negator = <>,
X procedure = macaddr_eq
X);
X
Xcreate operator >= (
X leftarg = macaddr,
X rightarg = macaddr,
X negator = <,
X procedure = macaddr_ge
X);
X
Xcreate operator > (
X leftarg = macaddr,
X rightarg = macaddr,
X negator = <=,
X procedure = macaddr_gt
X);
X
Xcreate operator <> (
X leftarg = macaddr,
X rightarg = macaddr,
X negator = =,
X procedure = macaddr_ne
X);
X
X--
X-- Finally, the special manufacurer matching function:
X--
X
Xcreate function macaddr_manuf(macaddr)
X returns text
X as 'TARGET/mac.so'
X language 'c';
X
X--
X-- Finally, make it possible to do btree indexing on MAC addresses.
X--
X
Xbegin;
X
Xcreate table tmp_op (oprname name, opi int2);
X
Xinsert into tmp_op values ('<', 1);
Xinsert into tmp_op values ('<=', 2);
Xinsert into tmp_op values ('=', 3);
Xinsert into tmp_op values ('>=', 4);
Xinsert into tmp_op values ('>', 5);
X
Xdelete from pg_opclass
X where opcname = 'macaddr_ops';
X
Xinsert into pg_opclass (opcname, opcdeftype)
X select 'macaddr_ops', oid from pg_type
X where typname = 'macaddr';
X
Xselect o.oid as opoid, o.oprname
X into table macaddr_tmp
X from pg_operator o, pg_type t
X where o.oprleft = t.oid and
X o.oprright = t.oid and
X t.typname = 'macaddr';
X
Xinsert into pg_amop (amopid, amopclaid, amopopr, amopstrategy,
X amopselect, amopnpages)
X select am.oid, opcl.oid, c.opoid, t.opi,
X 'btreesel'::regproc, 'btreenpage'::regproc
X from pg_am am, pg_opclass opcl, macaddr_tmp c, tmp_op t
X where t.oprname = c.oprname and
X amname = 'btree' and opcname = 'macaddr_ops';
X
Xinsert into pg_amproc (amid, amopclaid, amproc, amprocnum)
X select pgam.oid, opc.oid, prc.oid, '1'::int2
X from pg_am pgam, pg_opclass opc, pg_proc prc
X where prc.proname = 'macaddr_cmp' and
X pgam.amname = 'btree' and
X opc.opcname = 'macaddr_ops';
X
Xdrop table tmp_op;
Xdrop table macaddr_tmp;
X
Xcommit;
X
X--
X-- eof
X--
END-of-mac.sql.in
echo x - test.sql
sed 's/^X//' >test.sql << 'END-of-test.sql'
X--
X-- A quick test of the IP address code
X--
X-- $Id: test.sql,v 1.2 1998/09/08 12:11:34 tih Exp $
X--
X
X-- temporary table:
Xcreate table addresses (address ipaddr);
X
X-- sample data from two subnets:
Xinsert into addresses values ('158.37.96.15');
Xinsert into addresses values ('158.37.96.16');
Xinsert into addresses values ('158.37.96.17');
Xinsert into addresses values ('158.37.97.15');
Xinsert into addresses values ('158.37.97.16');
Xinsert into addresses values ('158.37.97.17');
Xinsert into addresses values ('158.37.98.15');
Xinsert into addresses values ('158.37.98.16');
Xinsert into addresses values ('158.37.98.17');
Xinsert into addresses values ('158.37.96.150');
Xinsert into addresses values ('158.37.96.160');
Xinsert into addresses values ('158.37.96.170');
Xinsert into addresses values ('158.37.97.150');
Xinsert into addresses values ('158.37.97.160');
Xinsert into addresses values ('158.37.97.170');
Xinsert into addresses values ('158.37.98.150');
Xinsert into addresses values ('158.37.98.160');
Xinsert into addresses values ('158.37.98.170');
X
X-- show them all:
Xselect * from addresses;
X
X-- select the ones in subnet 96:
Xselect * from addresses where address << '158.37.96.0/24';
X
X-- select the ones not in subnet 96:
Xselect * from addresses where not address << '158.37.96.0/24';
X
X-- select the ones in subnet 97:
Xselect * from addresses where address << '158.37.97.0/24';
X
X-- select the ones not in subnet 97:
Xselect * from addresses where not address << '158.37.97.0/24';
X
X-- select the ones in subnet 96 or 97, sorted:
Xselect * from addresses where address << '158.37.96.0/23'
X order by address;
X
X-- now some networks:
Xcreate table networks (network ipaddr);
X
X-- now the subnets mentioned above:
Xinsert into networks values ('158.37.96.0/24');
Xinsert into networks values ('158.37.97.0/24');
Xinsert into networks values ('158.37.98.0/24');
X
X-- select matching pairs of addresses and containing nets:
Xselect address, network from addresses, networks
X where address << network;
X
X-- tidy up:
Xdrop table addresses;
Xdrop table networks;
X
X--
X-- eof
X--
END-of-test.sql
exit
--
Bruce Momjian | 830 Blythe Avenue
maillist@candle.pha.pa.us | Drexel Hill, Pennsylvania 19026
+ If your life is a hard drive, | (610) 353-9879(w)
+ Christ can be your backup. | (610) 853-3000(h)