Skip to content

Commit 8513efe

Browse files
Jeff GarzikJeff Garzik
authored andcommitted
RPC: Require HTTP basic authentication, like bitcoind. Add 'help' RPC.
1 parent d57be38 commit 8513efe

2 files changed

Lines changed: 57 additions & 4 deletions

File tree

README

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ In its current form, node.py does nothing more than listen for new
77
transactions and blocks, perform some simple verification checks on
88
the data, and store them in a database. It does not handle script
99
verification or chain reorg, and is therefore not safe for general use.
10-
See BUGS and TODO files.
10+
See BUGS and TODO files. A simple HTTP server for JSON-RPC API calls
11+
is also included. Send the "help" RPC call for a list of supported
12+
commands.
1113

1214
It might be useful as the base for a P2P monitoring node, or similar
1315
tasks.
@@ -24,8 +26,12 @@ The configuration file is a key=value text file, with the following settings:
2426
# port of network node to connect to (default: 8333)
2527
port=8333
2628

27-
# Incoming (listen) port, for JSON-RPC requests (default: 9332)
28-
port=9332
29+
# JSON-RPC server user, password. Uses HTTP Basic authentication.
30+
rpcuser=XXXX
31+
rpcpass=YYYY
32+
33+
# JSON-RPC server incoming TCP port (default: 9332)
34+
rpcport=9332
2935

3036
# database directory
3137
db=/tmp/chaindb

node.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import cStringIO
1818
import copy
1919
import json
20+
import re
21+
import base64
2022
from Crypto.Hash import SHA256
2123

2224
import ChainDb
@@ -435,6 +437,13 @@ def __init__(self, mempool, chaindb):
435437
self.mempool = mempool
436438
self.chaindb = chaindb
437439

440+
def help(self, params):
441+
l = []
442+
l.append("Available RPC calls:")
443+
l.append("getrawmempool - list mempool contents")
444+
l.append("getinfo - misc. node info")
445+
return (l, None)
446+
438447
def getrawmempool(self, params):
439448
l = []
440449
for k in self.mempool.pool.iterkeys():
@@ -458,11 +467,42 @@ def __init__(self, conn, addr, server, privdata):
458467
def do_GET(self):
459468
self.send_error(501, "Unsupported method (%s)" %self.command)
460469

470+
def check_auth(self):
471+
hdr = self.headers.getheader('authorization')
472+
if hdr is None:
473+
return None
474+
475+
m = re.search('\s*(\w+)\s+(\S+)', hdr)
476+
if m is None or m.group(0) is None:
477+
return None
478+
if m.group(1) != 'Basic':
479+
return None
480+
481+
unpw = base64.b64decode(m.group(2))
482+
if unpw is None:
483+
return None
484+
485+
m = re.search('^([^:]+):(.*)$', unpw)
486+
if m is None:
487+
return None
488+
489+
un = m.group(1)
490+
pw = m.group(2)
491+
if (un != settings['rpcuser'] or
492+
pw != settings['rpcpass']):
493+
return None
494+
495+
return un
496+
461497
def handle_data(self):
462498
if self.path != '/':
463499
self.send_error(404, "Path not found")
464500
return
465-
print "BODY", type(self.body), self.body
501+
username = self.check_auth()
502+
if username is None:
503+
self.send_error(401, "Forbidden")
504+
return
505+
466506
try:
467507
rpcreq = json.loads(self.body)
468508
except ValueError:
@@ -508,6 +548,8 @@ def jsonrpc(self, method, params):
508548
return self.rpc.getrawmempool(params)
509549
elif method == 'getinfo':
510550
return self.rpc.getinfo(params)
551+
elif method == 'help':
552+
return self.rpc.help(params)
511553
return (None, {"code":-32601, "message":"method not found"})
512554

513555
if __name__ == '__main__':
@@ -537,6 +579,11 @@ def jsonrpc(self, method, params):
537579
if 'log' not in settings or (settings['log'] == '-'):
538580
settings['log'] = None
539581

582+
if ('rpcuser' not in settings or
583+
'rpcpass' not in settings):
584+
print "You must set the following in config: rpcuser, rpcpass"
585+
sys.exit(1)
586+
540587
settings['port'] = int(settings['port'])
541588
settings['rpcport'] = int(settings['rpcport'])
542589

0 commit comments

Comments
 (0)