+++ /dev/null
-source "https://rubygems.org"
-
-gem "json"
-gem "webrick"
-gem "zeromqrb"
-gem "sqlite3"
+++ /dev/null
-../Gemfile.lock
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+
+import sqlite3
+from pdns.remotebackend import Handler
+
+
+class BackendHandler(Handler):
+ def __init__(self, options={}):
+ super().__init__(options=options)
+ self.dbpath = options['dbpath']
+ self.db = sqlite3.connect(self.dbpath)
+
+ def get_domain_id(self, name):
+ cur = self.db.execute("SELECT id FROM domains WHERE name = ?", (name,))
+ row = cur.fetchone()
+ if not row:
+ self.result = False
+ raise KeyError
+ return int(row[0])
+
+ def record(self, qname='', qtype='', content='', ttl=1, prio=0, auth=1, domain_id=-1):
+ """Generate one record"""
+ if ttl == -1:
+ ttl = self.ttl
+ if qtype in ('MX', 'SRV'):
+ content = "%d %s" % (prio, content)
+ return {'qtype': qtype, 'qname': qname, 'content': content,
+ 'ttl': ttl, 'auth': auth, 'domain_id': domain_id}
+
+ # ends up here as qname=qname, id=id
+ def getbeforename(self, **kwargs):
+ cur = self.db.execute("SELECT ordername FROM records WHERE ordername < :qname AND domain_id = :id ORDER BY ordername DESC LIMIT 1", kwargs)
+ row = cur.fetchone()
+ if not row:
+ cur = self.db.execute("SELECT ordername FROM records WHERE domain_id = :id ORDER by ordername DESC LIMIT 1", kwargs)
+ row = cur.fetchone()
+ result = row[0]
+ if row[0] is None:
+ result = ''
+ return result
+
+ def getaftername(self, **kwargs):
+ cur = self.db.execute("SELECT ordername FROM records WHERE ordername > :qname AND domain_id = :id ORDER BY ordername LIMIT 1", kwargs)
+ row = cur.fetchone()
+ if row is None:
+ cur = self.db.execute("SELECT ordername FROM records WHERE domain_id = :id ORDER by ordername LIMIT 1", kwargs)
+ row = cur.fetchone()
+ result = row[0]
+ if row[0] is None:
+ result = ''
+ return result
+
+ def do_getbeforeandafternamesabsolute(self, **kwargs):
+ self.result = {
+ 'before': self.getbeforename(**kwargs),
+ 'after': self.getaftername(**kwargs),
+ 'unhashed': kwargs['qname']
+ }
+
+ def do_getbeforeandafternames(self, **kwargs):
+ self.do_getbeforeandafternamesabsolute(**kwargs)
+
+ def do_getdomainkeys(self, name, **kwargs):
+ self.result = []
+ cur = self.db.execute("SELECT cryptokeys.id, flags, active, published, content FROM domains JOIN cryptokeys ON domains.id = cryptokeys.domain_id WHERE domains.name = :name", {'name':name})
+ for row in cur.fetchall():
+ self.result.append({
+ 'id': row[0],
+ 'flags': row[1],
+ 'active': row[2] != 0,
+ 'published': row[3],
+ 'content': row[4]
+ })
+ if len(self.result) == 0:
+ self.result = False
+ self.log.append(self.dbpath)
+
+ def do_lookup(self, qname='', qtype='', domain_id=-1, **kwargs):
+ self.result = []
+ if kwargs.get('zone-id', -1) > 0:
+ domain_id = kwargs['zone-id']
+ if domain_id > -1:
+ if qtype == "ANY":
+ sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND domain_id = :domain_id"
+ else:
+ sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND type = :qtype AND domain_id = :domain_id"
+ else:
+ if qtype == "ANY":
+ sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname"
+ else:
+ sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND type = :qtype"
+ cur = self.db.execute(sql, {'qname': qname, 'qtype': qtype, 'domain_id': domain_id})
+ for row in cur.fetchall():
+ self.result.append(self.record(qname=row[1],qtype=row[2],content=row[3],ttl=row[4],prio=row[5],auth=row[6],domain_id=row[0]))
+
+ def do_getdomaininfo(self, name='', **kwargs):
+ self.result = False
+ cur = self.db.execute("SELECT domain_id,name,content FROM records WHERE name = :name AND type = 'SOA'", {'name': name})
+ for row in cur.fetchall():
+ self.result = {
+ 'zone': row[1],
+ 'serial': int(row[2].split(' ')[2]),
+ 'kind': 'native',
+ 'id': row[0],
+ }
+
+ def do_getalldomains(self):
+ self.result = []
+ cur = self.db.execute("SELECT domain_id,name,content FROM records WHERE name = :name AND type = 'SOA'", {'name': name})
+ for row in cur.fetchall():
+ self.result.append({
+ 'zone': row[1],
+ 'serial': int(row[2].split(' ')[2]),
+ 'kind': 'native',
+ 'id': row[0],
+ })
+
+ def do_list(self, zonename='', domain_id=-1, **kwargs):
+ if domain_id == -1:
+ try:
+ domain_id = self.get_domain_id(zonename)
+ except KeyError:
+ return
+ if domain_id > -1:
+ self.result = []
+ cur = self.db.execute("SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE domain_id = ?", (domain_id,))
+ for row in cur.fetchall():
+ self.result.append(self.record(qname=row[1],qtype=row[2],content=row[3],ttl=row[4],prio=row[5],auth=row[6],domain_id=row[0]))
+
+ def do_adddomainkey(self, name, key, **kwargs):
+ try:
+ domain_id = self.get_domain_id(name)
+ except KeyError:
+ return
+ key['domain_id'] = domain_id
+
+ cur = self.db.execute("INSERT INTO cryptokeys (domain_id, flags, active, published, content) VALUES(:domain_id, :flags, :active, :published, :content)", key)
+ self.db.commit()
+
+ self.result = cur.lastrowid
+ self.log.append(self.dbpath)
+
+ def do_deactivatedomainkey(self, **kwargs):
+ try:
+ domain_id = self.get_domain_id(kwargs['name'])
+ except KeyError:
+ return
+ kwargs['domain_id'] = domain_id
+
+ self.db.execute("UPDATE cryptokeys SET active = 0 WHERE domain_id = :domain_id AND id = :id", kwargs)
+ self.db.commit()
+
+ self.result = True
+
+ def do_activatedomainkey(self, **kwargs):
+ try:
+ domain_id = self.get_domain_id(kwargs['name'])
+ except KeyError:
+ return
+ kwargs['domain_id'] = domain_id
+
+ self.db.execute("UPDATE cryptokeys SET active = 1 WHERE domain_id = :domain_id AND id = :id", kwargs)
+ self.db.commit()
+
+ self.result = True
+
+ def do_unpublishdomainkey(self, **kwargs):
+ try:
+ domain_id = self.get_domain_id(kwargs['name'])
+ except KeyError:
+ return
+ kwargs['domain_id'] = domain_id
+
+ self.db.execute("UPDATE cryptokeys SET published = 0 WHERE domain_id = :domain_id AND id = :id", kwargs)
+ self.db.commit()
+
+ self.result = True
+
+ def do_publishdomainkey(self, **kwargs):
+ try:
+ domain_id = self.get_domain_id(kwargs['name'])
+ except KeyError:
+ return
+ kwargs['domain_id'] = domain_id
+
+ self.db.execute("UPDATE cryptokeys SET published = 1 WHERE domain_id = :domain_id AND id = :id", kwargs)
+ self.db.commit()
+
+ self.result = True
+
+ def do_getalldomainmetadata(self, name, **kwargs):
+ cur = self.db.execute("SELECT kind, content FROM domainmetadata JOIN domains WHERE name = :name", {'name': name})
+ self.result = {}
+ for row in cur.fetchall():
+ if not row[0] in self.result:
+ self.result[row[0]] = list()
+ self.result[row[0]].append(row[1])
+
+ def do_getdomainmetadata(self, name, kind, **kwargs):
+ cur = self.db.execute("SELECT content FROM domainmetadata JOIN domains WHERE name = :name AND kind = :kind", {'name': name, 'kind': kind})
+ self.result = cur.fetchall()
+
+ def do_setdomainmetadata(self, name, kind, value, **kwargs):
+ try:
+ domain_id = self.get_domain_id(name)
+ except KeyError:
+ return
+
+ self.db.execute("DELETE FROM domainmetadata WHERE domain_id = :domain_id AND kind = :kind", {
+ 'domain_id': domain_id,
+ 'kind': kind
+ })
+ if value:
+ self.db.execute("INSERT INTO domainmetadata (domain_id,kind,content) VALUES(:domain_id, :kind, :content)", {
+ 'domain_id': domain_id,
+ 'kind': kind,
+ 'content': content
+ })
+ self.db.commit()
+
+ def do_starttransaction(self, trxid, **kwargs):
+ pass
+
+ def do_committransaction(self, trxid, **kwargs):
+ pass
+
+ def do_directbackendcmd(self, query, **kwargs):
+ self.result = query
+++ /dev/null
-#!/usr/bin/env ruby
-
-require 'rubygems'
-require 'json'
-require 'sqlite3'
-
-def rr(qname, qtype, content, ttl, auth = 1, domain_id = -1)
- {:qname => qname, :qtype => qtype, :content => content, :ttl => ttl.to_i, :auth => auth.to_i, :domain_id => domain_id.to_i}
-end
-
-class Handler
- def initialize(dbpath)
- @dbpath = dbpath
- @db = SQLite3::Database.new @dbpath
- end
-
- def db
- if block_given?
- @db.transaction
- begin
- yield @db
- rescue
- @db.rollback
- return
- end
- @db.commit
- else
- @db
- end
- end
-
- def do_initialize(*args)
- return true, "Test bench initialized"
- end
-
- def getbeforename(qname, id)
- before = db.get_first_value("SELECT ordername FROM records WHERE ordername < ? AND domain_id = ? ORDER BY ordername DESC", qname, id)
- if (before.nil?)
- before = db.get_first_value("SELECT ordername FROM records WHERE domain_id = ? ORDER by ordername DESC LIMIT 1", id)
- end
- before
- end
-
- def getaftername(qname, id)
- after = db.get_first_value("SELECT ordername FROM records WHERE ordername > ? AND domain_id = ? ORDER BY ordername", qname, id)
- if (after.nil?)
- after = db.get_first_value("SELECT ordername FROM records WHERE domain_id = ? ORDER by ordername LIMIT 1", id)
- end
- after
- end
-
-
- def do_getbeforeandafternamesabsolute(args)
- args["qname"] = "" if args["qname"].nil?
- return [{:before => getbeforename(args["qname"],args["id"]), :after => getaftername(args["qname"],args["id"]), :unhashed => args["qname"]}, nil]
- end
-
- def do_getbeforeandafternames(args)
- args["qname"] = "" if args["qname"].nil?
- return [{:before => getbeforename(args["qname"],args["id"]), :after => getaftername(args["qname"],args["id"]), :unhashed => args["qname"]}, nil]
- end
-
- def do_getdomainkeys(args)
- ret = []
- db.execute("SELECT cryptokeys.id,flags,active, published, content FROM domains JOIN cryptokeys ON domains.id = cryptokeys.domain_id WHERE domains.name = ?", [args["name"]]) do |row|
- ret << {:id => row[0].to_i, :flags => row[1].to_i, :active => !(row[2].to_i.zero?), :published => row[3], :content => row[4]}
- end
- return false if ret.empty?
- return [ret,nil]
- end
-
- def do_lookup(args)
- ret = []
- loop do
- begin
- sargs = {}
- if (args["zone-id"].to_i > 0)
- sargs["domain_id"] = args["zone-id"].to_i
- if (args["qtype"] == "ANY")
- sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND domain_id = :domain_id"
- sargs["qname"] = args["qname"]
- else
- sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND type = :qtype AND domain_id = :domain_id"
- sargs["qname"] = args["qname"]
- sargs["qtype"] = args["qtype"]
- end
- else
- if (args["qtype"] == "ANY")
- sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname"
- sargs["qname"] = args["qname"]
- else
- sql = "SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE name = :qname AND type = :qtype"
- sargs["qname"] = args["qname"]
- sargs["qtype"] = args["qtype"]
- end
- end
- db.execute(sql, sargs) do |row|
- if (row[2] == "MX" || row[2] == "SRV")
- ret << rr(row[1], row[2], row[5]+" "+row[3], row[4], row[6], row[0])
- else
- ret << rr(row[1], row[2], row[3], row[4], row[6], row[0])
- end
- end
- rescue Exception => e
- e.backtrace
- return false, [e.message]
- end
- break
- end
- return false unless ret.size > 0
- return [ret,nil]
- end
-
- def do_getdomaininfo(args)
- ret = {}
- sql = "SELECT name,content FROM records WHERE name = :name AND type = 'SOA'"
- db.execute(sql, args) do |row|
- ret[:zone] = row[0]
- ret[:serial] = row[1].split(' ')[2].to_i
- ret[:kind] = "native"
- end
- return [ret,nil] if ret.has_key?(:zone)
- return false
- end
-
- def do_list(args)
- target = args["zonename"]
- ret = []
- loop do
- begin
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", target)
- return false if d_id.nil?
- db.execute("SELECT domain_id,name,type,content,ttl,prio,auth FROM records WHERE domain_id = ?", d_id) do |row|
- if (row[2] == "MX" || row[2] == "SRV")
- ret << rr(row[1], row[2], row[5]+" "+row[3], row[4], row[6], row[0])
- else
- ret << rr(row[1], row[2], row[3], row[4], row[6], row[0])
- end
- end
- rescue Exception => e
- e.backtrace
- return false, [e.message]
- end
- break
- end
- return false unless ret.size > 0
- return [ret,nil]
- end
-
- def do_adddomainkey(args)
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
- return false if d_id.nil?
- sql = "INSERT INTO cryptokeys (domain_id, flags, active, published, content) VALUES(?,?,?,?,?)"
- active = args["key"]["active"]
- if (active)
- active = 1
- else
- active = 0
- end
- published = args["key"]["published"]
- if (published)
- published = 1
- else
- published = 0
- end
- db do |tx|
- tx.execute(sql, [d_id, args["key"]["flags"].to_i, active, published, args["key"]["content"]])
- end
- return db.get_first_value("SELECT last_insert_rowid()").to_i
- end
-
- def do_deactivatedomainkey(args)
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
- return false if d_id.nil?
- db do |tx|
- tx.execute("UPDATE cryptokeys SET active = 0 WHERE domain_id = ? AND id = ?", [d_id, args["id"]])
- end
- return true
- end
-
- def do_activatedomainkey(args)
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
- return false if d_id.nil?
- db do |tx|
- db.execute("UPDATE cryptokeys SET active = 1 WHERE domain_id = ? AND id = ?", [d_id, args["id"]])
- end
- return true
- end
-
- def do_unpublishdomainkey(args)
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
- return false if d_id.nil?
- db do |tx|
- tx.execute("UPDATE cryptokeys SET published = 0 WHERE domain_id = ? AND id = ?", [d_id, args["id"]])
- end
- return true
- end
-
- def do_publishdomainkey(args)
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
- return false if d_id.nil?
- db do |tx|
- db.execute("UPDATE cryptokeys SET published = 1 WHERE domain_id = ? AND id = ?", [d_id, args["id"]])
- end
- return true
- end
-
- def do_getdomainmetadata(args)
- ret = []
- sql = "SELECT content FROM domainmetadata JOIN domains WHERE name = :name AND kind = :kind"
- sargs = {:name => args["name"], :kind => args["kind"]}
- db.execute(sql,sargs) do |row|
- ret << row[0]
- end
- return false unless ret.size > 0
- return [ret,nil]
- end
-
- def do_setdomainmetadata(args)
- d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
- return false if d_id.nil?
- db do |tx|
- sql = "DELETE FROM domainmetadata WHERE domain_id = ? AND kind = ?"
- tx.execute(sql, [d_id, args["kind"]])
- unless args["value"].nil?
- sql = "INSERT INTO domainmetadata (domain_id,kind,content) VALUES(?,?,?)"
- args["value"].each do |value|
- STDERR.puts"Executing INSERT INTO domainmetadata (domain_id,kind,content) VALUES(#{d_id}, #{args["kind"]}, #{value})"
- tx.execute(sql,[d_id, args["kind"], value])
- end
- end
- end
- return true
- end
-
- def do_directbackendcmd(args)
- return [args["query"]]
- end
-end
--- /dev/null
+#!/usr/bin/env python
+
+import http.server
+import json
+import re
+
+from urllib.parse import parse_qsl, urlparse, unquote
+
+class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
+ def __init__(self, *args, **kwargs):
+ self.handler = kwargs['handler']
+ super().__init__(*args)
+
+ def url_to_args(self):
+ url = urlparse(self.path)
+ parts = list(map(lambda part: unquote(part), url.path.split("/")))
+ parts.pop(0)
+ self.method = None
+
+ if parts.pop(0) != 'dns':
+ return
+
+ self.method = parts.pop(0).lower()
+ self.args = {}
+
+ if self.method == 'lookup':
+ self.args['qname'] = parts.pop(0)
+ self.args['qtype'] = parts.pop(0)
+ elif self.method == 'list':
+ self.args['id'] = int(parts.pop(0))
+ self.args['zonename'] = parts.pop(0)
+ elif self.method in ('getbeforeandafternamesabsolute', 'getbeforeandafternames'):
+ self.args['id'] = int(parts.pop(0))
+ self.args['qname'] = parts.pop(0)
+ elif self.method in ('getdomainmetadata', 'setdomainmetadata'):
+ self.args['name'] = parts.pop(0)
+ self.args['kind'] = parts.pop(0)
+ elif self.method == 'getdomainkeys':
+ self.args['name'] = parts.pop(0)
+ elif self.method in ('removedomainkey', 'activatedomainkey', 'deactivatedomainkey'):
+ self.args['id'] = int(parts.pop(0))
+ self.args['name'] = parts.pop(0)
+ elif self.method in ('adddomainkey', 'gettsigkey', 'getdomaininfo', 'settsigkey', 'deletetsigkey', 'getalldomainmetadata'):
+ self.args['name'] = parts.pop(0)
+ elif self.method == 'setnotified':
+ self.args['id'] = int(parts.pop(0))
+ elif self.method == 'feedents':
+ self.args['id'] = int(parts.pop(0))
+ self.args['trxid'] = int(parts.pop(0))
+ elif self.method == 'ismaster':
+ self.args['name'] = parts.pop(0)
+ self.args['ip'] = parts.pop(0)
+ elif self.method in ('supermasterbackend', 'createslavedomain'):
+ self.args['ip'] = parts.pop(0)
+ self.args['domain'] = parts.pop(0)
+ elif self.method in ('feedents3', 'starttransaction'):
+ self.args['id'] = int(parts.pop(0))
+ self.args['domain'] = parts.pop(0)
+ self.args['trxid'] = int(parts.pop(0))
+ elif self.method in ('feedrecord', 'committransaction', 'aborttransaction'):
+ self.args['trxid'] = int(parts.pop(0))
+ elif self.method == 'replacerrset':
+ self.args['id'] = int(parts.pop(0))
+ self.args['qname'] = parts.pop(0)
+ self.args['qtype'] = parts.pop(0)
+ assert len(parts) == 0, parts
+
+ self.parse_qsl(url.query)
+
+ def parse_qsl(self, qs):
+ res = {}
+ for key, value in parse_qsl(qs):
+ m = re.match(r"^(.*)\[(.*)\]\[(.*)\]", key)
+ if m:
+ k1 = m.group(1)
+ k2 = int(m.group(2))
+ k3 = m.group(3)
+ if k1 not in res:
+ res[k1] = list({})
+ while len(res[k1]) <= k2:
+ res[k1].append({})
+ res[k1][k2][k3] = value
+ else:
+ m = re.match(r"^(.*)\[(.*)\]", key)
+ if m:
+ k1 = m.group(1)
+ k2 = m.group(2)
+ if k1 not in res:
+ if k2 == '':
+ res[k1] = list()
+ else:
+ res[k1] = {}
+ if k2 == '':
+ res[k1].append(value)
+ else:
+ res[k1][k2] = value
+ else:
+ res[key] = value
+ self.args = self.args | res
+
+ def do_GET(self):
+ if self.path == '/ping':
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write("pong".encode())
+ return
+ self.do_POST()
+
+ def do_DELETE(self):
+ self.do_POST()
+
+ def do_PATCH(self):
+ self.do_POST()
+
+ def do_PUT(self):
+ self.do_POST()
+
+ def do_POST(self):
+ self.url_to_args()
+
+ if not self.method:
+ self.send_error(404)
+ return
+
+ try:
+ length = 0
+ if 'content-length' in self.headers:
+ length = int(self.headers.get('content-length'))
+ if length > 0:
+ qs = self.rfile.read(length).decode()
+ self.parse_qsl(qs)
+
+ if self.method == "adddomainkey":
+ self.args['key'] = {
+ 'flags': self.args['flags'],
+ 'active': self.args['active'],
+ 'published': self.args['published'],
+ 'content': self.args['content']
+ }
+ del self.args['flags']
+ del self.args['active']
+ del self.args['published']
+ del self.args['content']
+
+ if 'serial' in self.args:
+ self.args['serial'] = int(self.args['serial'])
+
+ method = "do_%s" % self.method
+
+ self.handler.result = False
+ self.handler.log = []
+
+ if callable(getattr(self.handler, method, None)):
+ getattr(self.handler, method)(**self.args)
+ result = json.dumps({'result':self.handler.result,'log':self.handler.log}).encode()
+ self.send_response(200)
+ self.send_header("content-type", "text/javascript");
+ self.send_header("content-length", len(result))
+ self.end_headers()
+ self.wfile.write(result)
+ else:
+ self.send_error(404, message=json.dumps({'error': 'No such method'}))
+ except BrokenPipeError as e2:
+ raise e2
+ except Exception as e:
+ raise e
+ self.log_error("Exception handling request: %r", e)
+ self.send_error(400, message=str(e))
+++ /dev/null
-require 'json'
-require 'thread'
-
-class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
- def initialize(server, dnsbackend)
- @dnsbackend = dnsbackend
- @semaphore = Mutex.new
- unless defined? @@f
- @@f = File.open("/tmp/remotebackend.txt.#{$$}","a")
- @@f.sync
- end
- @dnsbackend.do_initialize({})
- end
-
- def parse_arrays(params)
- newparams = {}
- params.each do |key,val|
- if key=~/^(.*)\[(.*)\]\[(.*)\]/
- newparams[$1] = {} unless newparams.has_key? $1
- newparams[$1][$2] = {} unless newparams[$1].has_key? $2
- newparams[$1][$2][$3] = val
- params.delete key
- elsif key=~/^(.*)\[(.*)\]/
- if $2 == ""
- newparams[$1] = [] unless newparams.has_key? $1
- newparams[$1] << val
- else
- newparams[$1] = {} unless newparams.has_key? $1
- newparams[$1][$2] = val
- end
- params.delete key
- end
- end
- params.merge newparams
- end
-
- def parse_url(url)
- url = url.split('/')
- method = url.shift.downcase
-
- # do some determining based on method names
- args = case method
- when "lookup"
- {
- "qname" => url.shift,
- "qtype" => url.shift,
- }
- when "list"
- {
- "id" => url.shift,
- "zonename" => url.shift
- }
- when "getbeforeandafternamesabsolute", "getbeforeandafternames"
- {
- "id" => url.shift.to_i,
- "qname" => url.shift
- }
- when "getdomainmetadata", "setdomainmetadata", "getdomainkeys"
- {
- "name" => url.shift,
- "kind" => url.shift
- }
- when "removedomainkey", "activatedomainkey", "deactivatedomainkey"
- {
- "id" => url.shift,
- "name" => url.shift
- }
- when "adddomainkey", "gettsigkey", "getdomaininfo"
- {
- "name" => url.shift
- }
- else
- {
- }
- end
-
- [method, args]
- end
-
- def do_GET(req,res)
- req.continue
-
- tmp = req.path[/dns\/(.*)/,1]
- return 400, "Bad request" if (tmp.nil?)
-
- method, args = parse_url(tmp.force_encoding("UTF-8"))
-
- method = "do_#{method}"
-
- # get more arguments
- req.each do |k,v|
- attr = k[/x-remotebackend-(.*)/i,1]
- if attr
- args[attr.downcase] = v.force_encoding("UTF-8")
- end
- end
-
- args = args.merge req.query
-
- if method == "do_adddomainkey"
- args["key"] = {
- "flags" => args.delete("flags").to_i,
- "active" => args.delete("active").to_i,
- "published" => args.delete("published").to_i,
- "content" => args.delete("content")
- }
- end
-
- args = parse_arrays args
-
- @@f.puts "#{Time.now.to_f} [http]: #{({:method=>method,:parameters=>args}).to_json}"
-
- @semaphore.synchronize do
- if @dnsbackend.respond_to?(method.to_sym)
- result, log = @dnsbackend.send(method.to_sym, args)
- body = {:result => result, :log => log}
- res.status = 200
- res["Content-Type"] = "application/javascript; charset=utf-8"
- res.body = body.to_json
- else
- res.status = 404
- res["Content-Type"] = "application/javascript; charset=utf-8"
- res.body = ({:result => false, :log => ["Method not found"]}).to_json
- end
- @@f.puts "#{Time.now.to_f} [http]: #{res.body}"
- end
- end
-
- def do_DELETE(req,res)
- do_GET(req,res)
- end
-
- def do_POST(req,res)
- do_GET(req,res)
- end
-
- def do_PATCH(req,res)
- do_GET(req,res)
- end
-
- def do_PUT(req,res)
- do_GET(req,res)
- end
-end
--- /dev/null
+#!/usr/bin/env python
+
+import http.server
+from backend import BackendHandler
+from dnsbackend import DNSBackendHandler
+import os
+
+class DNSBackendServer(http.server.HTTPServer):
+ def __init__(self, *args, **kwargs):
+ path = os.path.dirname(os.path.realpath(__file__))
+ self.handler = BackendHandler(options={'dbpath': os.path.join(path, 'remote.sqlite3')})
+ super().__init__(*args, **kwargs)
+
+ def finish_request(self, request, client_address):
+ """Finish one request b instantiating RequestHandlerClass."""
+ h = self.RequestHandlerClass(request, client_address, self, handler=self.handler)
+
+def main():
+ server = DNSBackendServer(('', 62434), DNSBackendHandler)
+ try:
+ server.serve_forever()
+ except KeyboardInterrupt:
+ pass
+
+main()
+++ /dev/null
-#!/usr/bin/env ruby
-require "rubygems"
-require 'bundler/setup'
-require "webrick"
-$:.unshift File.dirname(__FILE__)
-require "dnsbackend"
-require "backend"
-require "pathname"
-
-server = WEBrick::HTTPServer.new(
- :Port=>62434,
- :BindAddress=>"localhost",
-# Logger: WEBrick::Log.new("remotebackend-server.log"),
- :AccessLog=>[ [ File.open("remotebackend-access.log", "w"), WEBrick::AccessLog::COMBINED_LOG_FORMAT ] ]
-)
-
-be = Handler.new(Pathname.new(File.join(File.dirname(__FILE__),"remote.sqlite3")).realpath.to_s)
-server.mount "/dns", DNSBackendHandler, be
-server.mount_proc("/ping"){ |req,resp| resp.body = "pong" }
-
-trap('INT') { server.stop }
-trap('TERM') { server.stop }
-
-server.start
--- /dev/null
+#!/usr/bin/env python
+
+from pdns.remotebackend import PipeConnector
+from backend import BackendHandler
+import os
+
+def main():
+ path = os.path.dirname(os.path.realpath(__file__))
+ connector = PipeConnector(BackendHandler, options={'dbpath': os.path.join(path, 'remote.sqlite3'), 'rawlog':'/tmp/raw.json'})
+ connector.run()
+
+main()
+++ /dev/null
-#!/usr/bin/env ruby
-require "rubygems"
-require 'bundler/setup'
-require 'json'
-$:.unshift File.dirname(__FILE__)
-require "backend"
-
-h = Handler.new(Pathname.new(File.join(File.dirname(__FILE__),"remote.sqlite3")).realpath.to_s)
-
-f = File.open "/tmp/remotebackend.txt.#{$$}","a"
-f.sync = true
-
-STDOUT.sync = true
-begin
- STDIN.each_line do |line|
- # expect json
- input = {}
- line = line.strip
- f.puts "#{Time.now.to_f}: [pipe] #{line}"
- next if line.empty?
- begin
- input = JSON.parse(line)
- next unless input and input["method"]
- method = "do_#{input["method"].downcase}"
- args = input["parameters"]
-
- if h.respond_to?(method.to_sym) == false
- res = false
- elsif args.size > 0
- res, log = h.send(method,args)
- else
- res, log = h.send(method)
- end
- puts ({:result => res, :log => log}).to_json
- f.puts "#{Time.now.to_f} [pipe]: #{({:result => res, :log => log}).to_json}"
- rescue JSON::ParserError
- puts ({:result => false, :log => "Cannot parse input #{line}"}).to_json
- f.puts "#{Time.now.to_f} [pipe]: #{({:result => false, :log => "Cannot parse input #{line}"}).to_json}"
- next
- end
- end
-rescue SystemExit, Interrupt
-end
--- /dev/null
+#!/usr/bin/env python
+
+from pdns.remotebackend import PipeConnector
+from backend import BackendHandler
+import os
+
+def main():
+ path = os.path.dirname(os.path.realpath(__file__))
+ connector = PipeConnector(BackendHandler, options={'dbpath': os.path.join(path, 'remote.sqlite3'), 'rawlog':'/tmp/raw.json'})
+ connector.run()
+
+main()
+++ /dev/null
-#!/usr/bin/env ruby
-require "rubygems"
-require 'bundler/setup'
-require 'json'
-$:.unshift File.dirname(__FILE__)
-require "backend"
-
-h = Handler.new(Pathname.new(File.join(File.dirname(__FILE__),"remote.sqlite3")).realpath.to_s)
-
-f = File.open "/tmp/remotebackend.txt.#{$$}","a"
-f.sync = true
-
-STDOUT.sync = true
-begin
- STDIN.each_line do |line|
- # expect json
- input = {}
- line = line.strip
- f.puts "#{Time.now.to_f}: [unix] #{line}"
- next if line.empty?
- begin
- input = JSON.parse(line)
- next unless input and input["method"]
- method = "do_#{input["method"].downcase}"
- args = input["parameters"]
-
- if h.respond_to?(method.to_sym) == false
- res = false
- elsif args.size > 0
- res, log = h.send(method,args)
- else
- res, log = h.send(method)
- end
- puts ({:result => res, :log => log}).to_json
- f.puts "#{Time.now.to_f} [unix]: #{({:result => res, :log => log}).to_json}"
- rescue JSON::ParserError
- f.puts "#{Time.now.to_f} [unix]: #{({:result => false, :log => "Cannot parse input #{line}"}).to_json}"
- next
- end
- end
-rescue SystemExit, Interrupt
-end
--- /dev/null
+#!/usr/bin/env python
+
+import zmq
+import json
+import os
+from urllib.parse import parse_qs, urlparse
+from backend import BackendHandler
+
+def run(socket, handler):
+ while True:
+ message = socket.recv()
+ try:
+ message = json.loads(message.decode().strip())
+ method = "do_%s" % message['method'].lower()
+ args = message['parameters']
+ handler.result = False
+ handler.log = []
+ if callable(getattr(handler, method, None)):
+ getattr(handler, method)(**args)
+ result = json.dumps({'result': handler.result,'log': handler.log})
+ socket.send(result.encode())
+ except KeyboardInterrupt as e3:
+ return
+ except BrokenPipeError as e2:
+ raise e2
+ except Exception as e:
+ print(e)
+ socket.send(json.dumps({'result':False}).encode())
+
+
+def main():
+ path = os.path.dirname(os.path.realpath(__file__))
+ context = zmq.Context()
+ socket = context.socket(zmq.REP)
+ socket.bind("ipc:///tmp/pdns.0")
+ handler = BackendHandler(options={'dbpath': os.path.join(path, 'remote.sqlite3')})
+
+ try:
+ run(socket, handler)
+ except KeyboardInterrupt as e:
+ pass
+
+ os.unlink("/tmp/remotebackend.0")
+
+main()
+++ /dev/null
-#!/usr/bin/env ruby
-require "rubygems"
-require 'bundler/setup'
-require 'json'
-require 'zero_mq'
-$:.unshift File.dirname(__FILE__)
-require "backend"
-
-h = Handler.new(Pathname.new(File.join(File.dirname(__FILE__),"remote.sqlite3")).realpath.to_s)
-
-f = File.open "/tmp/remotebackend.txt.#{$$}","a"
-f.sync = true
-
-begin
- context = ZeroMQ::Context.new
- socket = context.socket ZMQ::REP
- socket.bind("ipc:///tmp/pdns.0") or raise "Cannot bind to IPC socket"
-
- while(true) do
- line = ""
- rc = socket.recv_string line
- # expect json
- input = {}
- line = line.strip
- f.puts "#{Time.now.to_f}: [zmq] #{line}"
- next if line.empty?
- begin
- input = JSON.parse(line)
- next unless input and input["method"]
- method = "do_#{input["method"].downcase}"
- args = input["parameters"] || []
-
- if h.respond_to?(method.to_sym) == false
- res = false
- elsif args.size > 0
- res, log = h.send(method,args)
- else
- res, log = h.send(method)
- end
- socket.send_string ({:result => res, :log => log}).to_json, 0
- f.puts "#{Time.now.to_f} [zmq]: #{({:result => res, :log => log}).to_json}"
- rescue JSON::ParserError
- socket.send_string ({:result => false, :log => "Cannot parse input #{line}"}).to_json
- f.puts "#{Time.now.to_f} [zmq]: #{({:result => false, :log => "Cannot parse input #{line}"}).to_json}"
- next
- end
- end
-rescue SystemExit, Interrupt
-end
narrow=$(echo $context | cut -d- -f 4)
testsdir=../modules/remotebackend/regression-tests/
+ if [ ! -d $testsdir/../venv ]; then
+ python3 -m venv $testsdir/../venv
+ source $testsdir/../venv/bin/activate
+ pip install -U wheel
+ pip install -r $testsdir/../requirements.txt
+ else
+ source $testsdir/../venv/bin/activate
+ fi
+
# cleanup unbound-host.conf to avoid failures
rm -f unbound-host.conf
connstr="http:url=http://localhost:62434/dns"
rm -f remotebackend-server.log
rm -f remotebackend-access.log
- $testsdir/http-backend.rb &
+ $testsdir/http-backend.py &
echo $! > pdns-remotebackend.pid
set +e
# make sure it runs before continuing
;;
zeromq)
connstr="zeromq:endpoint=ipc:///tmp/pdns.0"
- $testsdir/zeromq-backend.rb &
+ $testsdir/zeromq-backend.py &
echo $! > pdns-remotebackend.pid
;;
unix)
connstr="unix:path=$testsdir/remote.socket"
rm -f $testsdir/remote.socket
- socat unix-listen:$testsdir/remote.socket,fork exec:$testsdir/unix-backend.rb &
+ socat unix-listen:$testsdir/remote.socket,fork exec:$testsdir/unix-backend.py &
echo $! > pdns-remotebackend.pid
;;
pipe)
- connstr="pipe:command=$testsdir/pipe-backend.rb"
+ connstr="pipe:command=$testsdir/pipe-backend.py"
;;
*)
echo "Invalid usage"