modules/ip/ip.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- IP_sizebits
- IP_addr_t2b
- IP_pref_t2b
- IP_rang_t2b
- IP_addr_b2a
- IP_pref_b2a
- IP_rang_b2a
- IP_addr_bit_get
- IP_addr_bit_set
- IP_pref_bit_fix
- ip_print_prefix
- IP_addr_cmp
- ad
- IP_rang_span
- IP_rang_decomp
- IP_rang_encomp
- IP_pref_2_rang
- IP_rang_classful
- IP_smart_conv
/***************************************
$Revision: 1.7 $
IP handling (ip). ip.c - conversions between ascii and binary forms
of IP addresses, prefixes and ranges.
various operations on binary forms.
Status: NOT REVUED, TESTED
Design and implementation by: Marek Bukowy
******************/ /******************
Copyright (c) 1999 RIPE NCC
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of the author not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
***************************************/
#include <iproutines.h>
#include <string.h>
#include <stdio.h>
#include <erroutines.h>
#include <ctype.h>
#include <memwrap.h>
#include <numconv.h>
#include <stubs.h>
/***************************************************************************/
/*+ return the max. length of bits per space
Yes, it *could* be a macro - but as a function it can detect
more programmer's errors. And will get inlined anyway.
+*/
int IP_sizebits(ip_space_t spc_id) {
/* [<][>][^][v][top][bottom][index][help] */
switch (spc_id) {
case IP_V4:
return 32;
case IP_V6:
return 128;
default:
// die; /* error: bad IP version specified */
return -1;
}
}
/***************************************************************************/
/*+
ascii IP address to binary.
In IP_EXPN mode IP will be treated as not-expanded.
(missing octets will be set to 0, MSB will be set).
In IP_PLAIN mode the routine will complain if it sees less octets.
+*/
er_ret_t
IP_addr_t2b(ip_addr_t *ipptr, char *addr, ip_exp_t expf)
/* [<][>][^][v][top][bottom][index][help] */
{
if( index(addr, ':') == NULL ) {
/* IPv4 */
char *dot;
unsigned len, byte, result=0;
char cpy[4];
int last = 0, dotsfound=0;
int bytes=0;
if( expf != IP_PLAIN && expf != IP_EXPN ) {
return IP_INVARG;
}
do {
if ( (dot = index (addr, '.')) == NULL) {
dot = index (addr, '\0');
last = 1;
}
else {
if( ++dotsfound > 3 ) {
/* handle syntax ERROR - too many dots found */
return IP_INVIP4;
}
}
if ((len = dot - addr) > 4) {
/* syntax ERROR - too many digits between dots*/
return IP_INVIP4;
}
strncpy( cpy, addr, len );
cpy[len]=0;
/* sscanf is waay too slow */
if( ut_dec_2_uns(cpy, &byte) < 0 ) {
/* handle syntax ERROR - invalid characters found */
return IP_INVIP4;
}
if( byte > 255 ) {
/* handle syntax ERROR - number between dots too high */
return IP_INVIP4;
}
result <<= 8;
result += byte;
bytes++;
addr = dot + 1;
} while (!last);
if( expf == IP_PLAIN ) {
if( bytes!=4 ) {
return IP_INVIP4;
}
}
else {
while( bytes<4 ) {
result <<= 8;
bytes++;
}
}
memset(ipptr, 0, sizeof(ip_addr_t));
ipptr->space = IP_V4;
ipptr->words[0] = result;
}
else {
/* IPv6 */
/* not yet implemented. Sorry. */
// die;
return IP_NO6YET;
}
return IP_OK;
}
/***************************************************************************/
/*+ converts a "IP/length" string into a binary prefix
+*/
er_ret_t
IP_pref_t2b(ip_prefix_t *prefptr, char *prefstr, ip_exp_t expf)
/* [<][>][^][v][top][bottom][index][help] */
{
char ip[256];
char *slash, *trash;
int len;
er_ret_t err;
if( expf != IP_PLAIN && expf != IP_EXPN ) {
return IP_INVARG;
}
if( (slash=index(prefstr, '/')) == NULL ) {
// die; /* error: missing slash in prefix */
return IP_NOSLAS;
}
else {
/* copy the IP part to another string, ERROR if 256 chars is not nough */
len = slash - prefstr;
if( len > 255 ) {
// die; /* ERROR - ip address part of the string too long. */
return IP_ADTOLO;
}
strncpy(ip, prefstr, len);
ip[len]=0;
if( (err=IP_addr_t2b( &(prefptr->ip), ip, expf)) != IP_OK) {
// die; /* set error flag: incorrect address format */
return err;
}
// stop at first non-digit
for(trash = slash+1; isdigit(*trash) ; trash++);
len = trash - (slash+1) ;
if( len > 4 ) {
// die; /* ERROR - prefix length part of the string too long. */
return IP_PRTOLO;
}
strncpy(ip, slash+1, len);
ip[len]=0;
if( ut_dec_2_uns(ip, &prefptr->bits) < 0 ) {
// if( sscanf (slash+1, "%d", &(prefptr->bits)) < 1 ) {
// die; /* handle syntax ERROR invalid characters found */
return IP_INVPRF;
}
}
// sanitify the prefix - maybe some irrelevant bits are set
// never create broken binary prefixes.
IP_pref_bit_fix(prefptr);
return IP_OK;
}
/***************************************************************************/
/*+ convert a range string into a binary range struct.
+*/
er_ret_t
IP_rang_t2b(ip_range_t *rangptr, char *rangstr, ip_exp_t expf)
/* [<][>][^][v][top][bottom][index][help] */
{
char *ips, *dash;
er_ret_t err;
if( expf != IP_PLAIN && expf != IP_EXPN ) {
return IP_INVARG;
}
if( (dash=index(rangstr, '-')) == NULL ) {
// die; /* error: missing dash in range */
return IP_INVRAN;
}
else {
/* copy the first IP */
if( (err = wr_calloc( (void*) &ips,1,dash - rangstr + 1)) != UT_OK ) {
return err;
}
strncpy(ips, rangstr, dash - rangstr);
/* convert the first IP into a binary struct */
err=IP_addr_t2b( &(rangptr->begin), ips, expf);
// check later /* set error flag: incorrect address format */
wr_free(ips);
if( err != IP_OK ) {
return err;
}
/* now find the other ip, skip the space */
ips=dash+1;
while( *ips == ' ' ) {
ips++;
}
/* convert the second IP into a binary struct */
if( (err=IP_addr_t2b( &(rangptr->end), ips, expf)) != IP_OK ) {
// die; /* incorrect address format */
return err;
}
if( rangptr->begin.space != rangptr->end.space ) {
// die; /* incompatible IP spaces */
return IP_INVRAN;
}
return IP_OK;
}
}
/***************************************************************************/
/*+converts the IP binary address (binaddr) to a string (ascaddr)
of at most strmax characters. Independent of the result
(success or failure) it messes up the string.
+*/
er_ret_t
IP_addr_b2a( ip_addr_t *binaddr, char *ascaddr, int strmax )
/* [<][>][^][v][top][bottom][index][help] */
{
if(binaddr->space == IP_V4) {
if (snprintf(ascaddr, strmax, "%d.%d.%d.%d",
((binaddr->words[0]) & ((unsigned)0xff<<24))>>24,
((binaddr->words[0]) & (0xff<<16))>>16,
((binaddr->words[0]) & (0xff<<8))>>8,
((binaddr->words[0]) & (0xff<<0))>>0
) >= strmax) {
//die; // string too short
return IP_TOSHRT;
}
#if 0
char buf[5];
int mask;
*ascaddr = '\0';
// this is very inefficient - but maybe this is the way to go for IPv6
for(mask=24; mask >= 0; mask -= 8) {
sprintf(buf, "%d%s", ((binaddr->words[0]) & 0xff<<mask)>>mask,
mask==0 ? "" : ".");
if( (strlen(buf)+strlen(ascaddr)) >= strmax ) {
// die; /* error: insufficient space */
return IP_TOSHRT;
}
else {
strcat(ascaddr, buf);
}
}
#endif
}
else {
/* IPv6 */
/* not yet implemented. Sorry. */
// die;
return IP_NO6YET;
}
return IP_OK;
}
/***************************************************************************/
/*+ convert a binary prefix back into ascii string at most strmax chars long
+*/
er_ret_t
IP_pref_b2a(ip_prefix_t *prefptr, char *ascaddr, int strmax)
/* [<][>][^][v][top][bottom][index][help] */
{
int strl;
er_ret_t err;
if( (err=IP_addr_b2a (&(prefptr->ip), ascaddr, strmax)) != IP_OK) {
//die; /* what the hell */
return err;
}
strl = strlen(ascaddr);
strmax -= strl;
/* now strmax holds the space that is left */
if( snprintf(ascaddr+strl, strmax, "/%d", prefptr->bits) >= strmax) {
// die; /* error: string too short */
return IP_TOSHRT;
}
return IP_OK;
}
/***************************************************************************/
/*+ convert a binary range back into ascii string at most strmax chars long
+*/
er_ret_t
IP_rang_b2a(ip_range_t *rangptr, char *ascaddr, int strmax)
/* [<][>][^][v][top][bottom][index][help] */
{
int strl=0, strleft;
er_ret_t err;
strleft = strmax - strl;
if( (err=IP_addr_b2a (&(rangptr->begin), ascaddr, strleft)) != IP_OK) {
return err;
}
strl = strlen(ascaddr);
strleft = strmax - strl;
if( strleft < 5 ) {
return IP_TOSHRT;
}
strcat( ascaddr, " - " );
strl += 3;
strleft = strmax - strl;
if( (err=IP_addr_b2a (&(rangptr->end), ascaddr+strl, strleft)) != IP_OK) {
return err;
}
return IP_OK;
}
/***************************************************************************/
/*+ return the bitnum bit of the address,
COUNTING FROM THE TOP !!!!! ,
starting with 0 for the *most significant bit*.
+*/
int
IP_addr_bit_get(ip_addr_t *binaddr, int bitnum) {
/* [<][>][^][v][top][bottom][index][help] */
register int bitval;
// IPv4 is easy...
bitval = (binaddr->words[0] & (0x80000000 >> (bitnum)));
return (bitval != 0);
}
/***************************************************************************/
/*+ set the bitnum bit of the address to bitval,
COUNTING FROM THE TOP !!!!! ,
starting with 0 for the *most significant bit*.
+*/
void
IP_addr_bit_set(ip_addr_t *binaddr, int bitnum, int bitval) {
/* [<][>][^][v][top][bottom][index][help] */
// IPv4 is easy...
if ( bitval == 1 )
binaddr->words[0] |= (0x80000000 >> (bitnum));
else
binaddr->words[0] &= ~(0x80000000 >> (bitnum));
}
/***************************************************************************/
/*+ this fixes a prefix by setting insignificant bits to 0 +*/
void
IP_pref_bit_fix( ip_prefix_t *prefix )
/* [<][>][^][v][top][bottom][index][help] */
{
unsigned mask=0xffffffff;
// shorthand for ipv4
// Shifting out by 32 bits does NOT turn all bits into 0...
if( prefix->bits < 32 ) {
prefix->ip.words[0] &= ~(mask >> prefix->bits);
}
#if 0
int i;
for(i=prefix->bits; i < IP_sizebits(prefix->ip.space) ; i++) {
IP_addr_bit_set( & prefix->ip, i, 0);
}
#endif
}
/***************************************************************************/
/*+
This is a hook function for use with g_list_foreach, to print a list
of prefixes
+*/
void ip_print_prefix(void *dataptr, void *junk) {
/* [<][>][^][v][top][bottom][index][help] */
char ascpref[IP_PREFSTR_MAX];
ip_prefix_t *binpref=dataptr;
IP_pref_b2a( binpref, ascpref, IP_PREFSTR_MAX );
printf ("prefix: %s\n", ascpref);
}
/***************************************************************************/
/*+ compares two IP addresses up to the bit # len,
returns 0 if equal, 1 if ptra greater, -1 if ptrb greater.
It is the responsility of the caller to ensure that both addresses
are from the same IP space.
+*/
int
IP_addr_cmp(ip_addr_t *ptra, ip_addr_t *ptrb, int len)
/* [<][>][^][v][top][bottom][index][help] */
{
int a,b,i;
for(i=0; i<len; i++) {
a=IP_addr_bit_get(ptra, i);
b=IP_addr_bit_get(ptrb, i);
if( a != b ) {
if( a > b ) return 1;
else return -1;
}
}
return 0;
}
/***************************************************************************/
/*+
this is a shorthand notation to pull out the first word of the address.
it is defined for the scope od the following functions
+*/
#define ad(which) (rangptr->which)
/* [<][>][^][v][top][bottom][index][help] */
/***************************************************************************/
/*+ calculate the span of a range == size - 1 +*/
ip_rangesize_t
IP_rang_span( ip_range_t *rangptr )
/* [<][>][^][v][top][bottom][index][help] */
{
// IPv4:
return ad(end).words[0] - ad(begin).words[0];
}
/***************************************************************************/
/*+ Decomposes a binary range into prefixes and appends them to the list.
Allocates prefix structures and list elements, they must be freed after use.
returns a bitmask of prefix lengths used.
+*/
unsigned
IP_rang_decomp(ip_range_t *rangptr, GList **preflist)
/* [<][>][^][v][top][bottom][index][help] */
{
unsigned prefmask=0;
register int slash=0;
register unsigned c_dif, blk, ff;
ip_range_t workrange;
ip_addr_t workbegin;
ip_addr_t workend;
ip_prefix_t *prefptr;
if( ad(begin).words[0] > ad(end).words[0] ) { // has gone too far
return 0;
}
if( ad(begin).words[0] == ad(end).words[0] ) { // one IP, i.e. /32 for IPv4
prefmask |= 1;
if( wr_calloc( (void **)& prefptr, sizeof(ip_prefix_t), 1) != UT_OK) {
die;
}
prefptr->ip = ad(begin);
prefptr->bits = 32;
*preflist = g_list_append( *preflist, prefptr );
return prefmask;
}
c_dif = ad(end).words[0] - ad(begin).words[0];
// initialize work vars
workbegin = ad(begin);
workend = ad(end);
// now find the biggest block fitting in this range
// i.e. the first 2^n number smaller than c_dif
// the loop would not work for /0 (some stupid queries may have that)
// so this must be checked for separately
if( c_dif == 0xffffffff ) {
// they are already set to 0.0.0.0 - 255.255.255.255
// leave them alone.
blk = 0;
slash = 0;
}
else {
c_dif += 1; // was not done earlier to protect from overflow
for(slash=1;
slash<32 && ((blk=((unsigned)0x80000000>>(slash-1))) & c_dif) == 0;
slash++) {}
// clear all digits in a and b under the blk one.
ff=blk-1;
workbegin.words[0] = (workbegin.words[0] + ff) & ~ff;
workend.words[0] = (workend.words[0] + 1) & ~ff;
}
if( workbegin.words[0] != workend.words[0] ) {
prefmask |= blk;
if( wr_malloc( (void **)& prefptr, sizeof(ip_prefix_t)) != UT_OK) {
die;
}
prefptr->ip = workbegin;
prefptr->bits = slash;
*preflist = g_list_append( *preflist, prefptr );
}
if( ad(begin).words[0] != workbegin.words[0] ) {
workrange.begin = ad(begin);
workbegin.words[0] -= 1;
workrange.end = workbegin;
prefmask |= IP_rang_decomp( &workrange, preflist );
}
// here we must protect from decomposition of
// 255.255.255.255 - 255.255.255.255 in case the range
// 0.0.0.0 - 255.255.255.255 is considered. Hence the slash>0 condition.
if( workend.words[0] <= ad(end).words[0] && slash > 0) {
workrange.begin = workend;
workrange.end = ad(end);
prefmask |= IP_rang_decomp( &workrange, preflist );
}
return prefmask;
}
/***************************************************************************/
/*+ Similar name, slightly different code, totally different functionality.
finds the smallest canonical block encompassing the whole given range,
then MODIFIES the range pointed to by the argument
so that it's equal to this block.
returns a bitmask of prefix length used.
+*/
unsigned
IP_rang_encomp(ip_range_t *rangptr)
/* [<][>][^][v][top][bottom][index][help] */
{
unsigned prefmask=0;
int slash=0;
unsigned c_dif, blk, ff, t_dif;
ip_range_t workrange;
ip_addr_t workbegin;
ip_addr_t workend;
c_dif = ad(end).words[0] - ad(begin).words[0];
// now find the biggest block fitting in this range
// i.e. the first 2^n number smaller than c_dif
// the loop would not work for /0 (some stupid queries may have that)
// so this must be checked for separately
if( c_dif > 0x80000000 ) {
slash = 0;
ff = 0xffffffff;
blk = 0;
workbegin = workend = ad(begin);
workbegin.words[0] = 0;
workend.words[0] = ff;
}
else {
do {
c_dif += 1;
// find the smallest block ENCOMPASSING c_dif.
// this implies a loop from the bottom up
for(slash=32;
slash>1 && (blk=((unsigned)0x80000000>>(slash-1))) < c_dif;
slash--) {}
ff=blk-1;
// clear all digits in workbegin under the blk one.
workbegin = ad(begin);
workbegin.words[0] = workbegin.words[0] & ~ff;
// see if it has not made the difference larger than blk,
// retry if so
t_dif = c_dif;
c_dif = ad(end).words[0] - workbegin.words[0];
} while( c_dif >= t_dif );
// set the endpoint to workbegin + blocksize - 1
// which amounts to + ff
workend = ad(begin);
workend.words[0] = workbegin.words[0] + ff;
}
// set the range to new values
rangptr->begin = workbegin;
rangptr->end = workend;
}
/***************************************************************************/
/*+ sets a range equal to a prefix +*/
er_ret_t
IP_pref_2_rang( ip_range_t *rangptr, ip_prefix_t *prefptr )
/* [<][>][^][v][top][bottom][index][help] */
{
ip_rangesize_t span;
ad(begin) = ad(end) = prefptr->ip;
if( prefptr->bits > 0 ) {
span = (1 << (32 - prefptr->bits)) - 1 ;
}
else {
span = 0xffffffff;
}
ad(end).words[0] += span;
return IP_OK;
}
#undef ad
/***************************************************************************/
/*+
This is to parse a classfull address into a range.
Takes the address by pointer from addrptr and puts the result
at rangptr.
Throws error if the address does not fall into any of the
classfull categories
+*/
er_ret_t
IP_rang_classful( ip_range_t *rangptr, ip_addr_t *addrptr)
/* [<][>][^][v][top][bottom][index][help] */
{
int i;
unsigned b[4];
if( addrptr->space != IP_V4 ) {
// it's IPv6. There are no classful ranges or anything like that.
// we accept only explicit ranges
die;
}
rangptr->begin = *addrptr;
rangptr->end.space = IP_V4;
for(i=0; i<4; i++) {
rangptr->end.words[i] = 0;
}
/* assume it's at least a valid IP. let's try different classes now */
// we could have used a union here, but it would not work on
// low endians. So byte by byte copying to and from an array.
for(i=0; i<4; i++) {
b[i] = ( rangptr->begin.words[0] & (0xFF << i*8) ) >> i*8;
}
if( b[3] >= 1 && b[3] < 128
&& b[2] == 0 && b[1] == 0 && b[0] == 0 ) {
b[2]=b[1]=b[0]=255;
}
else if( b[3] >= 128 && b[3] < 192
&& b[1] == 0 && b[0] == 0 ) {
b[1]=b[0]=255;
}
else if( b[3] >= 192 && b[3] < 224
&& b[0] == 0 ) {
b[0]=255;
}
else if( b[3] >= 224 && b[3] < 255 ) {
// just leave it, make it a /32, i.e. begin == end
}
else {
// Leave it and make it a /32
// This is AGAINST the rule! but we have some junk
// so we have to compensate for it.
}
// copy the (now - modified) bytes into the end of range
for(i=0; i<4; i++) {
rangptr->end.words[0] |= (b[i] << i*8);
}
return IP_OK;
}
/***************************************************************************/
/*+
Trying to be smart :-) and convert a query search term into prefix(es),
regardless of whether specified as IP address, prefix or range.
justcheck - if just checking the syntax (justcheck == 1),
then the prefixes are freed before the function returns,
otherwise it is the responsibility of the caller to free the list.
+*/
er_ret_t
IP_smart_conv(char *key,
/* [<][>][^][v][top][bottom][index][help] */
int justcheck,
int encomp,
GList **preflist,
ip_exp_t expf)
{
int free_it;
er_ret_t call_err, err=IP_OK; // let's be optimistic :-)
ip_prefix_t *querypref;
/* if just checking the syntax (justcheck == 1),
then free_it = 1,
else 0, but may be modified later (in range conversion)
*/
free_it = justcheck;
if( (call_err = wr_malloc( (void **) &querypref, sizeof(ip_prefix_t)))
!= UT_OK) {
return call_err;
}
if( IP_pref_t2b(querypref, key, expf) == IP_OK ) {
if( justcheck == 0) {
*preflist = g_list_append(*preflist, querypref);
}
}
else {
// not a prefix.
// Maybe an IP ?
if( IP_addr_t2b( &(querypref->ip), key, expf) == IP_OK ) {
//convert to a /32
querypref->bits = 32;
if( justcheck == 0) {
*preflist = g_list_append(*preflist, querypref);
}
}
else {
// hm, maybe a range then ?
ip_range_t myrang;
// won't use the querypref anymore, mark it for freeing later
free_it = 1;
if( IP_rang_t2b(&myrang, key, expf) == IP_OK ) {
// Wow. Great.
// sometimes (exless match) we look for the first bigger(shorter)
// prefix containing this range.
if( encomp ) {
IP_rang_encomp(&myrang);
}
// OK, now we can let the engine happily find that it's just one
// range
if( justcheck == 0) {
IP_rang_decomp(&myrang, preflist);
}
}
else {
err = IP_INVARG; // "conversion error"
}
}
}
if( free_it ) {
wr_free(querypref);
}
return err;
}
#ifdef MODULE_TEST
#include "ip_test.c"
#endif