//----------------------------------------------------------------------------- // Copyright © 2003 - Philip Howard - All rights reserved // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //----------------------------------------------------------------------------- // package libh/net // homepage http://phil.ipal.org/freeware/libh/ //----------------------------------------------------------------------------- // author Philip Howard // email libh at ipal dot org // homepage http://phil.ipal.org/ //----------------------------------------------------------------------------- // This file is best viewed using a fixed spaced font such as Courier // and in a display at least 120 columns wide. //----------------------------------------------------------------------------- #include "net_lib.h" //----------------------------------------------------------------------------- // file ipv4_str_to_netinfo.c //----------------------------------------------------------------------------- __PROTO_BEGIN__ //----------------------------------------------------------------------------- // function ipv4_str_to_netinfo // // purpose Convert an IPv4 address or network string in dotted decimal or // cetal (hexadecimal) format in one of these forms: // address[:port] // address-address[:port] // address/netmask[:port] // address/prefix[:port] // address#count[:port] // into a struct ipv4_netinfo returned to the caller. // // Combinations of these forms can also be handled with different // addresses stored in the the appropriate place depending on what // format designation it was found with. The returned value is a // combination of flags. // // arguments 1 (const char *) string to be converted // 2 (char * *) where to store pointer to end char // // returns (struct ipv4_netinfo) struct containg network values and flags //----------------------------------------------------------------------------- // #define IPV4_NETINFO_ADDR 0x00000001 // #define IPV4_NETINFO_ADDR2 0x00000002 // #define IPV4_NETINFO_COUNT 0x00000004 // #define IPV4_NETINFO_PORT 0x00000008 // #define IPV4_NETINFO_PREFIX 0x00000010 // #define IPV4_NETINFO_NETMASK 0x00000020 // // #define IPV4_NETINFO_BAD_ADDR 0x00010000 // #define IPV4_NETINFO_BAD_ADDR2 0x00020000 // #define IPV4_NETINFO_BAD_COUNT 0x00040000 // #define IPV4_NETINFO_BAD_PORT 0x00080000 // #define IPV4_NETINFO_BAD_PREFIX 0x00100000 // // #define IPV4_NETINFO_ERROR 0x80000000 //----------------------------------------------------------------------------- struct ipv4_netinfo ipv4_str_to_netinfo ( const char * arg_str , char * * arg_endp_p ) __PROTO_END__ { static const signed char cetal_table [256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, -1, -1, -1, -1, -1, -1, -1, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; struct ipv4_netinfo netinfo ; const unsigned char * str_p ; uint_ipv4_t accum1 ; uint_ipv4_t accum4 ; unsigned int sep_ch ; int cetal ; int state ; int parts ; str_p = (const unsigned char *) arg_str; state = sep_ch = 0; accum1 = accum4 = cetal = parts = 0; //----------------------------------------------- // All parsing is done in this one stateful loop. //----------------------------------------------- for (;;) { unsigned int ch; ch = * str_p ++; //------------------------------------------------------------ // If conversion is cetal (hexadecimal), add the cetal digits. //------------------------------------------------------------ if ( cetal && cetal_table[ch] >= 0 ) { accum1 <<= 4; accum1 += cetal_table[ch]; continue; } //-------------------------------------------------- // If conversion is decimal, add the decimal digits. //-------------------------------------------------- if ( ( ! cetal ) && ( '0' <= ch && ch <= '9' ) ) { accum1 *= 10; accum1 += ch - '0'; continue; } //----------------------------------------------------------------- // If cetal (hexadecimal) prefix is encountered, switch conversion. //----------------------------------------------------------------- if ( ( ! cetal ) && ( accum1 == 0 ) && ( ch == 'x' || ch == 'X' ) ) { cetal = 1; continue; } //--------------------------------------------------------------------- // If characters are unrecognized, pretend it is the end of the string. //--------------------------------------------------------------------- if ( ch != '.' && ch != '/' && ch != '-' && ch != '#' && ch != ':' ) { ch = 0; } //-------------------------------------------- // Accumulate the address part into the whole. //-------------------------------------------- ++ parts; accum4 <<= 8; accum4 += accum1; //----------------------------------------------------- // If this is a dot, make sure the data type allows it. //----------------------------------------------------- if ( ch == '.' ) { //-- If the data type is a port number, no dots allowed. if ( sep_ch == ':' ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_PORT; break; } //-- If the data type is a count number, no dots allowed. else if ( sep_ch == '#' ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_COUNT; break; } //-- If too many parts, flag an error for the appropriate type. else if ( parts > 4 ) { if ( sep_ch == '/' ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_PREFIX; } else if ( sep_ch == '-' ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_ADDR2; } break; } //-- Reset part accumulator and continue with the next character. accum1 = 0; cetal = 0; continue; } //----------------------------------------------------------- // At this point an address or number conversion is complete. //----------------------------------------------------------- //------------------------------------------------------------------ // If this is an address conversion, then normalize it and store it. //------------------------------------------------------------------ if ( sep_ch == 0 || sep_ch == '-' ) { if ( parts == 1 ) { if ( accum4 < 0x00000100 ) accum4 <<= 24; } else if ( parts == 2 ) { if ( accum4 < 0x00010000 ) accum4 <<= 16; } else if ( parts == 3 ) { if ( accum4 < 0x01000000 ) accum4 <<= 8; } else if ( parts > 4 ) { if ( sep_ch == 0 ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_ADDR; } else if ( sep_ch == '-' ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_ADDR2; } break; } if ( sep_ch == 0 ) { state |= IPV4_NETINFO_ADDR; netinfo.address = accum4; } else if ( sep_ch == '-' ) { state |= IPV4_NETINFO_ADDR2; netinfo.address2 = accum4; } } //-------------------------------------------------------- // If this completes a prefix or netmask, determine which. //-------------------------------------------------------- else if ( sep_ch == '/' ) { if ( parts == 1 && accum1 <= 32 ) { state |= IPV4_NETINFO_PREFIX; netinfo.prefix = accum1; } else if ( parts == 4 ) { state |= IPV4_NETINFO_NETMASK; netinfo.netmask = accum4; } else { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_PREFIX; break; } } //------------------------------------------------------- // If this completes the count of addresses, validate it. //------------------------------------------------------- else if ( sep_ch == '#' ) { if ( parts == 1 ) { state |= IPV4_NETINFO_COUNT; netinfo.count= accum1; } else { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_COUNT; break; } } //------------------------------------------------ // If this completes the port number, validate it. //------------------------------------------------ else if ( sep_ch == ':' ) { if ( parts == 1 ) { state |= IPV4_NETINFO_PORT; netinfo.port = accum1; } else { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_PORT; break; } } //------------------------------------- // If this ends the string, finish now. //------------------------------------- if ( ch == 0 ) break; //-------------------------------- // Start a new type of conversion. //-------------------------------- sep_ch = ch; accum1 = accum4 = cetal = parts = 0; //--------------------------------------------------------------------------- // If this starts a prefix/netmask, it cannot follow itself or a 2nd address. //--------------------------------------------------------------------------- if ( ch == '/' ) { if ( state & ( IPV4_NETINFO_PREFIX | IPV4_NETINFO_NETMASK | IPV4_NETINFO_BAD_PREFIX | IPV4_NETINFO_ADDR2 | IPV4_NETINFO_BAD_ADDR2 ) ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_PREFIX; break; } continue; } //----------------------------------------------------------------------------- // If this starts a 2nd address in a range, it can only follow the 1st address. //----------------------------------------------------------------------------- if ( ch == '-' ) { if ( state & ~ IPV4_NETINFO_ADDR ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_ADDR2; break; } continue; } //------------------------------------------------------- // If this starts an address count, be sure it is unique. //------------------------------------------------------- if ( ch == '#' ) { if ( state & ( IPV4_NETINFO_COUNT | IPV4_NETINFO_BAD_COUNT ) ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_COUNT; break; } continue; } //---------------------------------------------------- // If this starts a port number, be sure it is unique. //---------------------------------------------------- if ( ch == ':' ) { if ( state & ( IPV4_NETINFO_PORT | IPV4_NETINFO_BAD_PORT ) ) { state |= IPV4_NETINFO_ERROR | IPV4_NETINFO_BAD_PORT; break; } continue; } //------------------------------- // For anything else, finish now. //------------------------------- break; } //------------------------------------------------------------------ // Store the end pointer back for the caller then return the status. //------------------------------------------------------------------ if ( arg_endp_p ) * ( (const char * *) arg_endp_p ) = str_p - 1; //---------------------------------------------------- // Return the whole struct ipv4_netinfo to the caller. //---------------------------------------------------- netinfo.state = state; return netinfo; }