Browse Source

Merge pull request #4102

21bf3d2 Add tests for BoostAsioToCNetAddr (Wladimir J. van der Laan)
fdbd707 Remove unused function WildcardMatch (Wladimir J. van der Laan)
ee21912 rpc: Use netmasks instead of wildcards for IP address matching (Wladimir J. van der Laan)
e16be73 net: Add CSubNet class for subnet matching (Wladimir J. van der Laan)
d864275 Use new function parseint32 in SplitHostPort (Wladimir J. van der Laan)
0d4ea1c util: add parseint32 function with strict error reporting (Wladimir J. van der Laan)
0.10
Wladimir J. van der Laan 11 years ago
parent
commit
605d5b5558
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 131
      src/netbase.cpp
  2. 30
      src/netbase.h
  3. 62
      src/rpcserver.cpp
  4. 4
      src/rpcserver.h
  5. 37
      src/test/netbase_tests.cpp
  6. 16
      src/test/rpc_tests.cpp
  7. 33
      src/test/util_tests.cpp
  8. 51
      src/util.cpp
  9. 16
      src/util.h

131
src/netbase.cpp

@ -47,12 +47,10 @@ void SplitHostPort(std::string in, int &portOut, std::string &hostOut) {
bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe bool fBracketed = fHaveColon && (in[0]=='[' && in[colon-1]==']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos); bool fMultiColon = fHaveColon && (in.find_last_of(':',colon-1) != in.npos);
if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) { if (fHaveColon && (colon==0 || fBracketed || !fMultiColon)) {
char *endp = NULL; int32_t n;
int n = strtol(in.c_str() + colon + 1, &endp, 10); if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) {
if (endp && *endp == 0 && n >= 0) {
in = in.substr(0, colon); in = in.substr(0, colon);
if (n > 0 && n < 0x10000) portOut = n;
portOut = n;
} }
} }
if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']') if (in.size()>0 && in[0] == '[' && in[in.size()-1] == ']')
@ -548,6 +546,22 @@ void CNetAddr::SetIP(const CNetAddr& ipIn)
memcpy(ip, ipIn.ip, sizeof(ip)); memcpy(ip, ipIn.ip, sizeof(ip));
} }
void CNetAddr::SetRaw(Network network, const uint8_t *ip_in)
{
switch(network)
{
case NET_IPV4:
memcpy(ip, pchIPv4, 12);
memcpy(ip+12, ip_in, 4);
break;
case NET_IPV6:
memcpy(ip, ip_in, 16);
break;
default:
assert(!"invalid network");
}
}
static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43};
bool CNetAddr::SetSpecial(const std::string &strName) bool CNetAddr::SetSpecial(const std::string &strName)
@ -571,13 +585,12 @@ CNetAddr::CNetAddr()
CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
{ {
memcpy(ip, pchIPv4, 12); SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr);
memcpy(ip+12, &ipv4Addr, 4);
} }
CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr) CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr)
{ {
memcpy(ip, &ipv6Addr, 16); SetRaw(NET_IPV6, (const uint8_t*)&ipv6Addr);
} }
CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup) CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup)
@ -1122,3 +1135,105 @@ void CService::SetPort(unsigned short portIn)
{ {
port = portIn; port = portIn;
} }
CSubNet::CSubNet():
valid(false)
{
memset(netmask, 0, sizeof(netmask));
}
CSubNet::CSubNet(const std::string &strSubnet, bool fAllowLookup)
{
size_t slash = strSubnet.find_last_of('/');
std::vector<CNetAddr> vIP;
valid = true;
// Default to /32 (IPv4) or /128 (IPv6), i.e. match single address
memset(netmask, 255, sizeof(netmask));
std::string strAddress = strSubnet.substr(0, slash);
if (LookupHost(strAddress.c_str(), vIP, 1, fAllowLookup))
{
network = vIP[0];
if (slash != strSubnet.npos)
{
std::string strNetmask = strSubnet.substr(slash + 1);
int32_t n;
// IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
int noffset = network.IsIPv4() ? (12 * 8) : 0;
if (ParseInt32(strNetmask, &n)) // If valid number, assume /24 symtex
{
if(n >= 0 && n <= (128 - noffset)) // Only valid if in range of bits of address
{
n += noffset;
// Clear bits [n..127]
for (; n < 128; ++n)
netmask[n>>3] &= ~(1<<(n&7));
}
else
{
valid = false;
}
}
else // If not a valid number, try full netmask syntax
{
if (LookupHost(strNetmask.c_str(), vIP, 1, false)) // Never allow lookup for netmask
{
// Remember: GetByte returns bytes in reversed order
// Copy only the *last* four bytes in case of IPv4, the rest of the mask should stay 1's as
// we don't want pchIPv4 to be part of the mask.
int asize = network.IsIPv4() ? 4 : 16;
for(int x=0; x<asize; ++x)
netmask[15-x] = vIP[0].GetByte(x);
}
else
{
valid = false;
}
}
}
}
else
{
valid = false;
}
}
bool CSubNet::Match(const CNetAddr &addr) const
{
if (!valid || !addr.IsValid())
return false;
for(int x=0; x<16; ++x)
if ((addr.GetByte(x) & netmask[15-x]) != network.GetByte(x))
return false;
return true;
}
std::string CSubNet::ToString() const
{
std::string strNetmask;
if (network.IsIPv4())
strNetmask = strprintf("%u.%u.%u.%u", netmask[12], netmask[13], netmask[14], netmask[15]);
else
strNetmask = strprintf("%x:%x:%x:%x:%x:%x:%x:%x",
netmask[0] << 8 | netmask[1], netmask[2] << 8 | netmask[3],
netmask[4] << 8 | netmask[5], netmask[6] << 8 | netmask[7],
netmask[8] << 8 | netmask[9], netmask[10] << 8 | netmask[11],
netmask[12] << 8 | netmask[13], netmask[14] << 8 | netmask[15]);
return network.ToString() + "/" + strNetmask;
}
bool CSubNet::IsValid() const
{
return valid;
}
bool operator==(const CSubNet& a, const CSubNet& b)
{
return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16);
}
bool operator!=(const CSubNet& a, const CSubNet& b)
{
return !(a==b);
}

