Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lib/dns/records.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,35 @@ const types = {
ADDR: 21 // TXT
};

/**
* DNS RR Types by Handshake
* record type.
* @constant
*/

const rrTypesByType = {
[types.INET4]: wire.types.A,
[types.INET6]: wire.types.AAAA,
[types.ONION]: wire.types.TXT,
[types.ONIONNG]: wire.types.TXT,
[types.NAME]: wire.types.CNAME,
[types.CANONICAL]: wire.types.CNAME,
[types.DELEGATE]: wire.types.DNAME,
[types.NS]: wire.types.NS,
[types.SERVICE]: wire.types.SRV,
[types.URI]: wire.types.URI,
[types.EMAIL]: wire.types.RP,
[types.TEXT]: wire.types.TXT,
[types.LOCATION]: wire.types.LOC,
[types.MAGNET]: wire.types.URI,
[types.DS]: wire.types.DS,
[types.TLS]: wire.types.TLSA,
[types.SMIME]: wire.types.SMIMEA,
[types.SSH]: wire.types.SSHFP,
[types.PGP]: wire.types.OPENPGPKEY,
[types.ADDR]: wire.types.URI
};

/**
* Target
* @extends {Struct}
Expand Down Expand Up @@ -1311,6 +1340,7 @@ function isSingle(label) {
*/

records.types = types;
records.rrTypesByType = rrTypesByType;

records.Target = Target;
records.Service = Service;
Expand Down
57 changes: 57 additions & 0 deletions lib/node/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
const assert = require('bsert');
const path = require('path');
const {Server} = require('bweb');
const bns = require('bns');
const Validator = require('bval');
const {base58} = require('bstring');
const {BloomFilter} = require('bfilter');
Expand All @@ -19,6 +20,9 @@ const util = require('../utils/util');
const TX = require('../primitives/tx');
const Claim = require('../primitives/claim');
const Address = require('../primitives/address');
const Records = require('../dns/records');
const Resource = require('../dns/resource');
const NameState = require('../covenants/namestate');
const Network = require('../protocol/network');
const pkg = require('../pkg');

Expand Down Expand Up @@ -404,6 +408,59 @@ class HTTP extends Server {
res.json(200, { success: true });
});

