4

Is there a clever/tricky way to analyze if a string represented IP address is valid and to recognize its version, so that to be able to convert it to the appropriate structure, just by using the UNIX API?

I don't want to use regex, no need to add dependency to additional libraries just about this.


My first approach was:

in_addr addr;
memset( &addr, 0, sizeof( in_addr ) );
// try to convert from standard numbers-and-dots notation into binary data
if( 0 != inet_aton( sIPAddress.c_str(), &addr ) )
{
    return Socket::enIPv4;      // valid IPv4
}

in6_addr addr6;
memset( &addr6, 0, sizeof( in6_addr ) );
if( inet_pton( AF_INET6, sIPAddress.c_str(), &addr6 ) > 0 )
{
    return Socket::enIPv6;      // valid IPv6
}

return Socket::enUnknown;

The problem here is, that if I pass string like 1, it's successfully converted to IPv4. String like 11111 is converted to IPv4, too. By documentation:

inet_aton() returns non-zero if the address is valid, zero if not.

Obviously, this function recognizes not only XXX.XXX.XXX.XXX format, but does something more internally.

Of course I can write my own function(and it's going to be fun, actually), by analyzing the string, but I wanted to use already existing and tested functions, instead. Is it possible or I should write my own?

0

4 Answers 4

4

As per the manual page, strings like "a", "a.b" and "a.b.c" are all valid addresses for inet_aton. If you only want the "normal" dotted-decimal, use inet_pton for those addresses too.

Sign up to request clarification or add additional context in comments.

4 Comments

LOL, the info in this link is not in the man page of RHEL4/5 XD Anyway, inet_pton does the same thing. So no, it does not solve my issue :\
@KirilKirov In that case there is a bug in inet_pton, it should only recognize valid dotted-decimal.
interesting. I'm testing on RHE5U3, it's not that old.
OMFG :X This is totally my bad. You're terribly right about inet_pton. Thanks a lot, accepted and +1.
3

If you're open to Boost libraries check out Boost Asio

The boost::asio::ip::address class is very good at parsing IPv6 and IPv4 addresses and beats trying to come up with regexes and the like to verify input. It's also cross platform.

#include <boost/asio.hpp>
#include <boost/assert.hpp>

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char* argv[]){

    std::string ipv4("192.168.1.1");
    std::string ipv6("::1");
    std::string notIP("1");

    boost::asio::ip::address ipv4Addr = boost::asio::ip::address::from_string(ipv4);
    BOOST_ASSERT(ipv4Addr.is_v4() == true);

    boost::asio::ip::address ipv6Addr = boost::asio::ip::address::from_string(ipv6);
    BOOST_ASSERT(ipv6Addr.is_v6() == true);

    boost::asio::ip::address badAddr;
    try{
        badAddr = boost::asio::ip::address::from_string(notIP);
    }
    catch(const std::exception& e){
        std::cout << "Bad Address:  " << e.what() << std::endl;
    }   

    return 0;
}

This prints:

$ g++ -Wall -I/usr/local/boost-1.47.0/include/ -L/usr/local/boost-1.47.0/lib -o ./asioTestIP ./asioTestIP.cpp -lboost_system
$ ./asioTestIP 
Bad Address:  Invalid argument

1 Comment

Thanks, but I clearly asked only about the standard UNIX API, as I don't want to add dependency to another library just about this, as I can write my own function, that will do this. Thanks anyway :)
0

This should work:

#include <iostream>
#include <string>
#include <vector>
#include <stdlib.h>

void printError(const std::string& errStr) {
    std::cerr << errStr << std::endl;
}

bool checkAddress(const std::string& address) {
    std::vector<std::string> arr;
    int k = 0;
    arr.push_back(std::string());
    for (std::string::const_iterator i = address.begin(); i != address.end(); ++i) {
        if (*i == '.') {
            ++k;
            arr.push_back(std::string());
            if (k == 4) {
                printError("too many '.'");
            }
            continue;
        }
        if (*i >= '0' && *i <= '9') {
            arr[k] += *i;
        } else {
            printError("wrong character");
            return false;
        }
        if (arr[k].size() > 3) {
            printError("size exceeded");
            return false;
        }
    }
    if (k != 3) {
        printError("not enough '.'");
        return false;
    }
    for (int i = 0; i != 4; ++i) {
        const char* nPtr = arr[i].c_str();
        char* endPtr = 0;
        const unsigned long a = ::strtoul(nPtr, &endPtr, 10);
        if (nPtr == endPtr) {
            printError("invalid numeric input");
            return false;
        }
        if (a > 255) {
            printError("out of range");
            return false;
        }
    }
    return true;
}

int main(int argc, char* argv[]) {
    if (argc < 2) {
        return -1;
    }
    std::string address = argv[1];
    if (checkAddress(address)) {
        std::cout << "address ok" << std::endl;
    } else {
        std::cout << "bad address" << std::endl;
    }
    return 0;
}

1 Comment

I know how to write it, this is not my question. Thanks anyway. And, btw, this will recognize only IPv4. IPv6 supports several formats (3, as far as I know), which could be very error prone
0

I solved this problem once in a project. First I made an EBNF of ip-addresses and then created the algorithm. It works quit fine and doesn't use any foreign libraries. You have to call the method CheckWholeIp and hand it the ip-string (example: CheckWholeIp("127.0.0.1"). It'll return int 0 if it fits in the ipv4 scheme and 1 if it's not. But it is imortand to note, that addresses like 127.055.008.001 would be accepted too. There was no need to filter foregoing zeros. Let me know your thoughts ;)

cpp file:

    #include "CheckIp.h"

CheckIp::CheckIp()
{
}

int CheckIp::CheckWholeIp(string s)
{

    int size = s.size();
    int psum = 0;
    //check amount of points
    for (int i = 0; i <= size; i++) {
        if (s[i] == '.')psum++;
    }
    if (psum != 3)return 1;

    //write stringblocks
    string sbl0 = "";
    string sbl1 = "";
    string sbl2 = "";
    string sbl3 = "";
    int ii = 0;

    while (s[ii] != '.') {
        sbl0 = sbl0 + s[ii];
        ii++;
    }
    ii++;
    while (s[ii] != '.') {
        sbl1 = sbl1 + s[ii];
        ii++;
    }
    ii++;
    while (s[ii] != '.') {
        sbl2 = sbl2 + s[ii];
        ii++;
    }
    ii++;
    while (s[ii] != NULL) {
        sbl3 = sbl3 + s[ii];
        ii++;
    }

    //checking the blocks
    if (CheckBlock(sbl0) == 1 | CheckBlock(sbl1) == 1 | CheckBlock(sbl2) == 1 | CheckBlock(sbl3) == 1) return 1;

    return 0;
}

int CheckIp::CheckBlock(string s)
{
    int sizeb = s.size();
    //checking size of block
    if (sizeb > 3) return 1;
    string ss;
    for (int i = 0; i < sizeb; i++) {
        ss = s[i];
        if (CheckNumber(ss) == 1) return 1;
    }

    return 0;
}

int CheckIp::CheckNumber(string s)
{
    if (s == "0" | s == "1" | s == "2" | s == "3" | s == "4" | s == "5" | s == "6" | s == "7" | s == "8" | s == "9") {
        return 0;
    }
    else {
        return 1;
    }

}

Header:

    #pragma once
#include <string>

using namespace std;

ref class CheckIp
{
public:
    CheckIp();

    static int CheckWholeIp(string s);

private: static int CheckBlock(string s);

        static int CheckNumber(string s);

};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.