30
src/netbase.h

@ -49,6 +49,13 @@ class CNetAddr
explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false); explicit CNetAddr(const std::string &strIp, bool fAllowLookup = false);
void Init(); void Init();
void SetIP(const CNetAddr& ip); void SetIP(const CNetAddr& ip);
/**
* Set raw IPv4 or IPv6 address (in network byte order)
* @note Only NET_IPV4 and NET_IPV6 are allowed for network.
*/
void SetRaw(Network network, const uint8_t *data);
bool SetSpecial(const std::string &strName); // for Tor addresses bool SetSpecial(const std::string &strName); // for Tor addresses
bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
@ -90,6 +97,29 @@ class CNetAddr
) )
}; };
class CSubNet
{
protected:
/// Network (base) address
CNetAddr network;
/// Netmask, in network byte order
uint8_t netmask[16];
/// Is this value valid? (only used to signal parse errors)
bool valid;
public:
CSubNet();
explicit CSubNet(const std::string &strSubnet, bool fAllowLookup = false);
bool Match(const CNetAddr &addr) const;
std::string ToString() const;
bool IsValid() const;
friend bool operator==(const CSubNet& a, const CSubNet& b);
friend bool operator!=(const CSubNet& a, const CSubNet& b);
};
/** A combination of a network address (CNetAddr) and a (TCP) port */ /** A combination of a network address (CNetAddr) and a (TCP) port */
class CService : public CNetAddr class CService : public CNetAddr
{ {

62
src/rpcserver.cpp

@ -38,6 +38,7 @@ static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
static ssl::context* rpc_ssl_context = NULL; static ssl::context* rpc_ssl_context = NULL;
static boost::thread_group* rpc_worker_group = NULL; static boost::thread_group* rpc_worker_group = NULL;
static boost::asio::io_service::work *rpc_dummy_work = NULL; static boost::asio::io_service::work *rpc_dummy_work = NULL;
static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from
void RPCTypeCheck(const Array& params, void RPCTypeCheck(const Array& params,
const list<Value_type>& typesExpected, const list<Value_type>& typesExpected,
@ -358,25 +359,33 @@ void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
stream << HTTPReply(nStatus, strReply, false) << std::flush; stream << HTTPReply(nStatus, strReply, false) << std::flush;
} }
bool ClientAllowed(const boost::asio::ip::address& address) CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address)
{ {
CNetAddr netaddr;
// Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses
if (address.is_v6() if (address.is_v6()
&& (address.to_v6().is_v4_compatible() && (address.to_v6().is_v4_compatible()
|| address.to_v6().is_v4_mapped())) || address.to_v6().is_v4_mapped()))
return ClientAllowed(address.to_v6().to_v4()); address = address.to_v6().to_v4();
if (address == asio::ip::address_v4::loopback() if(address.is_v4())
|| address == asio::ip::address_v6::loopback() {
|| (address.is_v4() boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes();
// Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet) netaddr.SetRaw(NET_IPV4, &bytes[0]);
&& (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000)) }
return true; else
{
const string strAddress = address.to_string(); boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes();
const vector<string>& vAllow = mapMultiArgs["-rpcallowip"]; netaddr.SetRaw(NET_IPV6, &bytes[0]);
BOOST_FOREACH(string strAllow, vAllow) }
if (WildcardMatch(strAddress, strAllow)) return netaddr;
}
bool ClientAllowed(const boost::asio::ip::address& address)
{
CNetAddr netaddr = BoostAsioToCNetAddr(address);
BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
if (subnet.Match(netaddr))
return true; return true;
return false; return false;
} }
@ -502,6 +511,31 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
void StartRPCThreads() void StartRPCThreads()
{ {
rpc_allow_subnets.clear();
rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet
rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost
if (mapMultiArgs.count("-rpcallowip"))
{
const vector<string>& vAllow = mapMultiArgs["-rpcallowip"];
BOOST_FOREACH(string strAllow, vAllow)
{
CSubNet subnet(strAllow);
if(!subnet.IsValid())
{
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -rpcallowip subnet specification: %s", strAllow),
"", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
}
rpc_allow_subnets.push_back(subnet);
}
}
std::string strAllowed;
BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
strAllowed += subnet.ToString() + " ";
LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
if (((mapArgs["-rpcpassword"] == "") || if (((mapArgs["-rpcpassword"] == "") ||
(mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword()) (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword())

4
src/rpcserver.h

@ -19,6 +19,7 @@
#include "json/json_spirit_writer_template.h" #include "json/json_spirit_writer_template.h"
class CBlockIndex; class CBlockIndex;
class CNetAddr;
/* Start RPC threads */ /* Start RPC threads */
void StartRPCThreads(); void StartRPCThreads();
@ -50,6 +51,9 @@ void RPCTypeCheck(const json_spirit::Object& o,
*/ */
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds); void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds);
//! Convert boost::asio address to CNetAddr
extern CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address);
typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp);
class CRPCCommand class CRPCCommand

37
src/test/netbase_tests.cpp

@ -102,4 +102,41 @@ BOOST_AUTO_TEST_CASE(onioncat_test)
BOOST_CHECK(addr1.IsRoutable()); BOOST_CHECK(addr1.IsRoutable());
} }
BOOST_AUTO_TEST_CASE(subnet_test)
{
BOOST_CHECK(CSubNet("1.2.3.0/24") == CSubNet("1.2.3.0/255.255.255.0"));
BOOST_CHECK(CSubNet("1.2.3.0/24") != CSubNet("1.2.4.0/255.255.255.0"));
BOOST_CHECK(CSubNet("1.2.3.0/24").Match(CNetAddr("1.2.3.4")));
BOOST_CHECK(!CSubNet("1.2.2.0/24").Match(CNetAddr("1.2.3.4")));
BOOST_CHECK(CSubNet("1.2.3.4").Match(CNetAddr("1.2.3.4")));
BOOST_CHECK(CSubNet("1.2.3.4/32").Match(CNetAddr("1.2.3.4")));
BOOST_CHECK(!CSubNet("1.2.3.4").Match(CNetAddr("5.6.7.8")));
BOOST_CHECK(!CSubNet("1.2.3.4/32").Match(CNetAddr("5.6.7.8")));
BOOST_CHECK(CSubNet("::ffff:127.0.0.1").Match(CNetAddr("127.0.0.1")));
BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8").Match(CNetAddr("1:2:3:4:5:6:7:8")));
BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8").Match(CNetAddr("1:2:3:4:5:6:7:9")));
BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:0/112").Match(CNetAddr("1:2:3:4:5:6:7:1234")));
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6
BOOST_CHECK(CSubNet("::/0").Match(CNetAddr("1:2:3:4:5:6:7:1234")));
BOOST_CHECK(CSubNet("::/0").Match(CNetAddr("1.2.3.4")));
// All-Matching IPv4 does not Match IPv6
BOOST_CHECK(!CSubNet("0.0.0.0/0").Match(CNetAddr("1:2:3:4:5:6:7:1234")));
// Invalid subnets Match nothing (not even invalid addresses)
BOOST_CHECK(!CSubNet().Match(CNetAddr("1.2.3.4")));
BOOST_CHECK(!CSubNet("").Match(CNetAddr("4.5.6.7")));
BOOST_CHECK(!CSubNet("bloop").Match(CNetAddr("0.0.0.0")));
BOOST_CHECK(!CSubNet("bloop").Match(CNetAddr("hab")));
// Check valid/invalid
BOOST_CHECK(CSubNet("1.2.3.0/0").IsValid());
BOOST_CHECK(!CSubNet("1.2.3.0/-1").IsValid());
BOOST_CHECK(CSubNet("1.2.3.0/32").IsValid());
BOOST_CHECK(!CSubNet("1.2.3.0/33").IsValid());
BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/0").IsValid());
BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/33").IsValid());
BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8/-1").IsValid());
BOOST_CHECK(CSubNet("1:2:3:4:5:6:7:8/128").IsValid());
BOOST_CHECK(!CSubNet("1:2:3:4:5:6:7:8/129").IsValid());
BOOST_CHECK(!CSubNet("fuzzy").IsValid());
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

16
src/test/rpc_tests.cpp

@ -6,6 +6,7 @@
#include "rpcclient.h" #include "rpcclient.h"
#include "base58.h" #include "base58.h"
#include "netbase.h"
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@ -138,4 +139,19 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values)
BOOST_CHECK(AmountFromValue(ValueFromString("20999999.99999999")) == 2099999999999999LL); BOOST_CHECK(AmountFromValue(ValueFromString("20999999.99999999")) == 2099999999999999LL);
} }
BOOST_AUTO_TEST_CASE(rpc_boostasiotocnetaddr)
{
// Check IPv4 addresses
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("1.2.3.4")).ToString(), "1.2.3.4");
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("127.0.0.1")).ToString(), "127.0.0.1");
// Check IPv6 addresses
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("::1")).ToString(), "::1");
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("123:4567:89ab:cdef:123:4567:89ab:cdef")).ToString(),
"123:4567:89ab:cdef:123:4567:89ab:cdef");
// v4 compatible must be interpreted as IPv4
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("::0:127.0.0.1")).ToString(), "127.0.0.1");
// v4 mapped must be interpreted as IPv4
BOOST_CHECK_EQUAL(BoostAsioToCNetAddr(boost::asio::ip::address::from_string("::ffff:127.0.0.1")).ToString(), "127.0.0.1");
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

