Compare commits

..

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

9 changed files with 109 additions and 168 deletions

66
README Normal file
View file

@ -0,0 +1,66 @@
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-mail address (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.
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

View file

@ -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,12 @@ class CNode {
CAddress me(CService("0.0.0.0"));
BeginMessage("version");
int nBestHeight = GetRequireHeight();
string ver = "/lbry-seeder:0.01/";
string ver = "/bitcoin-seeder:0.01/";
uint8_t fRelayTxs = 0;
vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight << fRelayTxs;
EndMessage();
}
void GotVersion() {
// printf("\n%s: version %i\n", ToString(you).c_str(), nVersion);
if (vAddr) {
@ -112,7 +112,7 @@ class CNode {
vRecv >> strSubVer;
if (nVersion >= 209 && !vRecv.empty())
vRecv >> nStartingHeight;
if (nVersion >= 209) {
BeginMessage("verack");
EndMessage();
@ -124,13 +124,13 @@ 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;
@ -153,10 +153,10 @@ class CNode {
}
return false;
}
return false;
}
bool ProcessMessages() {
if (vRecv.empty()) return false;
do {
@ -172,16 +172,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 +201,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);
@ -261,19 +261,19 @@ 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;
}
@ -314,3 +314,4 @@ int main(void) {
printf("ret=%s ban=%i vAddr.size()=%i\n", ret ? "good" : "bad", ban, (int)vAddr.size());
}
*/

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;
}

6
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; }

View file

@ -36,7 +36,7 @@ public:
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) {}
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"
@ -78,24 +78,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:a:p:d:o:i:k:w:", 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;
@ -418,9 +418,11 @@ extern "C" void* ThreadStats(void*) {
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;
@ -478,10 +480,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;
}

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()
{

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);
)

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