Compare commits

..

No commits in common. "master" and "longttl" have entirely different histories.

18 changed files with 475 additions and 471 deletions

View file

@ -4,5 +4,10 @@ LDFLAGS = $(CXXFLAGS)
dnsseed: dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o
g++ -pthread $(LDFLAGS) -o dnsseed dns.o bitcoin.o netbase.o protocol.o db.o main.o util.o -lcrypto
%.o: %.cpp *.h
g++ -std=c++11 -pthread $(CXXFLAGS) -Wall -Wno-unused -Wno-sign-compare -Wno-reorder -Wno-comment -c -o $@ $<
%.o: %.cpp bitcoin.h netbase.h protocol.h db.h serialize.h uint256.h util.h
g++ -DUSE_IPV6 -pthread $(CXXFLAGS) -Wno-invalid-offsetof -c -o $@ $<
dns.o: dns.c
gcc -pthread -std=c99 $(CXXFLAGS) dns.c -c -o dns.o
%.o: %.cpp

62
README Normal file
View file

@ -0,0 +1,62 @@
bitcoin-seeder
==============
Bitcoin-seeder is a crawler for the Bitcoin network, which exposes a list
of reliable nodes via a built-in DNS server.
Features:
* regularly revisits known nodes to check their availability
* bans nodes after enough failures, or bad behaviour
* accepts nodes down to v0.3.19 to request new IP addresses from,
but only reports good post-v0.3.24 nodes.
* keeps statistics over (exponential) windows of 2 hours, 8 hours,
1 day and 1 week, to base decisions on.
* very low memory (a few tens of megabytes) and cpu requirements.
* crawlers run in parallel (by default 24 threads simultaneously).
REQUIREMENTS
------------
$ sudo apt-get install build-essential libboost-all-dev libssl-dev
USAGE
-----
Assuming you want to run a dns seed on dnsseed.example.com, you will
need an authorative NS record in example.com's domain record, pointing
to for example vps.example.com:
$ dig -t NS dnsseed.example.com
;; ANSWER SECTION
dnsseed.example.com. 86400 IN NS vps.example.com.
On the system vps.example.com, you can now run dnsseed:
./dnsseed -h dnsseed.example.com -n vps.example.com
If you want the DNS server to report SOA records, please provide an
e-mailadres (with the @ part replaced by .) using -m.
COMPILING
---------
Compiling will require boost and ssl. On debian systems, these are provided
by `libboost-dev` and `libssl-dev` respectively.
$ make
This will produce the `dnsseed` binary.
RUNNING AS NON-ROOT
-------------------
Typically, you'll need root privileges to listen to port 53 (name service).
One solution is using an iptables rule (Linux only) to redirect it to
a non-privileged port:
$ iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353
If properly configured, this will allow you to run dnsseed in userspace, using
the -p 5353 option.

View file