33
src/test/util_tests.cpp

@ -165,17 +165,6 @@ BOOST_AUTO_TEST_CASE(util_GetArg)
BOOST_CHECK_EQUAL(GetBoolArg("booltest4", false), true); BOOST_CHECK_EQUAL(GetBoolArg("booltest4", false), true);
} }
BOOST_AUTO_TEST_CASE(util_WildcardMatch)
{
BOOST_CHECK(WildcardMatch("127.0.0.1", "*"));
BOOST_CHECK(WildcardMatch("127.0.0.1", "127.*"));
BOOST_CHECK(WildcardMatch("abcdef", "a?cde?"));
BOOST_CHECK(!WildcardMatch("abcdef", "a?cde??"));
BOOST_CHECK(WildcardMatch("abcdef", "a*f"));
BOOST_CHECK(!WildcardMatch("abcdef", "a*x"));
BOOST_CHECK(WildcardMatch("", "*"));
}
BOOST_AUTO_TEST_CASE(util_FormatMoney) BOOST_AUTO_TEST_CASE(util_FormatMoney)
{ {
BOOST_CHECK_EQUAL(FormatMoney(0, false), "0.00"); BOOST_CHECK_EQUAL(FormatMoney(0, false), "0.00");
@ -342,4 +331,26 @@ BOOST_AUTO_TEST_CASE(gettime)
BOOST_CHECK((GetTime() & ~0xFFFFFFFFLL) == 0); BOOST_CHECK((GetTime() & ~0xFFFFFFFFLL) == 0);
} }
BOOST_AUTO_TEST_CASE(test_ParseInt32)
{
int32_t n;
// Valid values
BOOST_CHECK(ParseInt32("1234", NULL));
BOOST_CHECK(ParseInt32("0", &n) && n == 0);
BOOST_CHECK(ParseInt32("1234", &n) && n == 1234);
BOOST_CHECK(ParseInt32("01234", &n) && n == 1234); // no octal
BOOST_CHECK(ParseInt32("2147483647", &n) && n == 2147483647);
BOOST_CHECK(ParseInt32("-2147483648", &n) && n == -2147483648);
BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234);
// Invalid values
BOOST_CHECK(!ParseInt32("1a", &n));
BOOST_CHECK(!ParseInt32("aap", &n));
BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex
// Overflow and underflow
BOOST_CHECK(!ParseInt32("-2147483649", NULL));
BOOST_CHECK(!ParseInt32("2147483648", NULL));
BOOST_CHECK(!ParseInt32("-32482348723847471234", NULL));
BOOST_CHECK(!ParseInt32("32482348723847471234", NULL));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