this.get('/zonefile', async (req, res) => {
const valid = Validator.fromRequest(req);

// TODO: add param for zone name, default to '.'
// DNSSEC on/off
// height param (figure out reversing tree)
// goal is that client can call response.join('\n')
// and then write that to disk and have a valid
// zone file

const txn = this.chain.db.txn;
const iter = txn.iterator();

res.setType('json');
res.write('[');

// TODO: generate header of zonefile

while (await iter.next()) {
const {key, value} = iter;
const ns = NameState.decode(value);

const resource = Resource.decode(ns.data);
const fqdn = bns.util.fqdn(ns.name.toString('ascii'));

const rrTypesByType = Records.rrTypesByType;
const rrTypes = new Set(Object.values(rrTypesByType));

let seen = 0;
for (const rrtype of rrTypes) {
const msg = resource.toDNS(fqdn, rrtype);

// TODO
// potentially ignore authority field
// it is causing a lot of duplicates
// or handle it as special case
for (const rr of msg.records()) {
const entry = rr.toString();

if (seen === 0)
res.write(`"${entry}"`);
else
res.write(`,"${entry}"`);

seen++;
}
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log number of records seen here

res.write(']');
res.end();
});

// Estimate fee
this.get('/fee', async (req, res) => {
const valid = Validator.fromRequest(req);
Expand Down
44 changes: 44 additions & 0 deletions lib/node/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

const assert = require('bsert');
const bweb = require('bweb');
const bns = require('bns');
const {Lock} = require('bmutex');
const IP = require('binet');
const Validator = require('bval');
Expand Down Expand Up @@ -36,6 +37,7 @@ const consensus = require('../protocol/consensus');
const pkg = require('../pkg');
const rules = require('../covenants/rules');
const Resource = require('../dns/resource');
const Records = require('../dns/records');
const NameState = require('../covenants/namestate');
const AirdropProof = require('../primitives/airdropproof');
const {EXP} = consensus;
Expand Down Expand Up @@ -243,6 +245,8 @@ class RPC extends RPCBase {
this.add('sendrawclaim', this.sendRawClaim);
this.add('sendrawairdrop', this.sendRawAirdrop);

this.add('dumpzone', this.dumpzone);

// Compat
// this.add('getnameinfo', this.getNameInfo);
// this.add('getnameresource', this.getNameResource);
Expand Down Expand Up @@ -2314,6 +2318,46 @@ class RPC extends RPCBase {
return items;
}

async dumpzone(args, help) {
if (help || args.length !== 0)
throw new RPCError(errs.MISC_ERROR, 'dumpzone');

// TODO: clean some of this up, dead code
const network = this.network;
const height = this.chain.height;
const txn = this.chain.db.txn;
const items = [];

// TODO:
// open up a stream to a file
// write the file to disk

const iter = txn.iterator();

while (await iter.next()) {
const {key, value} = iter;
const ns = NameState.decode(value);

const resource = Resource.decode(ns.data);
const fqdn = bns.util.fqdn(ns.name.toString('ascii'));

const rrTypesByType = Records.rrTypesByType;

for (const [hs, rrtype] of Object.entries(rrTypesByType)) {
const dns = resource.toDNS(fqdn, rrtype);

for (const rr of dns.records()) {
const entry = rr.toString();
console.log(entry);
}
}
}

// see backup wallet for example
// of return type
return items;
}

async getNameInfo(args, help) {
if (help || args.length !== 1)
throw new RPCError(errs.MISC_ERROR, 'getnameinfo "name"');
Expand Down
135 changes: 135 additions & 0 deletions test/node-http-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
*
*/

'use strict';

const {NodeClient,WalletClient} = require('hs-client');
const Network = require('../lib/protocol/network');
const FullNode = require('../lib/node/fullnode');
const common = require('./util/common');
const rules = require('../lib/covenants/rules');

const network = Network.get('regtest');

const {
treeInterval,
biddingPeriod,
revealPeriod
} = network.names;

const ports = {
p2p: 14331,
node: 14332,
wallet: 14333
};

const node = new FullNode({
network: network.type,
apiKey: 'bar',
walletAuth: true,
memory: true,
port: ports.p2p,
httpPort: ports.node,
workers: true,
plugins: [require('../lib/wallet/plugin')],
env: {
'HSD_WALLET_HTTP_PORT': ports.wallet.toString()
}
});

const nclient = new NodeClient({
port: ports.node,
apiKey: 'bar'
});

const wclient = new WalletClient({
port: ports.wallet,
apiKey: 'bar'
});

const wallet = wclient.wallet('primary');

let coinbase, name;

describe('Node RPC Methods', function() {
this.timeout(20000);

before(async () => {
await node.open();
await nclient.open();
await wclient.open();

// mine a bunch of blocks
const info = await wallet.createAddress('default');
coinbase = info.address;

await mineBlocks(10, coinbase);

name = rules.grindName(3, 1, network);
await doAuction(name);
});

after(async () => {
await nclient.close();
await wclient.close();
await node.close();
});

it('should get zonefile', async () => {
const res = await nclient.get('/zonefile')
console.log(res)
});
});

async function doAuction(name) {
await wallet.client.post(`/wallet/${wallet.id}/open`, {
name: name
});

await mineBlocks(treeInterval + 1, coinbase);

await wallet.client.post(`/wallet/${wallet.id}/bid`, {
name: name,
bid: 1000,
lockup: 2000
});

await mineBlocks(biddingPeriod + 1, coinbase);

await wallet.client.post(`/wallet/${wallet.id}/reveal`, {
name: name
});

await mineBlocks(revealPeriod + 1, coinbase);

await wallet.client.post(`/wallet/${wallet.id}/update`, {
name: name,
data: {
hosts: ['192.168.0.91'],
ns: [
'ns1.cloudflare.com@1.2.3.4',
'ns2.cloudflare.com@1.2.4.4'
],
ttl: 64
}
});

await mineBlocks(treeInterval, coinbase);
}

/**
* Mine blocks and take into
* account race conditions
*/

async function mineBlocks(count, address) {
for (let i = 0; i < count; i++) {
const obj = { complete: false };
node.once('block', () => {
obj.complete = true;
});
await nclient.execute('generatetoaddress', [1, address]);
await common.forValue(obj, 'complete', true);
}
}