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_revd_t2b
- IP_rang_t2b
- IP_addr_b2_space
- IP_addr_b2v4_addr
- IP_addr_b2v6_hi
- IP_addr_b2v6_lo
- IP_pref_b2_space
- IP_pref_b2v4_len
- IP_pref_b2v4_addr
- IP_pref_b2v6_len
- IP_rang_b2_space
- IP_addr_b2v4
- IP_pref_b2v4
- IP_pref_b2v6
- IP_rang_b2v4
- IP_addr_v4_mk
- IP_addr_v6_mk
- IP_pref_v4_mk
- IP_rang_v4_mk
- IP_pref_a2v4
- IP_pref_a2v6
- IP_revd_a2v4
- IP_addr_a2v4
- IP_rang_a2v4
- IP_addr_f2b_v4
- IP_rang_f2b_v4
- IP_pref_f2b_v4
- IP_addr_f2b_v6
- IP_pref_f2b_v6
- IP_addr_s2b
- IP_addr_b2a
- IP_pref_b2a
- IP_rang_b2a
- IP_addr_bit_get
- IP_addr_bit_set
- IP_pref_bit_fix
- IP_addr_cmp
- IP_rang_span
- ad
- IP_rang_decomp
- IP_rang_encomp
- IP_pref_2_rang
- IP_rang_classful
- IP_smart_conv
- IP_smart_range
/***************************************
$Revision: 1.19 $
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, COMPLETE
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.
***************************************/
#define IP_IMPL
#include <iproutines.h>
#include <string.h>
#include <stdio.h>
#include <erroutines.h>
#include <ctype.h>
#include <memwrap.h>
#include <numconv.h>
#include <stubs.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <inet6def.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 "expanded"
(missing octets will be set to 0, MSB's will be set).
In IP_PLAIN mode the routine will complain if it sees less octets.
why not use the standard inet_blabla routine ?
it's because if some octets are missing, we make the address zero-padded
(unlike the inet_blabla, which puts zeros in the middle). We also want
to control the expansion with a flag.
+*/
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 {
char *olddot = dot+1;
/* dot should point to the "end of this number", not necessarily a dot */
if ( (dot = index (addr, '.')) == NULL) {
/* after the ip it can contain lots of junk spaces */
while( *olddot != 0 && ! isspace(* (unsigned char *) olddot) ) {
olddot++;
}
dot = olddot;
last = 1;
}
else {
if( ++dotsfound > 3 ) {
/* handle syntax ERROR - too many dots found */
return IP_INVIP4;
}
}
if ((len = dot - addr) > 3) {
/* syntax ERROR - too many digits in an octet */
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 */
#define _IPV6_LENGTH 128
char addrcpy[_IPV6_LENGTH];
char *ch, *start;
strncpy(addrcpy, addr, _IPV6_LENGTH-1);
addrcpy[_IPV6_LENGTH-1] = 0;
/* get rid of superfluous whitespaces */
/* leading... */
for( ch = start = addrcpy ; *ch != 0; ch++ ) {
if( isspace( (int) *ch) ) {
start++;
}
else {
break;
}
}
/* and trailing */
while( *ch != 0 ) {
if( isspace( (int) *ch) ) {
*ch = 0;
break;
}
ch++;
}
if( inet_pton(AF_INET6, start, (ipptr->words)) == 0 ) {
return IP_NO6YET;
}
ipptr->space = IP_V6;
#undef _IPV6_LENGTH
}
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 *trash;
char *slash;
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 not enough */
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(* (unsigned char*) trash); /* cast for stupid gcc */
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
|| prefptr->bits > IP_sizebits(prefptr->ip.space))
{
/* 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;
}
/**************************************************************************/
/*+ converts an inaddr/ip6int string into a binary prefix.
no distinction is made with respect to "expand" argument.
+*/
er_ret_t
IP_revd_t2b(ip_prefix_t *prefptr, char *prefstr, ip_exp_t expf)
/* [<][>][^][v][top][bottom][index][help] */
{
char ip[256], temp[256];
char *arpa;
int len, octets=0, goon=1;
char *dot;
er_ret_t err;
dieif( expf != IP_PLAIN && expf != IP_EXPN );
if( (arpa=strstr(prefstr, ".in-addr.arpa")) == NULL ) {
#if 0 /* XXX no yet implemented */
if( (arpa=strstr(prefstr, ".ip6.int")) != NULL ) {
/* ipv6 */
}
#endif
return IP_NOREVD;
}
else {
/* copy the IP part to another string, ERROR if 256 chars not enough */
len = arpa - prefstr;
if( len > 255 ) {
/* die; */ /* ERROR - ip address part of the string too long. */
return IP_ADTOLO;
}
strncpy(temp, prefstr, len);
temp[len]=0;
/* now : get the octets reversed one by one. */
ip[0]=0; /* init */
do {
if( (dot = strrchr( temp, '.' )) == NULL ) {
goon = 0;
dot = temp;
}
strcat(ip, dot + ( goon ) );
octets++;
/* add a dot, unless that was the last octet */
if( goon ) {
strcat(ip, ".");
}
*dot = 0;
} while( goon );
if( (err=IP_addr_t2b( &(prefptr->ip), ip, IP_EXPN)) != IP_OK) {
/* die; */ /* set error flag: incorrect address format */
return err;
}
prefptr->bits = octets * 8;
}
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;
}
}
/**************************************************************************/
/* accessor functions */
/******** address **********/
unsigned IP_addr_b2_space(ip_addr_t *addrptr)
/* [<][>][^][v][top][bottom][index][help] */
{
return addrptr->space;
}
unsigned IP_addr_b2v4_addr(ip_addr_t *addrptr)
/* [<][>][^][v][top][bottom][index][help] */
{
dieif( addrptr->space != IP_V4 );
return addrptr->words[0];
}
/* ipv4 */
ip_v6word_t IP_addr_b2v6_hi(ip_addr_t *addrptr)
/* [<][>][^][v][top][bottom][index][help] */
{
dieif( addrptr->space != IP_V6 );
return ( (((ip_v6word_t) addrptr->words[0]) << 32)
+ (((ip_v6word_t) addrptr->words[1]) ));
}
ip_v6word_t IP_addr_b2v6_lo(ip_addr_t *addrptr)
/* [<][>][^][v][top][bottom][index][help] */
{
dieif( addrptr->space != IP_V6 );
return ( (((ip_v6word_t) addrptr->words[2]) << 32)
+ (((ip_v6word_t) addrptr->words[3]) ));
}
/******** prefix **********/
unsigned IP_pref_b2_space(ip_prefix_t *prefix) {
/* [<][>][^][v][top][bottom][index][help] */
return IP_addr_b2_space( &(prefix->ip) );
}
/* ipv4 */
unsigned IP_pref_b2v4_len(ip_prefix_t *prefix) {
/* [<][>][^][v][top][bottom][index][help] */
dieif( prefix->ip.space != IP_V4 );
return prefix->bits;
}
unsigned IP_pref_b2v4_addr(ip_prefix_t *prefix) {
/* [<][>][^][v][top][bottom][index][help] */
return IP_addr_b2v4_addr( &(prefix->ip) );
}
/* ipv6 */
unsigned IP_pref_b2v6_len(ip_prefix_t *prefix) {
/* [<][>][^][v][top][bottom][index][help] */
dieif( prefix->ip.space != IP_V6 );
return prefix->bits;
}
/* range */
unsigned IP_rang_b2_space(ip_range_t *myrang) {
/* [<][>][^][v][top][bottom][index][help] */
/* hardwire to IPV4 for now */
return IP_V4;
}
/*
* complex conversions (return void, set values through pointers *
*/
void IP_addr_b2v4(ip_addr_t *addrptr, unsigned *address) {
/* [<][>][^][v][top][bottom][index][help] */
*address = IP_addr_b2v4_addr(addrptr);
}
void IP_pref_b2v4(ip_prefix_t *prefptr,
/* [<][>][^][v][top][bottom][index][help] */
unsigned int *prefix,
unsigned int *prefix_length)
{
*prefix = IP_addr_b2v4_addr( &(prefptr->ip));
*prefix_length = IP_pref_b2v4_len(prefptr);
}
void IP_pref_b2v6(ip_prefix_t *prefptr,
/* [<][>][^][v][top][bottom][index][help] */
ip_v6word_t *high,
ip_v6word_t *low,
unsigned int *prefix_length)
{
*high = IP_addr_b2v6_hi( &(prefptr->ip));
*low = IP_addr_b2v6_lo( &(prefptr->ip));
*prefix_length = IP_pref_b2v6_len(prefptr);
}
void IP_rang_b2v4(ip_range_t *myrang,
/* [<][>][^][v][top][bottom][index][help] */
unsigned *begin,
unsigned *end)
{
*begin = IP_addr_b2v4_addr( &(myrang->begin));
*end = IP_addr_b2v4_addr( &(myrang->end));
}
/******** construct from raw values **********/
/******** address **********/
er_ret_t IP_addr_v4_mk(ip_addr_t *addrptr,
/* [<][>][^][v][top][bottom][index][help] */
unsigned addrval) {
addrptr->space = IP_V4;
addrptr->words[0] = addrval;
addrptr->words[1] = addrptr->words[2] = addrptr->words[3] = 0;
/* no real possibility of checking the syntax */
return IP_OK;
}
er_ret_t IP_addr_v6_mk(ip_addr_t *addrptr,
/* [<][>][^][v][top][bottom][index][help] */
ip_v6word_t high,
ip_v6word_t low) {
ip_v6word_t ff = 0xffffffff;
addrptr->space = IP_V6;
(addrptr->words[0]) = (high >> 32) & ff;
(addrptr->words[1]) = high & ff ;
(addrptr->words[2]) = (low >> 32) & ff;
(addrptr->words[3]) = low & ff;
/* no real possibility of checking the syntax */
return IP_OK;
}
/******** prefix **********/
er_ret_t IP_pref_v4_mk(ip_prefix_t *prefix,
/* [<][>][^][v][top][bottom][index][help] */
unsigned prefval,
unsigned preflen)
{
if( preflen > 32 ) {
die;
}
IP_addr_v4_mk(&(prefix->ip), prefval);
prefix->bits = preflen;
IP_pref_bit_fix( prefix ); /* never produce inconsistent prefixes */
return IP_OK;
}
/******** range **********/
er_ret_t IP_rang_v4_mk(ip_range_t *rangptr,
/* [<][>][^][v][top][bottom][index][help] */
unsigned addrbegin,
unsigned addrend)
{
er_ret_t err;
if( (err=IP_addr_v4_mk( &(rangptr->begin), addrbegin)) == IP_OK ) {
err=IP_addr_v4_mk( &(rangptr->end), addrend);
}
return err;
}
/**************************************************************************/
/**************************************************************************/
/*+ a2v4 == functions to convert the ascii representation into binary,
* and then set the unsigned values at the pointers provided.
*
+*/
/* Convert route string into numbers */
/* ipv4 */
er_ret_t
IP_pref_a2v4(char *avalue, ip_prefix_t *pref,
/* [<][>][^][v][top][bottom][index][help] */
unsigned *prefix, unsigned *prefix_length)
{
er_ret_t ret;
if((ret = IP_pref_e2b(pref, avalue)) == IP_OK) {
IP_pref_b2v4(pref, prefix, prefix_length);
}
return(ret);
}
/* ipv6 */
er_ret_t
IP_pref_a2v6(char *avalue, ip_prefix_t *pref,
/* [<][>][^][v][top][bottom][index][help] */
ip_v6word_t *high, ip_v6word_t *low,
unsigned *prefix_length)
{
er_ret_t ret;
if((ret = IP_pref_e2b(pref, avalue)) == IP_OK) {
IP_pref_b2v6(pref, high, low, prefix_length);
}
return(ret);
}
/* Convert reverse domain string into numbers */
er_ret_t
IP_revd_a2v4(char *avalue, ip_prefix_t *pref,
/* [<][>][^][v][top][bottom][index][help] */
unsigned int *prefix, unsigned int *prefix_length)
{
er_ret_t ret;
if((ret = IP_revd_e2b(pref, avalue)) == IP_OK) {
IP_pref_b2v4(pref, prefix, prefix_length);
}
return(ret);
}
/* Convert ip addr string into numbers */
er_ret_t
IP_addr_a2v4(char *avalue,ip_addr_t *ipaddr, unsigned int *address)
/* [<][>][^][v][top][bottom][index][help] */
{
er_ret_t ret;
if((ret = IP_addr_e2b(ipaddr, avalue)) == IP_OK) {
IP_addr_b2v4(ipaddr, address);
}
return(ret);
}
/* Convert inetnum attribute into numbers */
er_ret_t
IP_rang_a2v4(char *rangstr, ip_range_t *myrang,
/* [<][>][^][v][top][bottom][index][help] */
unsigned int *begin_in, unsigned int *end_in)
{
er_ret_t ret;
if( (ret=IP_rang_e2b(myrang, rangstr)) == IP_OK ) {
#if 0 /* no IPv4 classful ranges anymore */
if( IP_addr_e2b( &(myrang->begin), rangstr ) == IP_OK )
if ((ret=IP_rang_classful( myrang , &(myrang->begin))) == IP_OK )
;
#endif
IP_rang_b2v4(myrang, begin_in, end_in);
}
return (ret);
}
/* *********************************************************************
f2b - free numbers represented in ascii into a binary struct
********************************************************************* */
er_ret_t
IP_addr_f2b_v4(ip_addr_t *addrptr, char *adrstr)
/* [<][>][^][v][top][bottom][index][help] */
{
unsigned address;
if( ut_dec_2_uns(adrstr, &address) < 0 ) {
return IP_INVARG;
}
return IP_addr_v4_mk(addrptr, address);
}
er_ret_t
IP_rang_f2b_v4(ip_range_t *rangptr, char *beginstr, char *endstr)
/* [<][>][^][v][top][bottom][index][help] */
{
if( IP_addr_f2b_v4( &(rangptr->begin), beginstr) != IP_OK
|| IP_addr_f2b_v4( &(rangptr->end), endstr) != IP_OK) {
return IP_INVARG;
}
else {
return IP_OK;
}
}
er_ret_t
IP_pref_f2b_v4(ip_prefix_t *prefptr, char *prefixstr, char *lengthstr)
/* [<][>][^][v][top][bottom][index][help] */
{
if( IP_addr_f2b_v4( &(prefptr->ip), prefixstr) != IP_OK
|| ut_dec_2_uns(lengthstr, &(prefptr->bits) ) < 0
|| prefptr->bits > IP_sizebits(prefptr->ip.space)) {
return IP_INVARG;
}
IP_pref_bit_fix(prefptr); /* never create broken binary prefixes. */
return IP_OK;
}
er_ret_t
IP_addr_f2b_v6(ip_addr_t *addrptr, char *msbstr, char *lsbstr )
/* [<][>][^][v][top][bottom][index][help] */
{
ip_v6word_t high, low;
if( sscanf(msbstr, "%llu", &high) < 1 ||
sscanf(lsbstr, "%llu", &low) < 1 ) {
return IP_INVARG;
}
return IP_addr_v6_mk(addrptr, high, low);
}
er_ret_t
IP_pref_f2b_v6(ip_prefix_t *prefptr, char *msbstr, char *lsbstr, char *lengthstr)
/* [<][>][^][v][top][bottom][index][help] */
{
if( IP_addr_f2b_v6( &(prefptr->ip), msbstr, lsbstr ) != IP_OK
|| ut_dec_2_uns(lengthstr, &(prefptr->bits) ) < 0
|| prefptr->bits > IP_sizebits(prefptr->ip.space)) {
return IP_INVARG;
}
IP_pref_bit_fix(prefptr); /* never create broken binary prefixes. */
return IP_OK;
}
/**************************************************************************/
/*+ convert the socket's idea of address into a binary range struct.
space select the address type (and consequently struct type)
*/
er_ret_t
IP_addr_s2b(ip_addr_t *addrptr,
/* [<][>][^][v][top][bottom][index][help] */
void *addr_in,
int addr_len)
{
if( addr_len == sizeof(struct sockaddr_in)
&& ((struct sockaddr_in *)addr_in)->sin_family == AF_INET ) {
addrptr->space = IP_V4;
addrptr->words[0] =
ntohl( ((struct sockaddr_in*)addr_in)->sin_addr.s_addr);
/* set remaining limbs to zero */
addrptr->words[1] = addrptr->words[2] = addrptr->words[3] = 0;
}
else { /* unsupported family or invalid struct */
die;
}
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;
}
}
else {
/* IPv6 */
if( inet_ntop(AF_INET6, &(binaddr->words[0]), ascaddr, strmax)
== NULL ) {
return IP_TOSHRT;
}
/* 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] */
int bitval;
int w,c;
/* avoid unnecessary division */
if( binaddr->space == IP_V4 ) {
w = 0;
c = bitnum;
}
else {
w = bitnum / 32;
c = bitnum % 32;
}
bitval = (binaddr->words[w] & (0x80000000 >> (c)));
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] */
int w,c;
/* avoid unnecessary division */
if( binaddr->space == IP_V4 ) {
w = 0;
c = bitnum;
}
else {
w = bitnum / 32;
c = bitnum % 32;
}
if ( bitval == 1 )
binaddr->words[w] |= (0x80000000 >> (c));
else
binaddr->words[w] &= ~(0x80000000 >> (c));
}
/**************************************************************************/
/*+ this fixes a prefix by setting insignificant bits to 0 +*/
void
IP_pref_bit_fix( ip_prefix_t *prefix )
/* [<][>][^][v][top][bottom][index][help] */
{
if( prefix->ip.space == IP_V4 ) {
ip_limb_t 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);
}
}
else {
int i;
for(i=prefix->bits; i < IP_sizebits(prefix->ip.space) ; i++) {
IP_addr_bit_set( & prefix->ip, i, 0);
}
}
}
/**************************************************************************/
/*+ 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;
}
/**************************************************************************/
/*+ 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 rangptr.end.words[0] - rangptr.begin.words[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] */
/**************************************************************************/
/*+ 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;
dieif( rangptr->begin.space != IP_V4 );
if( ad(begin).words[0] > ad(end).words[0] ) { /* has gone too far */
return 0;
}
if( ad(begin).words[0] == ad(end).words[0] ) { /* an IP == a /32 (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.
+*/
void IP_rang_encomp(ip_range_t *rangptr)
/* [<][>][^][v][top][bottom][index][help] */
{
int slash=0;
unsigned c_dif, blk, ff, t_dif;
ip_addr_t workbegin;
ip_addr_t workend;
dieif( rangptr->begin.space != IP_V4 );
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;
dieif( rangptr->begin.space == IP_V4 );
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. */
die;
}
rangptr->begin = *addrptr;
rangptr->end.space = IP_V4;
/* initisalise end to zero */
for(i=0; i<IPLIMBNUM; 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 */
/* EMPTY */;
}
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. */
/* EMPTY */;
}
/* 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.
XXX must make sure all memory is freed if INVARG is returned
+*/
er_ret_t
IP_smart_conv(char *key,
/* [<][>][^][v][top][bottom][index][help] */
int justcheck,
int encomp,
GList **preflist,
ip_exp_t expf,
ip_keytype_t *keytype
)
{
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 ) {
*keytype = IPK_PREFIX;
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 ) {
*keytype = IPK_IP;
/*convert to a /32 or /128*/
querypref->bits = IP_sizebits(querypref->ip.space);
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. */
*keytype = IPK_RANGE;
/* 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 {
*keytype = IPK_UNDEF;
err = IP_INVARG; /* "conversion error" */
}
}
}
if( free_it ) {
wr_free(querypref);
}
return err;
}
/* convert whatever comes into a range */
er_ret_t
IP_smart_range(char *key,
/* [<][>][^][v][top][bottom][index][help] */
ip_range_t *rangptr,
ip_exp_t expf,
ip_keytype_t *keytype
)
{
er_ret_t err=IP_OK;
GList *preflist = NULL;
/* first : is it a range ? */
if( (err = IP_rang_t2b(rangptr, key, expf)) == IP_OK ) {
*keytype = IPK_RANGE;
}
else {
/* OK, this must be possible to convert it to prefix and from there
to a range. */
if( (err = IP_smart_conv(key, 0, 0, &preflist, expf, keytype))
== IP_OK ) {
dieif( g_list_length(preflist) != 1 );
dieif(IP_pref_2_rang( rangptr, g_list_first(preflist)->data ) != IP_OK );
}
}
wr_clear_list( &preflist );
return err;
}