51
src/util.cpp

@ -889,43 +889,6 @@ string DecodeBase32(const string& str)
return string((const char*)&vchRet[0], vchRet.size()); return string((const char*)&vchRet[0], vchRet.size());
} }
bool WildcardMatch(const char* psz, const char* mask)
{
while (true)
{
switch (*mask)
{
case '\0':
return (*psz == '\0');
case '*':
return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask));
case '?':
if (*psz == '\0')
return false;
break;
default:
if (*psz != *mask)
return false;
break;
}
psz++;
mask++;
}
}
bool WildcardMatch(const string& str, const string& mask)
{
return WildcardMatch(str.c_str(), mask.c_str());
}
static std::string FormatException(std::exception* pex, const char* pszThread) static std::string FormatException(std::exception* pex, const char* pszThread)
{ {
#ifdef WIN32 #ifdef WIN32
@ -1427,3 +1390,17 @@ void RenameThread(const char* name)
#endif #endif
} }
bool ParseInt32(const std::string& str, int32_t *out)
{
char *endp = NULL;
errno = 0; // strtol will not set errno if valid
long int n = strtol(str.c_str(), &endp, 10);
if(out) *out = (int)n;
// Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow
// we still have to check that the returned value is within the range of an *int32_t*. On 64-bit
// platforms the size of these types may be different.
return endp && *endp == 0 && !errno &&
n >= std::numeric_limits<int32_t>::min() &&
n <= std::numeric_limits<int32_t>::max();
}