@ -26,9 +26,9 @@ class CNode {
int GetTimeout() {
if (you.IsTor())
return 120;
return 60;
else
return 30;
return 10;
}
void BeginMessage(const char *pszCommand) {
@ -36,16 +36,16 @@ class CNode {
nHeaderStart = vSend.size();
vSend << CMessageHeader(pszCommand, 0);
nMessageStart = vSend.size();
// printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand);
// printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand);
}
void AbortMessage() {
if (nHeaderStart == -1) return;
vSend.resize(nHeaderStart);
nHeaderStart = -1;
nMessageStart = -1;
}
void EndMessage() {
if (nHeaderStart == -1) return;
unsigned int nSize = vSend.size() - nMessageStart;
@ -60,7 +60,7 @@ class CNode {
nHeaderStart = -1;
nMessageStart = -1;
}
void Send() {
if (sock == INVALID_SOCKET) return;
if (vSend.empty()) return;
@ -72,7 +72,7 @@ class CNode {
sock = INVALID_SOCKET;
}
}
void PushVersion() {
int64 nTime = time(NULL);
uint64 nLocalNonce = BITCOIN_SEED_NONCE;
@ -80,12 +80,11 @@ class CNode {
CAddress me(CService("0.0.0.0"));
BeginMessage("version");
int nBestHeight = GetRequireHeight();
string ver = "/lbry-seeder:0.01/";
uint8_t fRelayTxs = 0;
vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight << fRelayTxs;
string ver = "/bitcoin-seeder:0.01/";
vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight;
EndMessage();
}
void GotVersion() {
// printf("\n%s: version %i\n", ToString(you).c_str(), nVersion);
if (vAddr) {
@ -112,7 +111,7 @@ class CNode {
vRecv >> strSubVer;
if (nVersion >= 209 && !vRecv.empty())
vRecv >> nStartingHeight;
if (nVersion >= 209) {
BeginMessage("verack");
EndMessage();
@ -124,22 +123,20 @@ class CNode {
}
return false;
}
if (strCommand == "verack") {
this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
GotVersion();
return false;
}
if (strCommand == "addr" && vAddr) {
vector<CAddress> vAddrNew;
vRecv >> vAddrNew;
// printf("%s: got %i addresses\n", ToString(you).c_str(), (int)vAddrNew.size());
int64 now = time(NULL);
vector<CAddress>::iterator it = vAddrNew.begin();
if (vAddrNew.size() > 1) {
if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1;
}
if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1;
while (it != vAddrNew.end()) {
CAddress &addr = *it;
// printf("%s: got address %s\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
@ -153,10 +150,10 @@ class CNode {
}
return false;
}
return false;
}
bool ProcessMessages() {
if (vRecv.empty()) return false;
do {
@ -172,16 +169,16 @@ class CNode {
vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize);
CMessageHeader hdr;
vRecv >> hdr;
if (!hdr.IsValid()) {
if (!hdr.IsValid()) {
// printf("%s: BAD (invalid header)\n", ToString(you).c_str());
ban = 100000; return true;
}
string strCommand = hdr.GetCommand();
unsigned int nMessageSize = hdr.nMessageSize;
if (nMessageSize > MAX_SIZE) {
if (nMessageSize > MAX_SIZE) {
// printf("%s: BAD (message too large)\n", ToString(you).c_str());
ban = 100000;
return true;
return true;
}
if (nMessageSize > vRecv.size()) {
vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
@ -201,7 +198,7 @@ class CNode {
} while(1);
return false;
}
public:
CNode(const CService& ip, vector<CAddress>* vAddrIn) : you(ip), nHeaderStart(-1), nMessageStart(-1), vAddr(vAddrIn), ban(0), doneAfter(0), nVersion(0) {
vSend.SetType(SER_NETWORK);
@ -221,11 +218,9 @@ public:
int64 now;
while (now = time(NULL), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) {
char pchBuf[0x10000];
fd_set read_set, except_set;
FD_ZERO(&read_set);
FD_ZERO(&except_set);
FD_SET(sock,&read_set);
FD_SET(sock,&except_set);
fd_set set;
FD_ZERO(&set);
FD_SET(sock,&set);
struct timeval wa;
if (doneAfter) {
wa.tv_sec = doneAfter - now;
@ -234,7 +229,7 @@ public:
wa.tv_sec = GetTimeout();
wa.tv_usec = 0;
}
int ret = select(sock+1, &read_set, NULL, &except_set, &wa);
int ret = select(sock+1, &set, NULL, &set, &wa);
if (ret != 1) {
if (!doneAfter) res = false;
break;
@ -261,29 +256,25 @@ public:
sock = INVALID_SOCKET;
return (ban == 0) && res;
}
int GetBan() {
return ban;
}
int GetClientVersion() {
return nVersion;
}
std::string GetClientSubVersion() {
return strSubVer;
}
int GetStartingHeight() {
return nStartingHeight;
}
uint64_t GetServices() {
return you.nServices;
}
};
bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, vector<CAddress>* vAddr, uint64_t& services) {
bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, vector<CAddress>* vAddr) {
try {
CNode node(cip, vAddr);
bool ret = node.Run();
@ -295,7 +286,6 @@ bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV
clientV = node.GetClientVersion();
clientSV = node.GetClientSubVersion();
blocks = node.GetStartingHeight();
services = node.GetServices();
// printf("%s: %s!!!\n", cip.ToString().c_str(), ret ? "GOOD" : "BAD");
return ret;
} catch(std::ios_base::failure& e) {
@ -314,3 +304,4 @@ int main(void) {
printf("ret=%s ban=%i vAddr.size()=%i\n", ret ? "good" : "bad", ban, (int)vAddr.size());
}
*/

View file

@ -3,6 +3,6 @@
#include "protocol.h"
bool TestNode(const CService &cip, int &ban, int &client, std::string &clientSV, int &blocks, std::vector<CAddress>* vAddr, uint64_t& services);
bool TestNode(const CService &cip, int &ban, int &client, std::string &clientSV, int &blocks, std::vector<CAddress>* vAddr);
#endif

View file

@ -58,7 +58,7 @@ for my $file (@ARGV) {
}
for my $addr (sort { $res->{$b} <=> $res->{$a} } (keys %{$res})) {
if ($addr =~ /\A(\d+)\.(\d+)\.(\d+)\.(\d+):9246/) {
if ($addr =~ /\A(\d+)\.(\d+)\.(\d+)\.(\d+):8333/) {
my $a = $1*0x1000000 + $2*0x10000 + $3*0x100 + $4;
printf "0x%08x %s %g%%\n",$a,$addr,(1-((1-$res->{$addr}) ** (1/$n)))*100;
}

36
db.cpp
View file

@ -69,7 +69,7 @@ int CAddrDb::Lookup_(const CService &ip) {
return -1;
}
void CAddrDb::Good_(const CService &addr, int clientV, std::string clientSV, int blocks, uint64_t services) {
void CAddrDb::Good_(const CService &addr, int clientV, std::string clientSV, int blocks) {
int id = Lookup_(addr);
if (id == -1) return;
unkId.erase(id);
@ -78,7 +78,6 @@ void CAddrDb::Good_(const CService &addr, int clientV, std::string clientSV, int
info.clientVersion = clientV;
info.clientSubVersion = clientSV;
info.blocks = blocks;
info.services = services;
info.Update(true);
if (info.IsGood() && goodId.count(id)==0) {
goodId.insert(id);
@ -141,8 +140,12 @@ void CAddrDb::Add_(const CAddress &addr, bool force) {
}
if (ipToId.count(ipp)) {
CAddrInfo &ai = idToInfo[ipToId[ipp]];
if (addr.nTime > ai.lastTry) ai.lastTry = addr.nTime;
// Do not update ai.nServices (data from VERSION from the peer itself is better than random ADDR rumours).
if (addr.nTime > ai.lastTry || ai.services != addr.nServices)
{
ai.lastTry = addr.nTime;
ai.services |= addr.nServices;
// printf("%s: updated\n", ToString(addr).c_str());
}
if (force) {
ai.ignoreTill = 0;
}
@ -163,7 +166,7 @@ void CAddrDb::Add_(const CAddress &addr, bool force) {
nDirty++;
}
void CAddrDb::GetIPs_(set<CNetAddr>& ips, uint64_t requestedFlags, int max, const bool* nets) {
void CAddrDb::GetIPs_(set<CNetAddr>& ips, int max, const bool* nets) {
if (goodId.size() == 0) {
int id = -1;
if (ourId.size() == 0) {
@ -172,28 +175,23 @@ void CAddrDb::GetIPs_(set<CNetAddr>& ips, uint64_t requestedFlags, int max, cons
} else {
id = *ourId.begin();
}
if (id >= 0 && (idToInfo[id].services & requestedFlags) == requestedFlags) {
if (id >= 0) {
ips.insert(idToInfo[id].ip);
}
return;
}
std::vector<int> goodIdFiltered;
for (std::set<int>::const_iterator it = goodId.begin(); it != goodId.end(); it++) {
if ((idToInfo[*it].services & requestedFlags) == requestedFlags)
goodIdFiltered.push_back(*it);
}
if (!goodIdFiltered.size())
return;
if (max > goodIdFiltered.size() / 2)
max = goodIdFiltered.size() / 2;
if (max > goodId.size() / 2)
max = goodId.size() / 2;
if (max < 1)
max = 1;
int low = *goodId.begin();
int high = *goodId.rbegin();
set<int> ids;
while (ids.size() < max) {
ids.insert(goodIdFiltered[rand() % goodIdFiltered.size()]);
int range = high-low+1;
int pos = low + (rand() % range);
int id = *(goodId.lower_bound(pos));
ids.insert(id);
}
for (set<int>::const_iterator it = ids.begin(); it != ids.end(); it++) {
CService &ip = idToInfo[*it].ip;

24
db.h
View file

@ -12,11 +12,11 @@
#define MIN_RETRY 1000
#define REQUIRE_VERSION 70015
#define REQUIRE_VERSION 70001
static inline int GetRequireHeight(const bool testnet = fTestNet)
{
return testnet ? 1000 : 940000;
return testnet ? 500000 : 350000;
}
std::string static inline ToString(const CService &ip) {
@ -119,7 +119,7 @@ public:
}
int GetBanTime() const {
if (IsGood()) return 0;
if (clientVersion && clientVersion < 31800) { return 604800; }
if (clientVersion && clientVersion < 31900) { return 604800; }
if (stat1M.reliability - stat1M.weight + 1.0 < 0.15 && stat1M.count > 32) { return 30*86400; }
if (stat1W.reliability - stat1W.weight + 1.0 < 0.10 && stat1W.count > 16) { return 7*86400; }
if (stat1D.reliability - stat1D.weight + 1.0 < 0.05 && stat1D.count > 8) { return 1*86400; }
@ -183,7 +183,6 @@ public:
struct CServiceResult {
CService service;
uint64_t services;
bool fGood;
int nBanTime;
int nHeight;
@ -216,11 +215,11 @@ protected:
void Add_(const CAddress &addr, bool force); // add an address
bool Get_(CServiceResult &ip, int& wait); // get an IP to test (must call Good_, Bad_, or Skipped_ on result afterwards)
bool GetMany_(std::vector<CServiceResult> &ips, int max, int& wait);
void Good_(const CService &ip, int clientV, std::string clientSV, int blocks, uint64_t services); // mark an IP as good (must have been returned by Get_)
void Good_(const CService &ip, int clientV, std::string clientSV, int blocks); // mark an IP as good (must have been returned by Get_)
void Bad_(const CService &ip, int ban); // mark an IP as bad (and optionally ban it) (must have been returned by Get_)
void Skipped_(const CService &ip); // mark an IP as skipped (must have been returned by Get_)
int Lookup_(const CService &ip); // look up id of an IP
void GetIPs_(std::set<CNetAddr>& ips, uint64_t requestedFlags, int max, const bool *nets); // get a random set of IPs (shared lock only)
void GetIPs_(std::set<CNetAddr>& ips, int max, const bool *nets); // get a random set of IPs (shared lock only)
public:
std::map<CService, time_t> banned; // nodes that are banned, with their unban time (a)
@ -282,7 +281,7 @@ public:
} else {
CAddrDb *db = const_cast<CAddrDb*>(this);
db->nId = 0;
int n = 0;
int n;
READWRITE(n);
for (int i=0; i<n; i++) {
CAddrInfo info;
@ -314,9 +313,9 @@ public:
for (int i=0; i<vAddr.size(); i++)
Add_(vAddr[i], fForce);
}
void Good(const CService &addr, int clientVersion, std::string clientSubVersion, int blocks, uint64_t services) {
void Good(const CService &addr, int clientVersion, std::string clientSubVersion, int blocks) {
CRITICAL_BLOCK(cs)
Good_(addr, clientVersion, clientSubVersion, blocks, services);
Good_(addr, clientVersion, clientSubVersion, blocks);
}
void Skipped(const CService &addr) {
CRITICAL_BLOCK(cs)
@ -329,7 +328,6 @@ public:
bool Get(CServiceResult &ip, int& wait) {
CRITICAL_BLOCK(cs)
return Get_(ip, wait);
return false;
}
void GetMany(std::vector<CServiceResult> &ips, int max, int& wait) {
CRITICAL_BLOCK(cs) {
@ -346,15 +344,15 @@ public:
CRITICAL_BLOCK(cs) {
for (int i=0; i<ips.size(); i++) {
if (ips[i].fGood) {
Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV, ips[i].nHeight, ips[i].services);
Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV, ips[i].nHeight);
} else {
Bad_(ips[i].service, ips[i].nBanTime);
}
}
}
}
void GetIPs(std::set<CNetAddr>& ips, uint64_t requestedFlags, int max, const bool *nets) {
void GetIPs(std::set<CNetAddr>& ips, int max, const bool *nets) {
SHARED_CRITICAL_BLOCK(cs)
GetIPs_(ips, requestedFlags, max, nets);
GetIPs_(ips, max, nets);
}
};

View file

@ -16,14 +16,20 @@
#define BUFLEN 512
#if defined(IP_RECVDSTADDR)
#if defined IP_RECVDSTADDR
# define DSTADDR_SOCKOPT IP_RECVDSTADDR
# define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_addr)))
# define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_addr)))
# define dstaddr(x) (CMSG_DATA(x))
#elif defined(IPV6_PKTINFO)
# define DSTADDR_SOCKOPT IPV6_PKTINFO
# define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in6_pktinfo)))
# define dstaddr(x) (&(((struct in6_pktinfo *)(CMSG_DATA(x)))->ipi6_addr))
#elif defined IP_PKTINFO
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};
# define DSTADDR_SOCKOPT IP_PKTINFO
# define DSTADDR_DATASIZE (CMSG_SPACE(sizeof(struct in_pktinfo)))
# define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr))
#else
# error "can't determine socket option"
#endif
@ -103,7 +109,7 @@ int static parse_name(const unsigned char **inpos, const unsigned char *inend, c
// -3: two subsequent dots
int static write_name(unsigned char** outpos, const unsigned char *outend, const char *name, int offset) {
while (*name != 0) {
const char *dot = strchr(name, '.');
char *dot = strchr(name, '.');
const char *fin = dot;
if (!dot) fin = name + strlen(name);
if (fin - name > 63) return -1;
@ -132,21 +138,16 @@ int static write_record(unsigned char** outpos, const unsigned char *outend, con
int error = 0;
// name
int ret = write_name(outpos, outend, name, offset);
if (ret) {
error = ret;
} else {
if (outend - *outpos < 8) {
error = -4;
} else {
// type
*((*outpos)++) = typ >> 8; *((*outpos)++) = typ & 0xFF;
// class
*((*outpos)++) = cls >> 8; *((*outpos)++) = cls & 0xFF;
// ttl
*((*outpos)++) = (ttl >> 24) & 0xFF; *((*outpos)++) = (ttl >> 16) & 0xFF; *((*outpos)++) = (ttl >> 8) & 0xFF; *((*outpos)++) = ttl & 0xFF;
return 0;
}
}
if (ret) { error = ret; goto error; }
if (outend - *outpos < 8) { error = -4; goto error; }
// type
*((*outpos)++) = typ >> 8; *((*outpos)++) = typ & 0xFF;
// class
*((*outpos)++) = cls >> 8; *((*outpos)++) = cls & 0xFF;
// ttl
*((*outpos)++) = (ttl >> 24) & 0xFF; *((*outpos)++) = (ttl >> 16) & 0xFF; *((*outpos)++) = (ttl >> 8) & 0xFF; *((*outpos)++) = ttl & 0xFF;
return 0;
error:
*outpos = oldpos;
return error;
}
@ -159,16 +160,14 @@ int static write_record_a(unsigned char** outpos, const unsigned char *outend, c
int error = 0;
int ret = write_record(outpos, outend, name, offset, TYPE_A, cls, ttl);
if (ret) return ret;
if (outend - *outpos < 6) {
error = -5;
} else {
// rdlength
*((*outpos)++) = 0; *((*outpos)++) = 4;
// rdata
for (int i=0; i<4; i++)
*((*outpos)++) = ip->data.v4[i];
return 0;
}
if (outend - *outpos < 6) { error = -5; goto error; }
// rdlength
*((*outpos)++) = 0; *((*outpos)++) = 4;
// rdata
for (int i=0; i<4; i++)
*((*outpos)++) = ip->data.v4[i];
return 0;
error:
*outpos = oldpos;
return error;
}
@ -180,92 +179,63 @@ int static write_record_aaaa(unsigned char** outpos, const unsigned char *outend
int error = 0;
int ret = write_record(outpos, outend, name, offset, TYPE_AAAA, cls, ttl);
if (ret) return ret;
if (outend - *outpos < 18) {
error = -5;
} else {
// rdlength
*((*outpos)++) = 0; *((*outpos)++) = 16;
// rdata
for (int i=0; i<16; i++)
*((*outpos)++) = ip->data.v6[i];
return 0;
}
if (outend - *outpos < 6) { error = -5; goto error; }
// rdlength
*((*outpos)++) = 0; *((*outpos)++) = 16;
// rdata
for (int i=0; i<16; i++)
*((*outpos)++) = ip->data.v6[i];
return 0;
error:
*outpos = oldpos;
return error;
}
int static write_record_ns(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_class cls, int ttl, const char *ns) {
int static write_record_ns(unsigned char** outpos, const unsigned char *outend, char *name, int offset, dns_class cls, int ttl, const char *ns) {
unsigned char *oldpos = *outpos;
int ret = write_record(outpos, outend, name, offset, TYPE_NS, cls, ttl);
if (ret) return ret;
int error = 0;
if (outend - *outpos < 2) {
error = -5;
} else {
(*outpos) += 2;
unsigned char *curpos = *outpos;
ret = write_name(outpos, outend, ns, -1);
if (ret) {
error = ret;
} else {
curpos[-2] = (*outpos - curpos) >> 8;
curpos[-1] = (*outpos - curpos) & 0xFF;
return 0;
}
}
if (outend - *outpos < 2) { error = -5; goto error; }
(*outpos) += 2;
unsigned char *curpos = *outpos;
ret = write_name(outpos, outend, ns, -1);
if (ret) { error = ret; goto error; }
curpos[-2] = (*outpos - curpos) >> 8;
curpos[-1] = (*outpos - curpos) & 0xFF;
return 0;
error:
*outpos = oldpos;
return error;
}
int static write_record_soa(unsigned char** outpos, const unsigned char *outend, const char *name, int offset, dns_class cls, int ttl, const char* mname, const char *rname,
int static write_record_soa(unsigned char** outpos, const unsigned char *outend, char *name, int offset, dns_class cls, int ttl, const char* mname, const char *rname,
uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) {
unsigned char *oldpos = *outpos;
int ret = write_record(outpos, outend, name, offset, TYPE_SOA, cls, ttl);
if (ret) return ret;
int error = 0;
if (outend - *outpos < 2) {
error = -5;
} else {
(*outpos) += 2;
unsigned char *curpos = *outpos;
ret = write_name(outpos, outend, mname, -1);
if (ret) {
error = ret;
} else {
ret = write_name(outpos, outend, rname, -1);
if (ret) {
error = ret;
} else {
if (outend - *outpos < 20) {
error = -5;
} else {
*((*outpos)++) = (serial >> 24) & 0xFF; *((*outpos)++) = (serial >> 16) & 0xFF; *((*outpos)++) = (serial >> 8) & 0xFF; *((*outpos)++) = serial & 0xFF;
*((*outpos)++) = (refresh >> 24) & 0xFF; *((*outpos)++) = (refresh >> 16) & 0xFF; *((*outpos)++) = (refresh >> 8) & 0xFF; *((*outpos)++) = refresh & 0xFF;
*((*outpos)++) = (retry >> 24) & 0xFF; *((*outpos)++) = (retry >> 16) & 0xFF; *((*outpos)++) = (retry >> 8) & 0xFF; *((*outpos)++) = retry & 0xFF;
*((*outpos)++) = (expire >> 24) & 0xFF; *((*outpos)++) = (expire >> 16) & 0xFF; *((*outpos)++) = (expire >> 8) & 0xFF; *((*outpos)++) = expire & 0xFF;
*((*outpos)++) = (minimum >> 24) & 0xFF; *((*outpos)++) = (minimum >> 16) & 0xFF; *((*outpos)++) = (minimum >> 8) & 0xFF; *((*outpos)++) = minimum & 0xFF;
curpos[-2] = (*outpos - curpos) >> 8;
curpos[-1] = (*outpos - curpos) & 0xFF;
return 0;
}
}
}
}
if (outend - *outpos < 2) { error = -5; goto error; }
(*outpos) += 2;
unsigned char *curpos = *outpos;
ret = write_name(outpos, outend, mname, -1);
if (ret) { error = ret; goto error; }
ret = write_name(outpos, outend, rname, -1);
if (ret) { error = ret; goto error; }
if (outend - *outpos < 20) { error = -5; goto error; }
*((*outpos)++) = (serial >> 24) & 0xFF; *((*outpos)++) = (serial >> 16) & 0xFF; *((*outpos)++) = (serial >> 8) & 0xFF; *((*outpos)++) = serial & 0xFF;
*((*outpos)++) = (refresh >> 24) & 0xFF; *((*outpos)++) = (refresh >> 16) & 0xFF; *((*outpos)++) = (refresh >> 8) & 0xFF; *((*outpos)++) = refresh & 0xFF;
*((*outpos)++) = (retry >> 24) & 0xFF; *((*outpos)++) = (retry >> 16) & 0xFF; *((*outpos)++) = (retry >> 8) & 0xFF; *((*outpos)++) = retry & 0xFF;
*((*outpos)++) = (expire >> 24) & 0xFF; *((*outpos)++) = (expire >> 16) & 0xFF; *((*outpos)++) = (expire >> 8) & 0xFF; *((*outpos)++) = expire & 0xFF;
*((*outpos)++) = (minimum >> 24) & 0xFF; *((*outpos)++) = (minimum >> 16) & 0xFF; *((*outpos)++) = (minimum >> 8) & 0xFF; *((*outpos)++) = minimum & 0xFF;
curpos[-2] = (*outpos - curpos) >> 8;
curpos[-1] = (*outpos - curpos) & 0xFF;
return 0;
error:
*outpos = oldpos;
return error;
}
static ssize_t set_error(unsigned char* outbuf, int error) {
// set error
outbuf[3] |= error & 0xF;
// set counts
outbuf[4] = 0; outbuf[5] = 0;
outbuf[6] = 0; outbuf[7] = 0;
outbuf[8] = 0; outbuf[9] = 0;
outbuf[10] = 0; outbuf[11] = 0;
return 12;
}
ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insize, unsigned char* outbuf) {
int error = 0;
if (insize < 12) // DNS header
@ -279,27 +249,27 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
// clear error
outbuf[3] &= ~15;
// check qr
if (inbuf[2] & 128) return set_error(outbuf, 1); /* printf("Got response?\n"); */
if (inbuf[2] & 128) { /* printf("Got response?\n"); */ error = 1; goto error; }
// check opcode
if (((inbuf[2] & 120) >> 3) != 0) return set_error(outbuf, 1); /* printf("Opcode nonzero?\n"); */
if (((inbuf[2] & 120) >> 3) != 0) { /* printf("Opcode nonzero?\n"); */ error = 4; goto error; }
// unset TC
outbuf[2] &= ~2;
// unset RA
outbuf[3] &= ~128;
// check questions
int nquestion = (inbuf[4] << 8) + inbuf[5];
if (nquestion == 0) return set_error(outbuf, 0); /* printf("No questions?\n"); */
if (nquestion > 1) return set_error(outbuf, 4); /* printf("Multiple questions %i?\n", nquestion); */
if (nquestion == 0) { /* printf("No questions?\n"); */ error = 0; goto error; }
if (nquestion > 1) { /* printf("Multiple questions %i?\n", nquestion); */ error = 4; goto error; }
const unsigned char *inpos = inbuf + 12;
const unsigned char *inend = inbuf + insize;
char name[256];
int offset = inpos - inbuf;
int ret = parse_name(&inpos, inend, inbuf, name, 256);
if (ret == -1) return set_error(outbuf, 1);
if (ret == -2) return set_error(outbuf, 5);
if (ret == -1) { error = 1; goto error; }
if (ret == -2) { error = 5; goto error; }
int namel = strlen(name), hostl = strlen(opt->host);
if (strcasecmp(name, opt->host) && (namel<hostl+2 || name[namel-hostl-1]!='.' || strcasecmp(name+namel-hostl,opt->host))) return set_error(outbuf, 5);
if (inend - inpos < 4) return set_error(outbuf, 1);
if (strcasecmp(name, opt->host) && (namel<hostl+2 || name[namel-hostl-1]!='.' || strcasecmp(name+namel-hostl,opt->host))) { error = 5; goto error; }
if (inend - inpos < 4) { error = 1; goto error; }
// copy question to output
memcpy(outbuf+12, inbuf+12, inpos+4 - (inbuf+12));
// set counts
@ -357,7 +327,7 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
// A/AAAA records
if ((typ == TYPE_A || typ == TYPE_AAAA || typ == QTYPE_ANY) && (cls == CLASS_IN || cls == QCLASS_ANY)) {
addr_t addr[32];
int naddr = opt->cb((void*)opt, name, addr, 32, typ == TYPE_A || typ == QTYPE_ANY, typ == TYPE_AAAA || typ == QTYPE_ANY);
int naddr = opt->cb((void*)opt, addr, 32, typ == TYPE_A || typ == QTYPE_ANY, typ == TYPE_AAAA || typ == QTYPE_ANY);
int n = 0;
while (n < naddr) {
int ret = 1;
@ -396,36 +366,45 @@ ssize_t static dnshandle(dns_opt_t *opt, const unsigned char *inbuf, size_t insi
outbuf[2] |= 4;
return outpos - outbuf;
error:
// set error
outbuf[3] |= error & 0xF;
// set counts
outbuf[4] = 0; outbuf[5] = 0;
outbuf[6] = 0; outbuf[7] = 0;
outbuf[8] = 0; outbuf[9] = 0;
outbuf[10] = 0; outbuf[11] = 0;
return 12;
}
static int listenSocket = -1;
int dnsserver(dns_opt_t *opt) {
struct sockaddr_in6 si_other;
struct sockaddr_in si_other;
int senderSocket = -1;
senderSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
senderSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (senderSocket == -1)
return -3;
int replySocket;
if (listenSocket == -1) {
struct sockaddr_in6 si_me;
if ((listenSocket=socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP))==-1) {
struct sockaddr_in si_me;
if ((listenSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
listenSocket = -1;
return -1;
}
replySocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
replySocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (replySocket == -1)
{
close(listenSocket);
return -1;
}
int sockopt = 1;
setsockopt(listenSocket, IPPROTO_IPV6, DSTADDR_SOCKOPT, &sockopt, sizeof sockopt);
setsockopt(listenSocket, IPPROTO_IP, DSTADDR_SOCKOPT, &sockopt, sizeof sockopt);
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin6_family = AF_INET6;
si_me.sin6_port = htons(opt->port);
inet_pton(AF_INET6, opt->addr, &si_me.sin6_addr);
si_me.sin_family = AF_INET;
si_me.sin_port = htons(opt->port);
si_me.sin_addr.s_addr = INADDR_ANY;
if (bind(listenSocket, (struct sockaddr*)&si_me, sizeof(si_me))==-1)
return -2;
}
@ -449,7 +428,7 @@ int dnsserver(dns_opt_t *opt) {
for (; 1; ++(opt->nRequests))
{
ssize_t insize = recvmsg(listenSocket, &msg, 0);
// unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr;
unsigned char *addr = (unsigned char*)&si_other.sin_addr.s_addr;
// printf("DNS: Request %llu from %i.%i.%i.%i:%i of %i bytes\n", (unsigned long long)(opt->nRequests), addr[0], addr[1], addr[2], addr[3], ntohs(si_other.sin_port), (int)insize);
if (insize <= 0)
continue;

13
dns.h
View file

@ -3,27 +3,26 @@
#include <stdint.h>
struct addr_t {
typedef struct {
int v;
union {
unsigned char v4[4];
unsigned char v6[16];
} data;
};
} addr_t;
struct dns_opt_t {
typedef struct {
int port;
int datattl;
int nsttl;
const char *host;
const char *addr;
const char *ns;
const char *mbox;
int (*cb)(void *opt, char *requested_hostname, addr_t *addr, int max, int ipv4, int ipv6);
int (*cb)(void *opt, addr_t *addr, int max, int ipv4, int ipv6);
// stats
uint64_t nRequests;
};
} dns_opt_t;
int dnsserver(dns_opt_t *opt);
extern int dnsserver(dns_opt_t *opt);
#endif

190
main.cpp
View file

@ -7,7 +7,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <atomic>
#include "bitcoin.h"
#include "db.h"
@ -28,15 +27,13 @@ public:
const char *ns;
const char *host;
const char *tor;
const char *ip_addr;
const char *ipv4_proxy;
const char *ipv6_proxy;
std::set<uint64_t> filter_whitelist;
CDnsSeedOpts() : nThreads(96), nDnsThreads(4), ip_addr("::"), nPort(53), mbox(NULL), ns(NULL), host(NULL), tor(NULL), fUseTestNet(false), fWipeBan(false), fWipeIgnore(false), ipv4_proxy(NULL), ipv6_proxy(NULL) {}
CDnsSeedOpts() : nThreads(96), nDnsThreads(4), nPort(53), mbox(NULL), ns(NULL), host(NULL), tor(NULL), fUseTestNet(false), fWipeBan(false), fWipeIgnore(false) {}
void ParseCommandLine(int argc, char **argv) {
static const char *help = "LBRY-seeder\n"
static const char *help = "Bitcoin-seeder\n"
"Usage: %s -h <host> -n <ns> [-m <mbox>] [-t <threads>] [-p <port>]\n"
"\n"
"Options:\n"
@ -45,12 +42,10 @@ public:
"-m <mbox> E-Mail address reported in SOA records\n"
"-t <threads> Number of crawlers to run in parallel (default 96)\n"
"-d <threads> Number of DNS server threads (default 4)\n"
"-a <address> Address to listen on (default ::)\n"
"-p <port> UDP port to listen on (default 53)\n"
"-o <ip:port> Tor proxy IP/Port\n"
"-i <ip:port> IPV4 SOCKS5 proxy IP/Port\n"
"-k <ip:port> IPV6 SOCKS5 proxy IP/Port\n"
"-w f1,f2,... Allow these flag combinations as filters\n"
"--testnet Use testnet\n"
"--wipeban Wipe list of banned nodes\n"
"--wipeignore Wipe list of ignored nodes\n"
@ -65,12 +60,10 @@ public:
{"mbox", required_argument, 0, 'm'},
{"threads", required_argument, 0, 't'},
{"dnsthreads", required_argument, 0, 'd'},
{"address", required_argument, 0, 'a'},
{"port", required_argument, 0, 'p'},
{"onion", required_argument, 0, 'o'},
{"proxyipv4", required_argument, 0, 'i'},
{"proxyipv6", required_argument, 0, 'k'},
{"filter", required_argument, 0, 'w'},
{"testnet", no_argument, &fUseTestNet, 1},
{"wipeban", no_argument, &fWipeBan, 1},
{"wipeignore", no_argument, &fWipeBan, 1},
@ -78,24 +71,24 @@ public:
{0, 0, 0, 0}
};
int option_index = 0;
int c = getopt_long(argc, argv, "h:n:m:t:a:p:d:o:i:k:w:?", long_options, &option_index);
int c = getopt_long(argc, argv, "h:n:m:t:p:d:o:i:k:", long_options, &option_index);
if (c == -1) break;
switch (c) {
case 'h': {
host = optarg;
break;
}
case 'm': {
mbox = optarg;
break;
}
case 'n': {
ns = optarg;
break;
}
case 't': {
int n = strtol(optarg, NULL, 10);
if (n > 0 && n < 1000) nThreads = n;
@ -108,18 +101,6 @@ public:
break;
}
case 'a': {
if (strchr(optarg, ':')==NULL) {
char* ip4_addr = (char*) malloc(strlen(optarg)+8);
strcpy(ip4_addr, "::FFFF:");
strcat(ip4_addr, optarg);
ip_addr = ip4_addr;
} else {
ip_addr = optarg;
}
break;
}
case 'p': {
int p = strtol(optarg, NULL, 10);
if (p > 0 && p < 65536) nPort = p;
@ -141,44 +122,20 @@ public:
break;
}
case 'w': {
char* ptr = optarg;
while (*ptr != 0) {
unsigned long l = strtoul(ptr, &ptr, 0);
if (*ptr == ',') {
ptr++;
} else if (*ptr != 0) {
break;
}
filter_whitelist.insert(l);
}
break;
}
case '?': {
showHelp = true;
break;
}
}
}
if (filter_whitelist.empty()) {
filter_whitelist.insert(NODE_NETWORK); // x1
filter_whitelist.insert(NODE_NETWORK | NODE_BLOOM); // x5
filter_whitelist.insert(NODE_NETWORK | NODE_WITNESS); // x9
filter_whitelist.insert(NODE_NETWORK | NODE_WITNESS | NODE_COMPACT_FILTERS); // x49
filter_whitelist.insert(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM); // xd
filter_whitelist.insert(NODE_NETWORK_LIMITED); // x400
filter_whitelist.insert(NODE_NETWORK_LIMITED | NODE_BLOOM); // x404
filter_whitelist.insert(NODE_NETWORK_LIMITED | NODE_WITNESS); // x408
filter_whitelist.insert(NODE_NETWORK_LIMITED | NODE_WITNESS | NODE_COMPACT_FILTERS); // x448
filter_whitelist.insert(NODE_NETWORK_LIMITED | NODE_WITNESS | NODE_BLOOM); // x40c
}
if (host != NULL && ns == NULL) showHelp = true;
if (showHelp) fprintf(stderr, help, argv[0]);
}
};
extern "C" {
#include "dns.h"
}
CAddrDb db;
@ -202,51 +159,42 @@ extern "C" void* ThreadCrawler(void* data) {
res.nClientV = 0;
res.nHeight = 0;
res.strClientV = "";
res.services = 0;
bool getaddr = res.ourLastSuccess + 86400 < now;
res.fGood = TestNode(res.service,res.nBanTime,res.nClientV,res.strClientV,res.nHeight,getaddr ? &addr : NULL, res.services);
bool getaddr = res.ourLastSuccess + 604800 < now;
res.fGood = TestNode(res.service,res.nBanTime,res.nClientV,res.strClientV,res.nHeight,getaddr ? &addr : NULL);
}
db.ResultMany(ips);
db.Add(addr);
} while(1);
return nullptr;
}
extern "C" int GetIPList(void *thread, char *requestedHostname, addr_t *addr, int max, int ipv4, int ipv6);
extern "C" int GetIPList(void *thread, addr_t *addr, int max, int ipv4, int ipv6);
class CDnsThread {
public:
struct FlagSpecificData {
int nIPv4, nIPv6;
std::vector<addr_t> cache;
time_t cacheTime;
unsigned int cacheHits;
FlagSpecificData() : nIPv4(0), nIPv6(0), cacheTime(0), cacheHits(0) {}
};
dns_opt_t dns_opt; // must be first
const int id;
std::map<uint64_t, FlagSpecificData> perflag;
std::atomic<uint64_t> dbQueries;
std::set<uint64_t> filterWhitelist;
vector<addr_t> cache;
int nIPv4, nIPv6;
time_t cacheTime;
unsigned int cacheHits;
uint64_t dbQueries;
void cacheHit(uint64_t requestedFlags, bool force = false) {
void cacheHit(bool force = false) {
static bool nets[NET_MAX] = {};
if (!nets[NET_IPV4]) {
nets[NET_IPV4] = true;
nets[NET_IPV6] = true;
}
time_t now = time(NULL);
FlagSpecificData& thisflag = perflag[requestedFlags];
thisflag.cacheHits++;
if (force || thisflag.cacheHits * 400 > (thisflag.cache.size()*thisflag.cache.size()) || (thisflag.cacheHits*thisflag.cacheHits * 20 > thisflag.cache.size() && (now - thisflag.cacheTime > 5))) {
cacheHits++;
if (force || cacheHits > (cache.size()*cache.size()/400) || (cacheHits*cacheHits > cache.size() / 20 && (now - cacheTime > 5))) {
set<CNetAddr> ips;
db.GetIPs(ips, requestedFlags, 1000, nets);
db.GetIPs(ips, 1000, nets);
dbQueries++;
thisflag.cache.clear();
thisflag.nIPv4 = 0;
thisflag.nIPv6 = 0;
thisflag.cache.reserve(ips.size());
cache.clear();
nIPv4 = 0;
nIPv6 = 0;
cache.reserve(ips.size());
for (set<CNetAddr>::iterator it = ips.begin(); it != ips.end(); it++) {
struct in_addr addr;
struct in6_addr addr6;
@ -254,18 +202,20 @@ public:
addr_t a;
a.v = 4;
memcpy(&a.data.v4, &addr, 4);
thisflag.cache.push_back(a);
thisflag.nIPv4++;
cache.push_back(a);
nIPv4++;
#ifdef USE_IPV6
} else if ((*it).GetIn6Addr(&addr6)) {
addr_t a;
a.v = 6;
memcpy(&a.data.v6, &addr6, 16);
thisflag.cache.push_back(a);
thisflag.nIPv6++;
cache.push_back(a);
nIPv6++;
#endif
}
}
thisflag.cacheHits = 0;
thisflag.cacheTime = now;
cacheHits = 0;
cacheTime = now;
}
}
@ -276,12 +226,16 @@ public:
dns_opt.datattl = 3600;
dns_opt.nsttl = 40000;
dns_opt.cb = GetIPList;
dns_opt.addr = opts->ip_addr;
dns_opt.port = opts->nPort;
dns_opt.nRequests = 0;
cache.clear();
cache.reserve(1000);
cacheTime = 0;
cacheHits = 0;
dbQueries = 0;
perflag.clear();
filterWhitelist = opts->filter_whitelist;
nIPv4 = 0;
nIPv6 = 0;
cacheHit(true);
}
void run() {
@ -289,25 +243,11 @@ public:
}
};
extern "C" int GetIPList(void *data, char *requestedHostname, addr_t* addr, int max, int ipv4, int ipv6) {
extern "C" int GetIPList(void *data, addr_t* addr, int max, int ipv4, int ipv6) {
CDnsThread *thread = (CDnsThread*)data;
uint64_t requestedFlags = 0;
int hostlen = strlen(requestedHostname);
if (hostlen > 1 && requestedHostname[0] == 'x' && requestedHostname[1] != '0') {
char *pEnd;
uint64_t flags = (uint64_t)strtoull(requestedHostname+1, &pEnd, 16);
if (*pEnd == '.' && pEnd <= requestedHostname+17 && std::find(thread->filterWhitelist.begin(), thread->filterWhitelist.end(), flags) != thread->filterWhitelist.end())
requestedFlags = flags;
else
return 0;
}
else if (strcasecmp(requestedHostname, thread->dns_opt.host))
return 0;
thread->cacheHit(requestedFlags);
auto& thisflag = thread->perflag[requestedFlags];
unsigned int size = thisflag.cache.size();
unsigned int maxmax = (ipv4 ? thisflag.nIPv4 : 0) + (ipv6 ? thisflag.nIPv6 : 0);
thread->cacheHit();
unsigned int size = thread->cache.size();
unsigned int maxmax = (ipv4 ? thread->nIPv4 : 0) + (ipv6 ? thread->nIPv6 : 0);
if (max > size)
max = size;
if (max > maxmax)
@ -316,16 +256,16 @@ extern "C" int GetIPList(void *data, char *requestedHostname, addr_t* addr, int
while (i<max) {
int j = i + (rand() % (size - i));
do {
bool ok = (ipv4 && thisflag.cache[j].v == 4) ||
(ipv6 && thisflag.cache[j].v == 6);
bool ok = (ipv4 && thread->cache[j].v == 4) ||
(ipv6 && thread->cache[j].v == 6);
if (ok) break;
j++;
if (j==size)
j=i;
} while(1);
addr[i] = thisflag.cache[j];
thisflag.cache[j] = thisflag.cache[i];
thisflag.cache[i] = addr[i];
addr[i] = thread->cache[j];
thread->cache[j] = thread->cache[i];
thread->cache[i] = addr[i];
i++;
}
return max;
@ -336,7 +276,6 @@ vector<CDnsThread*> dnsThread;
extern "C" void* ThreadDNS(void* arg) {
CDnsThread *thread = (CDnsThread*)arg;
thread->run();
return nullptr;
}
int StatCompare(const CAddrReport& a, const CAddrReport& b) {
@ -373,7 +312,7 @@ extern "C" void* ThreadDumper(void*) {
double stat[5]={0,0,0,0,0};
for (vector<CAddrReport>::const_iterator it = v.begin(); it < v.end(); it++) {
CAddrReport rep = *it;
fprintf(d, "%-47s %4d %11" PRId64 " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 " %5i \"%s\"\n", rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, 100.0*rep.uptime[0], 100.0*rep.uptime[1], 100.0*rep.uptime[2], 100.0*rep.uptime[3], 100.0*rep.uptime[4], rep.blocks, rep.services, rep.clientVersion, rep.clientSubVersion.c_str());
fprintf(d, "%-47s %4d %11"PRId64" %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08"PRIx64" %5i \"%s\"\n", rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, 100.0*rep.uptime[0], 100.0*rep.uptime[1], 100.0*rep.uptime[2], 100.0*rep.uptime[3], 100.0*rep.uptime[4], rep.blocks, rep.services, rep.clientVersion, rep.clientSubVersion.c_str());
stat[0] += rep.uptime[0];
stat[1] += rep.uptime[1];
stat[2] += rep.uptime[2];
@ -386,7 +325,6 @@ extern "C" void* ThreadDumper(void*) {
fclose(ff);
}
} while(1);
return nullptr;
}
extern "C" void* ThreadStats(void*) {
@ -415,12 +353,13 @@ extern "C" void* ThreadStats(void*) {
printf("%s %i/%i available (%i tried in %is, %i new, %i active), %i banned; %llu DNS requests, %llu db queries", c, stats.nGood, stats.nAvail, stats.nTracked, stats.nAge, stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, stats.nBanned, (unsigned long long)requests, (unsigned long long)queries);
Sleep(1000);
} while(1);
return nullptr;
}
static const string mainnet_seeds[] = {"dnsseed1.lbry.io", "dnsseed2.lbry.io", "dnsseed3.lbry.io", ""};
static const string testnet_seeds[] = {"testdnsseed1.lbry.io",
"testdnsseed2.lbry.io",
static const string mainnet_seeds[] = {"dnsseed.bluematt.me", "bitseed.xf2.org", "dnsseed.bitcoin.dashjr.org", "seed.bitcoin.sipa.be", ""};
static const string testnet_seeds[] = {"testnet-seed.alexykot.me",
"testnet-seed.bitcoin.petertodd.org",
"testnet-seed.bluematt.me",
"testnet-seed.bitcoin.schildbach.de",
""};
static const string *seeds = mainnet_seeds;
@ -438,7 +377,6 @@ extern "C" void* ThreadSeeder(void*) {
}
Sleep(1800000);
} while(1);
return nullptr;
}
int main(int argc, char **argv) {
@ -446,14 +384,6 @@ int main(int argc, char **argv) {
setbuf(stdout, NULL);
CDnsSeedOpts opts;
opts.ParseCommandLine(argc, argv);
printf("Supporting whitelisted filters: ");
for (std::set<uint64_t>::const_iterator it = opts.filter_whitelist.begin(); it != opts.filter_whitelist.end(); it++) {
if (it != opts.filter_whitelist.begin()) {
printf(",");
}
printf("0x%lx", (unsigned long)*it);
}
printf("\n");
if (opts.tor) {
CService service(opts.tor, 9050);
if (service.IsValid()) {
@ -478,10 +408,10 @@ int main(int argc, char **argv) {
bool fDNS = true;
if (opts.fUseTestNet) {
printf("Using testnet.\n");
pchMessageStart[0] = 0xfa;
pchMessageStart[1] = 0xe4;
pchMessageStart[2] = 0xaa;
pchMessageStart[3] = 0xe1;
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
pchMessageStart[2] = 0x09;
pchMessageStart[3] = 0x07;
seeds = testnet_seeds;
fTestNet = true;
}
@ -493,10 +423,6 @@ int main(int argc, char **argv) {
fprintf(stderr, "No hostname set. Please use -h.\n");
exit(1);
}
if (fDNS && !opts.mbox) {
fprintf(stderr, "No e-mail address set. Please use -m.\n");
exit(1);
}
FILE *f = fopen("dnsseed.dat","r");
if (f) {
printf("Loading dnsseed.dat...");

View file

@ -74,10 +74,18 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
aiHint.ai_socktype = SOCK_STREAM;
aiHint.ai_protocol = IPPROTO_TCP;
#ifdef WIN32
# ifdef USE_IPV6
aiHint.ai_family = AF_UNSPEC;
# else
aiHint.ai_family = AF_INET;
# endif
aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST;
#else
# ifdef USE_IPV6
aiHint.ai_family = AF_UNSPEC;
# else
aiHint.ai_family = AF_INET;
# endif
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
#endif
struct addrinfo *aiRes = NULL;
@ -94,11 +102,13 @@ bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsign
vIP.push_back(CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr));
}
#ifdef USE_IPV6
if (aiTrav->ai_family == AF_INET6)
{
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6));
vIP.push_back(CNetAddr(((struct sockaddr_in6*)(aiTrav->ai_addr))->sin6_addr));
}
#endif
aiTrav = aiTrav->ai_next;
}
@ -313,7 +323,11 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
{
hSocketRet = INVALID_SOCKET;
#ifdef USE_IPV6
struct sockaddr_storage sockaddr;
#else
struct sockaddr sockaddr;
#endif
socklen_t len = sizeof(sockaddr);
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
printf("Cannot connect to %s: unsupported network\n", addrConnect.ToString().c_str());
@ -564,10 +578,12 @@ CNetAddr::CNetAddr(const struct in_addr& ipv4Addr)
memcpy(ip+12, &ipv4Addr, 4);
}
#ifdef USE_IPV6
CNetAddr::CNetAddr(const struct in6_addr& ipv6Addr)
{
memcpy(ip, &ipv6Addr, 16);
}
#endif
CNetAddr::CNetAddr(const char *pszIp, bool fAllowLookup)
{
@ -756,7 +772,11 @@ std::string CNetAddr::ToStringIP() const
if (IsI2P())
return EncodeBase32(&ip[6], 10) + ".oc.b32.i2p";
CService serv(*this, 0);
#ifdef USE_IPV6
struct sockaddr_storage sockaddr;
#else
struct sockaddr sockaddr;
#endif
socklen_t socklen = sizeof(sockaddr);
if (serv.GetSockAddr((struct sockaddr*)&sockaddr, &socklen)) {
char name[1025] = "";
@ -801,11 +821,13 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const
return true;
}
#ifdef USE_IPV6
bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
{
memcpy(pipv6Addr, ip, 16);
return true;
}
#endif
// get canonical identifier of an address' group
// no two connections will be attempted to addresses with the same group
@ -991,19 +1013,23 @@ CService::CService(const struct in_addr& ipv4Addr, unsigned short portIn) : CNet
{
}
#ifdef USE_IPV6
CService::CService(const struct in6_addr& ipv6Addr, unsigned short portIn) : CNetAddr(ipv6Addr), port(portIn)
{
}
#endif
CService::CService(const struct sockaddr_in& addr) : CNetAddr(addr.sin_addr), port(ntohs(addr.sin_port))
{
assert(addr.sin_family == AF_INET);
}
#ifdef USE_IPV6
CService::CService(const struct sockaddr_in6 &addr) : CNetAddr(addr.sin6_addr), port(ntohs(addr.sin6_port))
{
assert(addr.sin6_family == AF_INET6);
}
#endif
bool CService::SetSockAddr(const struct sockaddr *paddr)
{
@ -1011,9 +1037,11 @@ bool CService::SetSockAddr(const struct sockaddr *paddr)
case AF_INET:
*this = CService(*(const struct sockaddr_in*)paddr);
return true;
#ifdef USE_IPV6
case AF_INET6:
*this = CService(*(const struct sockaddr_in6*)paddr);
return true;
#endif
default:
return false;
}
@ -1085,6 +1113,7 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
paddrin->sin_port = htons(port);
return true;
}
#ifdef USE_IPV6
if (IsIPv6()) {
if (*addrlen < (socklen_t)sizeof(struct sockaddr_in6))
return false;
@ -1097,6 +1126,7 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const
paddrin6->sin6_port = htons(port);
return true;
}
#endif
return false;
}

View file

@ -74,8 +74,10 @@ class CNetAddr
int GetReachabilityFrom(const CNetAddr *paddrPartner = NULL) const;
void print() const;
#ifdef USE_IPV6
CNetAddr(const struct in6_addr& pipv6Addr);
bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
#endif
friend bool operator==(const CNetAddr& a, const CNetAddr& b);
friend bool operator!=(const CNetAddr& a, const CNetAddr& b);
@ -116,8 +118,10 @@ class CService : public CNetAddr
std::string ToStringIPPort() const;
void print() const;
#ifdef USE_IPV6
CService(const struct in6_addr& ipv6Addr, unsigned short port);
CService(const struct sockaddr_in6& addr);
#endif
IMPLEMENT_SERIALIZE
(

View file

@ -22,7 +22,7 @@ static const char* ppszTypeName[] =
"block",
};
unsigned char pchMessageStart[4] = { 0xfa, 0xe4, 0xaa, 0xf1 };
unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 };
CMessageHeader::CMessageHeader()
{
@ -36,9 +36,7 @@ CMessageHeader::CMessageHeader()
CMessageHeader::CMessageHeader(const char* pszCommand, unsigned int nMessageSizeIn)
{
memcpy(pchMessageStart, ::pchMessageStart, sizeof(pchMessageStart));
size_t command_len = strnlen(pszCommand, COMMAND_SIZE);
memcpy(pchCommand, pszCommand, command_len);
memset(pchCommand + command_len, 0, COMMAND_SIZE - command_len);
strncpy(pchCommand, pszCommand, COMMAND_SIZE);
nMessageSize = nMessageSizeIn;
nChecksum = 0;
}

View file

@ -18,7 +18,7 @@
extern bool fTestNet;
static inline unsigned short GetDefaultPort(const bool testnet = fTestNet)
{
return testnet ? 19246 : 9246;
return testnet ? 18333 : 8333;
}
//
@ -44,7 +44,7 @@ class CMessageHeader
READWRITE(FLATDATA(pchMessageStart));
READWRITE(FLATDATA(pchCommand));
READWRITE(nMessageSize);
if (nVersion >= 1)
if (nVersion >= 209)
READWRITE(nChecksum);
)
@ -60,10 +60,6 @@ class CMessageHeader
enum
{
NODE_NETWORK = (1 << 0),
NODE_BLOOM = (1 << 2),
NODE_WITNESS = (1 << 3),
NODE_COMPACT_FILTERS = (1 << 6),
NODE_NETWORK_LIMITED = (1 << 10),
};
class CAddress : public CService

128
readme.md
View file

@ -1,128 +0,0 @@
# seeder
seeder is a crawler for the LBRY network, which exposes a list
of reliable nodes via a built-in DNS server.
Features:
* regularly revisits known nodes to check their availability
* bans nodes after enough failures, or bad behaviour
* accepts nodes down to v0.3.19 to request new IP addresses from,
but only reports good post-v0.3.24 nodes.
* keeps statistics over (exponential) windows of 2 hours, 8 hours,
1 day and 1 week, to base decisions on.
* very low memory (a few tens of megabytes) and cpu requirements.
* crawlers run in parallel (by default 24 threads simultaneously).
## Build
```
sudo apt-get install build-essential libboost-dev libssl-dev
make
```
## Use
Assumptions:
- lbrycrd will use the domain `seed.example.com` to find peer nodes
- you will be running this seeder on a server at domain `vps.example.com`
### Configure DNS
You will need two DNS records:
type | name | value
-----|------------------| ---------------
NS | seed.example.com | vps.example.com
A | vps.example.com | 1.2.3.4
Test your DNS records
```
$ dig -t NS seed.example.com
;; ANSWER SECTION
seed.example.com. 86400 IN NS vps.example.com.
```
### Disable systemd resolver (Ubuntu 18.04+)
You only need this if you want to run the seeder on port 53 and it's taken by
Ubuntu's resolved. Run the following to turn the resolver off and prevent
it from starting on reboot
```
sudo systemctl stop systemd-resolved.service
sudo systemctl disable systemd-resolved.service
```
### Open firewall port
For example, if using UFW, run `ufw allow 53`. Some VPS providers also
have their own firewall that you'll need to configure.
### Run the seeder
On the system vps.example.com, you can now run dnsseed:
```
./dnsseed -h seed.example.com -n vps.example.com
```
If you want the DNS server to report SOA records, please provide an
e-mail address (with the @ part replaced by .) using `-m`.
### Running as non-root
Typically, you'll need root privileges to listen to port 53 (name service).
One solution is using an iptables rule (Linux only) to redirect it to
a non-privileged port:
```
iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353
```
If properly configured, this will allow you to run dnsseed in userspace, using
the -p 5353 option.
Another solution is allowing a binary to bind to ports < 1024 with setcap (IPv6 access-safe)
```
setcap 'cap_net_bind_service=+ep' /path/to/dnsseed
```
## Debugging
### Server-side
On the server, run `sudo tcpdump port 53`. This will show you all traffic on port 53. As
you send DNS queries, you should see `A` requests come in and response IPs go out.
- no incoming responses: DNS or firewall issues, or DNS request is cached client-side
- no responses: seeder is not running, or running on the wrong port, or broken
- empty responses: requested domain doesn't match configured domain in seeder
You can also look at the output of the running seeder. It looks like this
```
[21-04-12 19:30:49] 28/104 available (104 tried in 994s, 1 new, 30 active), 0 banned; 38 DNS requests, 1 db queries
```
- if # of DNS requests is not going up as you send them, then seeder is not getting your requests
- if DNS requests are increasing but db queries are not, then the -h domain doesn't match
- if seeder didn't find any nodes, then it can't contact the nodes it itself is seeded with
### Client-side
Try `dig +short seed.example.com`. If you get node IPs, your setup is working.
Other things to try
- `dig @1.2.3.4 seed.example.com` to bypass local DNS cache or incorrect DNS records
- `dig +trace seed.example.com` for detailed routing info

View file

@ -60,7 +60,7 @@ class CDataStream;
class CAutoFile;
static const unsigned int MAX_SIZE = 0x02000000;
static const int PROTOCOL_VERSION = 70015;
static const int PROTOCOL_VERSION = 60000;
// Used to bypass the rule against non-const reference to temporary
// where it makes sense with wrappers such as CFlatData or CTxDB

148
uint256.h
View file

@ -22,6 +22,11 @@ typedef unsigned long long uint64;
#define for if (false) ; else for
#endif
inline int Testuint256AdHoc(std::vector<std::string> vArg);
// We have to keep a separate base class without constructors
// so the compiler will let us use it in a union
template<unsigned int BITS>
@ -317,7 +322,7 @@ public:
// hex string to uint
static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
const char* pbegin = psz;
while (phexdigit[(unsigned char)*psz] || *psz == '0')
while (phexdigit[*psz] || *psz == '0')
psz++;
psz--;
unsigned char* p1 = (unsigned char*)pn;
@ -617,4 +622,145 @@ inline const uint256 operator|(const uint256& a, const uint256& b) { return
inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; }
inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; }
inline int Testuint256AdHoc(std::vector<std::string> vArg)
{
uint256 g(0);
printf("%s\n", g.ToString().c_str());
g--; printf("g--\n");
printf("%s\n", g.ToString().c_str());
g--; printf("g--\n");
printf("%s\n", g.ToString().c_str());
g++; printf("g++\n");
printf("%s\n", g.ToString().c_str());
g++; printf("g++\n");
printf("%s\n", g.ToString().c_str());
g++; printf("g++\n");
printf("%s\n", g.ToString().c_str());
g++; printf("g++\n");
printf("%s\n", g.ToString().c_str());
uint256 a(7);
printf("a=7\n");
printf("%s\n", a.ToString().c_str());
uint256 b;
printf("b undefined\n");
printf("%s\n", b.ToString().c_str());
int c = 3;
a = c;
a.pn[3] = 15;
printf("%s\n", a.ToString().c_str());
uint256 k(c);
a = 5;
a.pn[3] = 15;
printf("%s\n", a.ToString().c_str());
b = 1;
b <<= 52;
a |= b;
a ^= 0x500;
printf("a %s\n", a.ToString().c_str());
a = a | b | (uint256)0x1000;
printf("a %s\n", a.ToString().c_str());
printf("b %s\n", b.ToString().c_str());
a = 0xfffffffe;
a.pn[4] = 9;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
uint256 d = a--;
printf("%s\n", d.ToString().c_str());
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
d = a;
printf("%s\n", d.ToString().c_str());
for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n");
uint256 neg = d;
neg = ~neg;
printf("%s\n", neg.ToString().c_str());
uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111");
printf("\n");
printf("%s\n", e.ToString().c_str());
printf("\n");
uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111");
uint256 x2;
printf("%s\n", x1.ToString().c_str());
for (int i = 0; i < 270; i += 4)
{
x2 = x1 << i;
printf("%s\n", x2.ToString().c_str());
}
printf("\n");
printf("%s\n", x1.ToString().c_str());
for (int i = 0; i < 270; i += 4)
{
x2 = x1;
x2 >>= i;
printf("%s\n", x2.ToString().c_str());
}
for (int i = 0; i < 100; i++)
{
uint256 k = (~uint256(0) >> i);
printf("%s\n", k.ToString().c_str());
}
for (int i = 0; i < 100; i++)
{
uint256 k = (~uint256(0) << i);
printf("%s\n", k.ToString().c_str());
}
return (0);
}
#endif

2
util.h
View file

@ -82,7 +82,7 @@ void static inline Sleep(int nMilliSec) {
std::string vstrprintf(const std::string &format, va_list ap);
std::string static inline strprintf(const std::string format, ...) {
std::string static inline strprintf(const std::string &format, ...) {
va_list arg_ptr;
va_start(arg_ptr, format);
std::string ret = vstrprintf(format, arg_ptr);