diff --git a/src/IPAddr.cc b/src/IPAddr.cc index 13fba62aa2..ff124025f9 100644 --- a/src/IPAddr.cc +++ b/src/IPAddr.cc @@ -62,7 +62,7 @@ HashKey* BuildExpectedConnHashKey(const ExpectedConn& c) void IPAddr::Mask(int top_bits_to_keep) { - if ( top_bits_to_keep <= 0 || top_bits_to_keep > 128 ) + if ( top_bits_to_keep < 0 || top_bits_to_keep > 128 ) { reporter->Error("Bad IPAddr::Mask value %d", top_bits_to_keep); return; @@ -91,7 +91,7 @@ void IPAddr::Mask(int top_bits_to_keep) void IPAddr::ReverseMask(int top_bits_to_chop) { - if ( top_bits_to_chop <= 0 || top_bits_to_chop > 128 ) + if ( top_bits_to_chop < 0 || top_bits_to_chop > 128 ) { reporter->Error("Bad IPAddr::ReverseMask value %d", top_bits_to_chop); return; @@ -118,52 +118,29 @@ void IPAddr::ReverseMask(int top_bits_to_chop) memcpy(in6.s6_addr, tmp, sizeof(in6.s6_addr)); } -std::string IPAddr::CanonIPv4(const std::string& input) - { - vector parts; - string output; - size_t start = 0; - size_t end; - - do - { - end = input.find('.', start); - string p; - bool in_leading_zeroes = true; - for ( size_t i = start; i != end && i < input.size(); ++i ) - { - if ( in_leading_zeroes && input[i] == '0' ) continue; - in_leading_zeroes = false; - p.push_back(input[i]); - } - - if ( p.size() == 0 ) - p.push_back('0'); - parts.push_back(p); - start = end + 1; - } while ( end != string::npos ); - - for ( size_t i = 0; i < parts.size(); ++i ) - { - if ( i > 0 ) - output += '.'; - output += parts[i]; - } - - return output; - } - void IPAddr::Init(const std::string& s) { if ( s.find(':') == std::string::npos ) // IPv4. { memcpy(in6.s6_addr, v4_mapped_prefix, sizeof(v4_mapped_prefix)); - if ( inet_pton(AF_INET, CanonIPv4(s).c_str(), &in6.s6_addr[12]) <=0 ) + // Parse the address directly instead of using inet_pton since + // some platforms have more sensitive implementations than others + // that can't e.g. handle leading zeroes. + int a[4]; + int n = sscanf(s.c_str(), "%d.%d.%d.%d", a+0, a+1, a+2, a+3); + + if ( n != 4 || a[0] < 0 || a[1] < 0 || a[2] < 0 || a[3] < 0 || + a[0] > 255 || a[1] > 255 || a[2] > 255 || a[3] > 255 ) { reporter->Error("Bad IP address: %s", s.c_str()); memset(in6.s6_addr, 0, sizeof(in6.s6_addr)); + return; } + + uint32_t addr = (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; + addr = htonl(addr); + memcpy(&in6.s6_addr[12], &addr, sizeof(uint32_t)); } else diff --git a/src/IPAddr.h b/src/IPAddr.h index 3b7316f095..f0c0ac12c8 100644 --- a/src/IPAddr.h +++ b/src/IPAddr.h @@ -325,14 +325,6 @@ public: unsigned int MemoryAllocation() const { return padded_sizeof(*this); } - /** - * Returns a canonicalized IPv4 dotted-decimal string such that - * leading zeroes of each decimal part are removed. - * - * @param s String containing an IPv4 address in dotted-decimal notation. - */ - static std::string CanonIPv4(const std::string& s); - private: /** * Initializes an address instance from a string representation. diff --git a/src/Val.cc b/src/Val.cc index fe7bd44481..db6e9eb23a 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -911,11 +911,19 @@ bool AddrVal::DoUnserialize(UnserialInfo* info) SubNetVal::SubNetVal(const char* text) : Val(TYPE_SUBNET) { - const char* sep = strchr(text, '/'); - if ( ! sep ) - Internal("separator missing in SubNetVal::SubNetVal"); + string s(text); + size_t slash_loc = s.find('/'); - val.subnet_val = new IPPrefix(text, atoi(sep+1)); + if ( slash_loc == string::npos ) + { + reporter->Error("Bad string in SubNetVal ctor: %s", text); + val.subnet_val = new IPPrefix(); + } + else + { + val.subnet_val = new IPPrefix(s.substr(0, slash_loc), + atoi(s.substr(slash_loc + 1).c_str())); + } } SubNetVal::SubNetVal(const char* text, int width) : Val(TYPE_SUBNET) diff --git a/src/bro.bif b/src/bro.bif index e65d1f4afa..7ffc4044fb 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -2105,7 +2105,7 @@ function counts_to_addr%(v: index_vec%): addr ## ## Returns: The :bro:type:`string` *str* as :bro:type:`int`. ## -## .. bro:see:: to_addr to_port +## .. bro:see:: to_addr to_port to_subnet function to_int%(str: string%): int %{ const char* s = str->CheckString(); @@ -2162,7 +2162,7 @@ function double_to_count%(d: double%): count ## Returns: The :bro:type:`string` *str* as unsigned integer or if in invalid ## format. ## -## .. bro:see:: to_addr to_int to_port +## .. bro:see:: to_addr to_int to_port to_subnet function to_count%(str: string%): count %{ const char* s = str->CheckString(); @@ -2257,9 +2257,11 @@ function count_to_port%(num: count, proto: transport_proto%): port ## ## ip: The :bro:type:`string` to convert. ## -## Returns: The :bro:type:`string` *ip* as :bro:type:`addr`. +## Returns: The :bro:type:`string` *ip* as :bro:type:`addr` or the unspecified +## address ``::`` if the input string does not parse correctly. ## ## .. bro:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr +## to_subnet function to_addr%(ip: string%): addr %{ char* s = ip->AsString()->Render(); @@ -2268,13 +2270,30 @@ function to_addr%(ip: string%): addr return ret; %} +## Converts a :bro:type:`string` to a :bro:type:`subnet`. +## +## sn: The subnet to convert. +## +## Returns: The *sn* string as a :bro:type:`subnet` or the unspecified subnet +## ``::/0`` if the input string does not parse correctly. +## +## .. bro:see:: to_count to_int to_port count_to_v4_addr raw_bytes_to_v4_addr +## to_addr +function to_subnet%(sn: string%): subnet + %{ + char* s = sn->AsString()->Render(); + Val* ret = new SubNetVal(s); + delete [] s; + return ret; + %} + ## Converts a :bro:type:`count` to an :bro:type:`addr`. ## ## ip: The :bro:type:`count` to convert. ## ## Returns: The :bro:type:`count` *ip* as :bro:type:`addr`. ## -## .. bro:see:: raw_bytes_to_v4_addr to_addr +## .. bro:see:: raw_bytes_to_v4_addr to_addr to_subnet function count_to_v4_addr%(ip: count%): addr %{ if ( ip > 4294967295LU ) @@ -2294,7 +2313,7 @@ function count_to_v4_addr%(ip: count%): addr ## ## Returns: The byte :bro:type:`string` *ip* as :bro:type:`addr`. ## -## .. bro:see:: raw_bytes_to_v4_addr to_addr +## .. bro:see:: raw_bytes_to_v4_addr to_addr to_subnet function raw_bytes_to_v4_addr%(b: string%): addr %{ uint32 a = 0; @@ -2317,7 +2336,7 @@ function raw_bytes_to_v4_addr%(b: string%): addr ## ## Returns: A :bro:type:`port` converted from *s*. ## -## .. bro:see:: to_addr to_count to_int +## .. bro:see:: to_addr to_count to_int to_subnet function to_port%(s: string%): port %{ int port = 0; diff --git a/testing/btest/Baseline/bifs.to_addr/error b/testing/btest/Baseline/bifs.to_addr/error new file mode 100644 index 0000000000..b8ba985f7a --- /dev/null +++ b/testing/btest/Baseline/bifs.to_addr/error @@ -0,0 +1 @@ +error: Bad IP address: not an IP diff --git a/testing/btest/Baseline/bifs.to_addr/output b/testing/btest/Baseline/bifs.to_addr/output index bcfdd3b6a2..ff277498f8 100644 --- a/testing/btest/Baseline/bifs.to_addr/output +++ b/testing/btest/Baseline/bifs.to_addr/output @@ -6,3 +6,4 @@ to_addr(10.20.30.40) = 10.20.30.40 (SUCCESS) to_addr(100.200.30.40) = 100.200.30.40 (SUCCESS) to_addr(10.0.0.0) = 10.0.0.0 (SUCCESS) to_addr(10.00.00.000) = 10.0.0.0 (SUCCESS) +to_addr(not an IP) = :: (SUCCESS) diff --git a/testing/btest/Baseline/bifs.to_subnet/error b/testing/btest/Baseline/bifs.to_subnet/error new file mode 100644 index 0000000000..ee0062cd83 --- /dev/null +++ b/testing/btest/Baseline/bifs.to_subnet/error @@ -0,0 +1 @@ +error: Bad string in SubNetVal ctor: 10.0.0.0 diff --git a/testing/btest/Baseline/bifs.to_subnet/output b/testing/btest/Baseline/bifs.to_subnet/output new file mode 100644 index 0000000000..0775063f89 --- /dev/null +++ b/testing/btest/Baseline/bifs.to_subnet/output @@ -0,0 +1,3 @@ +10.0.0.0/8, T +2607:f8b0::/32, T +::/0, T diff --git a/testing/btest/bifs/to_addr.bro b/testing/btest/bifs/to_addr.bro index 67ce79c795..3b79648b00 100644 --- a/testing/btest/bifs/to_addr.bro +++ b/testing/btest/bifs/to_addr.bro @@ -1,5 +1,6 @@ -# @TEST-EXEC: bro %INPUT >output +# @TEST-EXEC: bro -b %INPUT >output 2>error # @TEST-EXEC: btest-diff output +# @TEST-EXEC: btest-diff error function test_to_addr(ip: string, expect: addr) { @@ -16,3 +17,4 @@ test_to_addr("10.20.30.40", 10.20.30.40); test_to_addr("100.200.30.40", 100.200.30.40); test_to_addr("10.0.0.0", 10.0.0.0); test_to_addr("10.00.00.000", 10.0.0.0); +test_to_addr("not an IP", ::); diff --git a/testing/btest/bifs/to_subnet.bro b/testing/btest/bifs/to_subnet.bro new file mode 100644 index 0000000000..6b1eb54946 --- /dev/null +++ b/testing/btest/bifs/to_subnet.bro @@ -0,0 +1,11 @@ +# @TEST-EXEC: bro -b %INPUT >output 2>error +# @TEST-EXEC: btest-diff output +# @TEST-EXEC: btest-diff error + +global sn: subnet; +sn = to_subnet("10.0.0.0/8"); +print sn, sn == 10.0.0.0/8; +sn = to_subnet("2607:f8b0::/32"); +print sn, sn == 2607:f8b0::/32; +sn = to_subnet("10.0.0.0"); +print sn, sn == ::/0;