16
src/util.h

@ -182,8 +182,6 @@ std::string DecodeBase32(const std::string& str);
std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const unsigned char* pch, size_t len);
std::string EncodeBase32(const std::string& str); std::string EncodeBase32(const std::string& str);
void ParseParameters(int argc, const char*const argv[]); void ParseParameters(int argc, const char*const argv[]);
bool WildcardMatch(const char* psz, const char* mask);
bool WildcardMatch(const std::string& str, const std::string& mask);
void FileCommit(FILE *fileout); void FileCommit(FILE *fileout);
bool TruncateFile(FILE *file, unsigned int length); bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD); int RaiseFileDescriptorLimit(int nMinFD);
@ -256,6 +254,13 @@ inline int atoi(const std::string& str)
return atoi(str.c_str()); return atoi(str.c_str());
} }
/**
* Convert string to signed 32-bit integer with strict parse error feedback.
* @returns true if the entire string could be parsed as valid integer,
* false if not the entire string could be parsed or when overflow or underflow occured.
*/
bool ParseInt32(const std::string& str, int32_t *out);
inline int roundint(double d) inline int roundint(double d)
{ {
return (int)(d > 0 ? d + 0.5 : d - 0.5); return (int)(d > 0 ? d + 0.5 : d - 0.5);
@ -341,13 +346,6 @@ inline std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime)
return pszTime; return pszTime;
} }
template<typename T>
void skipspaces(T& it)
{
while (isspace(*it))
++it;
}
inline bool IsSwitchChar(char c) inline bool IsSwitchChar(char c)
{ {
#ifdef WIN32 #ifdef WIN32

Loading…
Cancel
Save