diff --git a/node/Address.hpp b/node/Address.hpp index 8baf9dbef..805c439da 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -32,7 +32,9 @@ #include #include #include + #include + #include "Utils.hpp" #include "MAC.hpp" #include "Constants.hpp" @@ -198,7 +200,7 @@ public: inline std::string toString() const { char buf[16]; - sprintf(buf,"%.10llx",(unsigned long long)_a); + Utils::snprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a); return std::string(buf); }; diff --git a/node/Demarc.cpp b/node/Demarc.cpp index ddbed6c84..f4060daca 100644 --- a/node/Demarc.cpp +++ b/node/Demarc.cpp @@ -68,15 +68,14 @@ Demarc::~Demarc() } std::string Demarc::describe(Demarc::Port p) - throw() { char buf[64]; switch ((DemarcPortType)(((uint64_t)p) >> 60)) { case PORT_TYPE_UDP_SOCKET_V4: - sprintf(buf,"udp/4/%d",(int)((uint64_t)p & 0xffff)); + Utils::snprintf(buf,sizeof(buf),"udp/4/%d",(int)((uint64_t)p & 0xffff)); return std::string(buf); case PORT_TYPE_UDP_SOCKET_V6: - sprintf(buf,"udp/6/%d",(int)((uint64_t)p & 0xffff)); + Utils::snprintf(buf,sizeof(buf),"udp/6/%d",(int)((uint64_t)p & 0xffff)); return std::string(buf); case PORT_TYPE_LOCAL_ETHERNET: return std::string("ethernet"); diff --git a/node/Demarc.hpp b/node/Demarc.hpp index 670e5b0a8..79db26d5d 100644 --- a/node/Demarc.hpp +++ b/node/Demarc.hpp @@ -83,8 +83,7 @@ public: * @param p Port * @return Human-readable description of port */ - static std::string describe(Port p) - throw(); + static std::string describe(Port p); /** * @param p Port to check diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index 94ee8adb7..226d03978 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -36,6 +36,7 @@ #include "RuntimeEnvironment.hpp" #include "Utils.hpp" #include "Mutex.hpp" +#include "Utils.hpp" // ff:ff:ff:ff:ff:ff with no ADI static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); @@ -99,22 +100,22 @@ private: inline void _findCmd(int id,const char *name) { char tmp[4096]; - sprintf(tmp,"/sbin/%s",name); + ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/sbin/%s",name); if (ZeroTier::Utils::fileExists(tmp)) { _paths[id] = tmp; return; } - sprintf(tmp,"/usr/sbin/%s",name); + ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/usr/sbin/%s",name); if (ZeroTier::Utils::fileExists(tmp)) { _paths[id] = tmp; return; } - sprintf(tmp,"/bin/%s",name); + ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/bin/%s",name); if (ZeroTier::Utils::fileExists(tmp)) { _paths[id] = tmp; return; } - sprintf(tmp,"/usr/bin/%s",name); + ZeroTier::Utils::snprintf(tmp,sizeof(tmp),"/usr/bin/%s",name); if (ZeroTier::Utils::fileExists(tmp)) { _paths[id] = tmp; return; @@ -178,8 +179,8 @@ EthernetTap::EthernetTap( int devno = 0; struct stat sbuf; do { - sprintf(ifr.ifr_name,"zt%d",devno++); - sprintf(procpath,"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); + Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++); + Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); } while (stat(procpath,&sbuf) == 0); } @@ -292,12 +293,12 @@ EthernetTap::EthernetTap( // Open the first available device (ones in use will fail with resource busy) for(int i=0;i<256;++i) { - sprintf(devpath,"/dev/zt%d",i); + Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i); if (stat(devpath,&tmp)) throw std::runtime_error("no more TAP devices available"); _fd = ::open(devpath,O_RDWR); if (_fd > 0) { - sprintf(_dev,"zt%d",i); + Utils::snprintf(_dev,sizeof(_dev),"zt%d",i); break; } } @@ -316,8 +317,8 @@ EthernetTap::EthernetTap( } // Configure MAC address and MTU, bring interface up - sprintf(ethaddr,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); - sprintf(mtustr,"%u",mtu); + Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]); + Utils::snprintf(mtustr,sizeof(mtustr),"%u",mtu); long cpid; if ((cpid = (long)vfork()) == 0) { execl(ifconfig,ifconfig,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0); @@ -895,7 +896,7 @@ EthernetTap::EthernetTap( // If we have a device, configure it if (_myDeviceInstanceId.length() > 0) { char tmps[4096]; - unsigned int tmpsl = sprintf_s(tmps,"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac.data[0],(unsigned int)mac.data[1],(unsigned int)mac.data[2],(unsigned int)mac.data[3],(unsigned int)mac.data[4],(unsigned int)mac.data[5]) + 1; + unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac.data[0],(unsigned int)mac.data[1],(unsigned int)mac.data[2],(unsigned int)mac.data[3],(unsigned int)mac.data[4],(unsigned int)mac.data[5]) + 1; RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl); RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl); DWORD tmp = mtu; @@ -961,7 +962,7 @@ EthernetTap::EthernetTap( // Open the tap, which is in this weird Windows analog of /dev char tapPath[4096]; - sprintf_s(tapPath,"\\\\.\\Global\\%s.tap",_myDeviceInstanceId.c_str()); + Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_myDeviceInstanceId.c_str()); _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); if (_tap == INVALID_HANDLE_VALUE) throw std::runtime_error("unable to open tap in \\\\.\\Global\\ namespace"); diff --git a/node/Filter.cpp b/node/Filter.cpp index 75a7036aa..6e28b7cd2 100644 --- a/node/Filter.cpp +++ b/node/Filter.cpp @@ -186,7 +186,7 @@ bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int l break; default: { char foo[128]; - sprintf(foo,"unrecognized IPv6 header type %d",(int)nextHeader); + Utils::snprintf(foo,sizeof(foo),"unrecognized IPv6 header type %d",(int)nextHeader); throw std::invalid_argument(foo); } } @@ -215,11 +215,11 @@ std::string Filter::Rule::toString() const s.push_back('*'); break; case 1: - sprintf(buf,"%u",_etherType.start); + Utils::snprintf(buf,sizeof(buf),"%u",_etherType.start); s.append(buf); break; default: - sprintf(buf,"%u-%u",_etherType.start,_etherType.end); + Utils::snprintf(buf,sizeof(buf),"%u-%u",_etherType.start,_etherType.end); s.append(buf); break; } @@ -229,11 +229,11 @@ std::string Filter::Rule::toString() const s.push_back('*'); break; case 1: - sprintf(buf,"%u",_protocol.start); + Utils::snprintf(buf,sizeof(buf),"%u",_protocol.start); s.append(buf); break; default: - sprintf(buf,"%u-%u",_protocol.start,_protocol.end); + Utils::snprintf(buf,sizeof(buf),"%u-%u",_protocol.start,_protocol.end); s.append(buf); break; } @@ -243,11 +243,11 @@ std::string Filter::Rule::toString() const s.push_back('*'); break; case 1: - sprintf(buf,"%u",_port.start); + Utils::snprintf(buf,sizeof(buf),"%u",_port.start); s.append(buf); break; default: - sprintf(buf,"%u-%u",_port.start,_port.end); + Utils::snprintf(buf,sizeof(buf),"%u-%u",_port.start,_port.end); s.append(buf); break; } @@ -269,7 +269,7 @@ Filter::Filter(const char *s) ++fn; } catch (std::invalid_argument &exc) { char tmp[256]; - sprintf(tmp,"invalid rule at index %u: %s",fn,exc.what()); + Utils::snprintf(tmp,sizeof(tmp),"invalid rule at index %u: %s",fn,exc.what()); throw std::invalid_argument(tmp); } } diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 24ec1f62b..b24768938 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -32,6 +32,7 @@ #include "Constants.hpp" #include "InetAddress.hpp" +#include "Utils.hpp" namespace ZeroTier { @@ -66,7 +67,7 @@ std::string InetAddress::toString() const #else if (inet_ntop(AF_INET,(const void *)&(_sa.sin.sin_addr.s_addr),buf,sizeof(buf))) { #endif - sprintf(buf2,"%s/%u",buf,(unsigned int)ntohs(_sa.sin.sin_port)); + Utils::snprintf(buf2,sizeof(buf2),"%s/%u",buf,(unsigned int)ntohs(_sa.sin.sin_port)); return std::string(buf2); } break; @@ -76,7 +77,7 @@ std::string InetAddress::toString() const #else if (inet_ntop(AF_INET6,(const void *)&(_sa.sin6.sin6_addr.s6_addr),buf,sizeof(buf))) { #endif - sprintf(buf2,"%s/%u",buf,(unsigned int)ntohs(_sa.sin6.sin6_port)); + Utils::snprintf(buf2,sizeof(buf2),"%s/%u",buf,(unsigned int)ntohs(_sa.sin6.sin6_port)); return std::string(buf2); } break; diff --git a/node/MAC.hpp b/node/MAC.hpp index f33277c0c..9765aa593 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -30,8 +30,9 @@ #include #include -#include "Array.hpp" + #include "Constants.hpp" +#include "Array.hpp" #include "Utils.hpp" namespace ZeroTier { @@ -150,7 +151,7 @@ public: inline std::string toString() const { char tmp[32]; - sprintf(tmp,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)data[0],(int)data[1],(int)data[2],(int)data[3],(int)data[4],(int)data[5]); + Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)data[0],(int)data[1],(int)data[2],(int)data[3],(int)data[4],(int)data[5]); return std::string(tmp); } }; diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index 3c654ea2d..9f2b111d7 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -106,7 +106,7 @@ public: inline std::string toString() const { char buf[64]; - sprintf(buf,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x/%.8lx",(unsigned int)_mac.data[0],(unsigned int)_mac.data[1],(unsigned int)_mac.data[2],(unsigned int)_mac.data[3],(unsigned int)_mac.data[4],(unsigned int)_mac.data[5],(unsigned long)_adi); + Utils::snprintf(buf,sizeof(buf),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x/%.8lx",(unsigned int)_mac.data[0],(unsigned int)_mac.data[1],(unsigned int)_mac.data[2],(unsigned int)_mac.data[3],(unsigned int)_mac.data[4],(unsigned int)_mac.data[5],(unsigned long)_adi); return std::string(buf); } diff --git a/node/Network.cpp b/node/Network.cpp index fc52e6d76..b66cc3b51 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -140,7 +140,7 @@ SharedPtr Network::newInstance(const RuntimeEnvironment *renv,uint64_t throw(std::runtime_error) { char tag[32]; - sprintf(tag,"%.16llx",(unsigned long long)id); + Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)id); // We construct Network via a static method to ensure that it is immediately // wrapped in a SharedPtr<>. Otherwise if there is traffic on the Ethernet diff --git a/node/Network.hpp b/node/Network.hpp index 0cb542bfa..4c18d93d7 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -107,7 +107,7 @@ public: inline void setNetworkId(uint64_t id) { char buf[32]; - sprintf(buf,"%.16llx",(unsigned long long)id); + Utils::snprintf(buf,sizeof(buf),"%.16llx",(unsigned long long)id); (*this)["nwid"] = buf; } @@ -141,9 +141,9 @@ public: inline void setTimestamp(uint64_t ts,uint64_t maxDelta) { char foo[32]; - sprintf(foo,"%llu",(unsigned long long)ts); + Utils::snprintf(foo,sizeof(foo),"%llu",(unsigned long long)ts); (*this)["ts"] = foo; - sprintf(foo,"%llu",(unsigned long long)maxDelta); + Utils::snprintf(foo,sizeof(foo),"%llu",(unsigned long long)maxDelta); (*this)["~ts"] = foo; } @@ -237,7 +237,7 @@ public: if (contains("name")) return get("name"); char buf[32]; - sprintf(buf,"%.16llx",(unsigned long long)networkId()); + Utils::snprintf(buf,sizeof(buf),"%.16llx",(unsigned long long)networkId()); return std::string(buf); } @@ -373,7 +373,7 @@ public: inline std::string toString() { char buf[64]; - sprintf(buf,"%.16llx",(unsigned long long)_id); + Utils::snprintf(buf,sizeof(buf),"%.16llx",(unsigned long long)_id); return std::string(buf); } diff --git a/node/Node.cpp b/node/Node.cpp index 96d1f87ad..1286342d0 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -590,7 +590,7 @@ public: char vs[32]; _VersionStringMaker() { - sprintf(vs,"%d.%d.%d",(int)ZEROTIER_ONE_VERSION_MAJOR,(int)ZEROTIER_ONE_VERSION_MINOR,(int)ZEROTIER_ONE_VERSION_REVISION); + Utils::snprintf(vs,sizeof(vs),"%d.%d.%d",(int)ZEROTIER_ONE_VERSION_MAJOR,(int)ZEROTIER_ONE_VERSION_MINOR,(int)ZEROTIER_ONE_VERSION_REVISION); } ~_VersionStringMaker() {} }; diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index fd8174101..18d8c4e23 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -635,9 +635,9 @@ bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const request["meta"] = std::string((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,dictLen),dictLen); request["type"] = "netconf-request"; request["peerId"] = peer->identity().toString(false); - sprintf(tmp,"%llx",(unsigned long long)nwid); + Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)nwid); request["nwid"] = tmp; - sprintf(tmp,"%llx",(unsigned long long)packetId()); + Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)packetId()); request["requestId"] = tmp; //TRACE("to netconf:\n%s",request.toString().c_str()); _r->netconfService->send(request); diff --git a/node/Peer.hpp b/node/Peer.hpp index b9ade9b1d..85f045ef6 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -356,7 +356,7 @@ public: { if ((_vMajor)||(_vMinor)||(_vRevision)) { char tmp[32]; - sprintf(tmp,"%u.%u.%u",_vMajor,_vMinor,_vRevision); + Utils::snprintf(tmp,sizeof(tmp),"%u.%u.%u",_vMajor,_vMinor,_vRevision); return std::string(tmp); } return std::string("?"); diff --git a/node/Utils.cpp b/node/Utils.cpp index 94f66c118..0b5afcbda 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -464,7 +464,7 @@ std::string Utils::toRfc1123(uint64_t t64) #else gmtime_r(&utc,&t); #endif - sprintf(buf,"%3s, %02d %3s %4d %02d:%02d:%02d GMT",DAY_NAMES[t.tm_wday],t.tm_mday,MONTH_NAMES[t.tm_mon],t.tm_year + 1900,t.tm_hour,t.tm_min,t.tm_sec); + Utils::snprintf(buf,sizeof(buf),"%3s, %02d %3s %4d %02d:%02d:%02d GMT",DAY_NAMES[t.tm_wday],t.tm_mday,MONTH_NAMES[t.tm_mon],t.tm_year + 1900,t.tm_hour,t.tm_min,t.tm_sec); return std::string(buf); } @@ -653,4 +653,22 @@ void Utils::stdsprintf(std::string &s,const char *fmt,...) s.append(buf); } +unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...) + throw(std::length_error) +{ + va_list ap; + + va_start(ap,fmt); + int n = (int)vsnprintf(buf,len,fmt,ap); + va_end(ap); + + if ((n >= (int)len)||(n < 0)) { + if (len) + buf[len - 1] = (char)0; + throw std::length_error("buf[] overflow in Utils::snprintf"); + } + + return (unsigned int)n; +} + } // namespace ZeroTier diff --git a/node/Utils.hpp b/node/Utils.hpp index 8bced8ad4..0d40e370b 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -536,6 +536,18 @@ public: static void stdsprintf(std::string &s,const char *fmt,...) throw(std::bad_alloc,std::length_error); + /** + * Variant of snprintf that is portable and throws an exception + * + * @param buf Buffer to write to + * @param len Length of buffer in bytes + * @param fmt Format string + * @param ... Format arguments + * @throws std::length_error buf[] too short (buf[] will still be left null-terminated) + */ + static unsigned int snprintf(char *buf,unsigned int len,const char *fmt,...) + throw(std::length_error); + /** * Count the number of bits set in an integer * diff --git a/selftest.cpp b/selftest.cpp index 641898620..c9ca9c71b 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -376,32 +376,6 @@ static int testOther() return 0; } -static int testRateLimiter() -{ - RateLimiter limiter; - RateLimiter::Limit limit; - - std::cout << "[ratelimiter] preload: 10000.0, rate: 1000.0/sec, max: 15000.0, min: -7500.0" << std::endl; - limit.bytesPerSecond = 1000.0; - limit.maxBalance = 15000.0; - limit.minBalance = -7500.0; - limiter.init(10000.0); - for(int i=0;i<25;++i) { - Thread::sleep(100); - std::cout << "[ratelimiter] delayed 0.1s, gate(1000.0): " << (limiter.gate(limit,1000.0) ? "OK" : "BLOCK"); - std::cout << " (new balance afterwords: " << limiter.balance() << ")" << std::endl; - } - std::cout << "[ratelimiter] delaying 15s..." << std::endl; - Thread::sleep(15000); - for(int i=0;i<20;++i) { - Thread::sleep(1000); - std::cout << "[ratelimiter] delayed 1s, gate(2000.0): " << (limiter.gate(limit,2000.0) ? "OK" : "BLOCK"); - std::cout << " (new balance afterwords: " << limiter.balance() << ")" << std::endl; - } - - return 0; -} - #ifdef __WINDOWS__ int _tmain(int argc, _TCHAR* argv[]) #else @@ -417,7 +391,6 @@ int main(int argc,char **argv) r |= testPacket(); r |= testOther(); r |= testIdentity(); - r |= testRateLimiter(); if (r) std::cout << std::endl << "SOMETHING FAILED!" << std::endl;