+++ /dev/null
-source "https://rubygems.org"
-
-gem "json"
-gem "webrick"
-gem "zeromqrb"
-gem "sqlite3"
+++ /dev/null
-GEM
- remote: https://rubygems.org/
- specs:
- ffi (1.15.3)
- ffi-rzmq (2.0.7)
- ffi-rzmq-core (>= 1.0.7)
- ffi-rzmq-core (1.0.7)
- ffi
- json (2.5.1)
- sqlite3 (1.4.2)
- webrick (1.7.0)
- zeromqrb (0.1.3)
- ffi-rzmq
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- json
- sqlite3
- webrick
- zeromqrb
-
-BUNDLED WITH
- 2.1.4
OBJECTFILES \
OBJECTLIBS \
testrunner.sh \
- unittest_http.rb \
- unittest_json.rb \
- unittest_pipe.rb \
- unittest_zeromq.rb \
- unittest_post.rb \
- unittest.rb \
- Gemfile \
- Gemfile.lock
+ unittest_http.py \
+ unittest_json.py \
+ unittest_pipe.py \
+ unittest_zeromq.py \
+ unittest_post.py \
+ pdns_unittest.py \
+ requirements.txt
EXTRA_PROGRAMS = \
remotebackend_pipe.test \
BOOST_TEST_LOG_LEVEL=message; \
export BOOST_TEST_LOG_LEVEL; \
REMOTEBACKEND_ZEROMQ=$(REMOTEBACKEND_ZEROMQ); \
- export REMOTEBACKEND_ZEROMQ;
+ export REMOTEBACKEND_ZEROMQ; \
+ abs_srcdir=$(abs_srcdir)
+ export abs_srcdir;
TEST_EXTENSIONS = .test
+++ /dev/null
-#!/usr/bin/ruby
-
-require 'rubygems'
-require 'bundler/setup'
-require 'json'
-
-## this is an example stub for remote backend
-## to add more methods, just write
-## def do_<methodname>(args)
-## end
-## look at the existing methods to find out
-## how to customize this.
-
-## WARNING: this contains some code that
-## should never be used in production, but
-## is provided to give a more comprehensive
-## example code.
-
-## Code provided only as example, not suitable
-## for production.
-
-## Usage:
-## launch=remote
-## remote-dnssec=yes
-## remote-connection-string=pipe:command=/path/to/example.rb,timeout=2000
-
-class RequestHandler
-
-public
- def initialize
- @_log = []
- @initialized = false
- @default_ttl = 3600
- end
-
-protected
-
- ## YOUR METHODS GO AFTER THIS LINE
-
- def do_initialize(args)
- if @initialized
- raise "Cannot reinitialize"
- end
- log "Example backend v1.0 starting"
- @initialized = true
- true
- end
-
- ## used to tell that we do NSEC3 NARROW
- def do_getdomainmetadata(args)
- if args["name"] == "example.com"
- if args["kind"] == "NSEC3NARROW"
- return "1"
- elsif args["kind"] == "NSEC3PARAM"
- return "1 1 1 fe"
- end
- end
- false
- end
-
- ## returns keys, do not use in production
- def do_getdomainkeys(args)
- if args["name"] == "example.com"
- return [
- {
- "id" => 1,
- "flags" => 257,
- "active" => true,
- "published" => true,
- "content" => "Private-key-format: v1.2
-Algorithm: 8 (RSASHA256)
-Modulus: ovvzf1fHdptdXsBrBLSqmGqdEKwR2B9st/KBgh8xQKoQzTGUG00CsPjF/J59IBU+EU/IIInMn0MxLLTyUKa2DJUkR6i7UKif5jKX1c7yvWzrFKLGOHjugUX2++r+o789biUte1qpWp3Kc2RYL18oPco4zpo6JcsPmhOK3aUCDJXmuWgHl1KudCQIiPkISArXVn4oOp+skQq+mUBl1Pysc4D+6sl77ERR2fW6xJ4ZRPOIKr445RJJmKgoMG8yRrR3it1RmV49hZlvMosQjBUoNcqhqOI0n4l8HOLyna7KIzoNKG62GtUCZh8uy8IjdUiWPYGEtkZ9zE0bnnF+R7HGvQ==
-PublicExponent: AQAB
-PrivateExponent: Lp/c3IUD7o4re7uX4dS9KLT3EZnn0OfMdiLNoafCszjzbX/NWrIBHxdLrCS6rr7k7pbgLU6+VqEmJB/vYdsPITJZGpbOXxieBYBbpzJ4hm/uIA0gn28Y66pUKWTkS3ud2zCPfkZFREL3c2M1Rvf1zxdWgOPl1oHsiKsmgpl9qJOSKHMWFC+m/pUMJ7iOMgyDRV+PNeb/8P1jVOAYyQMEnu+enw2ro2NiWXNikbnaWrIv3IxVZAyZG4/H8+1vfQFPDWztosOy7OhV3WyMJkfwcXrlGoyLlxyAgkh/jeCnmPllxlJZGTgCtoVYd/n8osMXCDKxpAhsfdfCPeNOcjocgQ==
-Prime1: +T+s7wv+zVqONJqkAKw4OCVzxBc5FWrmDPcjPCUeKIK/K/3+XjmIqTlbvBKf+7rm+AGVnXAbqk90+jzE3mKI8HMG/rM2cx01986xNQsIqwi2VAt25huPhEyrtNzos6lmrCYaioaQnNpMvMLun3DvcaygkDUXxH7Dg+6BTHeUfnk=
-Prime2: p2YbBveBK3XyGMuVrDH9CvvpgKEoko+mPwLoKNpBoHrGxeOdCQmlPbnr0GrtZpy4sBNc5+shz2c6c1J3GlgPndT7zi2+MFGfWIGV48SAknVLfOU4iUpaGllnxcbjZeytG6WHdy2RaR3ReeGvdWxmxeuv084c2zC/7/vkcmgOqWU=
-Exponent1: EdVFeUEBdQ3imM7rpwSrbRD47HHA6tBgL1NLWRVKyBk6tloQ5gr1xS3Oa3FlsuwXdG0gmEgaIqBWvUS1zTd9lr6UJIsL/UZ8wwMt2J62ew4/hVngouwb45pcuq8HkzsulmiPg5PHKwHPdb34tr2s1BRG1KqHzc5IDNt2stLnc/k=
-Exponent2: oT+Iv1BAu7WUa/AHj+RjJGZ+iaozo+H9uOq66Uc8OjKqMErNpLwG0Qu7rHqjjdlfSjSMpNXpLpj4Q8fm9JhpCpbzq6qCbpbhUGcbFFjfpLSZ74f5yr21R3ZhsLChsTenlF8Bu3pIfKH9e1M7KXgvE22xY+xB/Z3a9XeFmfLEVMU=
-Coefficient: vG8tLZBE4s3bftN5INv2/o3knEcaoUAPfakSsjM2uLwQCGiUbBOOlp3QSdTU4MiLjDsza3fKIptdwYP9PvSkhGhtLPjBpKjRk1J1+sct3dfT66JPClJc1A8bLQPj4ZpO/BkJe6ji4HYfOp7Rjn9z8rTqwEfbP64CZV3/frUzIkQ="
- },
- {
- "id" => 2,
- "flags" => 256,
- "active" => true,
- "published" => true,
- "content" => "Private-key-format: v1.2
-Algorithm: 8 (RSASHA256)
-Modulus: wKPNcDwkCd2DKxfdkMqTFOV2ITdgxIDaOd4vQ2QtphMBY9yYwmEkNsVdVFz7VVuQHdls20JUe+brFUhs1zEMMbokulFP/qVAItAeEWcqtkPULT+mmX5HsexpFVAZ5+UXuerObk/HMiIMt1CvkIWhmjSIkAI6dFRlf/93zTjy0+vwrNWZPXSzLccK5TfJmxdYdGPcsHkg6UmqEFPQuyZpmlmpg3IwjL5YddTDobAoABz/BrH7WsW0q/PyVubITo8JuFiBI5Fmw+3ef3PVUt1jtUCGASvtqNXW4wtWrgqvQKg/odthpceQ4QagV9XSlOdml527thnf9cMpm0Gh4Ox5HQ==
-PublicExponent: AQAB
-PrivateExponent: f+M+26fRdQstrUomuZ0Cj/jVt69/+nRga9JpJiA3fe1YGue0MjczR3k3QG6KHFyxDF/vuJAMbkUbBAIU37ecFNcy0s5wgOlL7tCjZYJMBLx6+58qBvSivCfqi0+mIyEf4zlS2kD0SP/52SkjpJpScoE1uAUCsX/l8lezPPb1nmH3RDwJwX1NVhsErHCAmxGDoj4nPCEhKgHkdbR0i8geXGdWR4slyq1EhuGJal4p5sNvzDQTYRy6r49rpbNHw9F7ojomIhTUCUjOXAX0X1HB5UTXRMpgpCNEjRG1a+aqxp/ZSMHSEGCv67fua5Qrd/qX1Ppns/oqZfCfTpTD3v/sMQ==
-Prime1: +0zQuFi7rZDTMGMIKiF6UOG5+pKwGxHmgKPOGF6fk3tIuSomgiVD3DLz5Y6kYk0kKls6IiA6X2esYwNXAaLe0dyMzpAnU4URXhFW7fUnHP0zA7NmaFRYPHstPeU59/JS+zmVlj4Ok1oeGocSGAFYGxXa+Sot0fyCXpAjZboDWg8=
-Prime2: xD4hprQmcn5gmLqYO9+nEEJTNyNccbAciiKjRJxIE7w6muuKESx0uUn5XdnzSxhbVkK16kkEqW3s+Y+VoLxwRj2fuvoPfx8nTQXY1esgcIZCG8ubvHW5T0bzee5gyX3cMvaxkoeM7euYgvh0UwR/FG910SwAlmMZjSwXay2YlhM=
-Exponent1: 6vcWzNcCnDWmkT53WtU0hb2Y4+YVzSm+iRcf039d20rRY3g6y0NGoPPvQftOTi9smkH0KAZULfJEp8tupbQAfN6ntVfpvVjVNUwnKJUo/hzsfxBVt0Ttv5c4ZQAYZHHqDsX3zKO3gyUmso0KaPGQzLpxpLlAYG+mAf7paeszyRc=
-Exponent2: ouvWMjk0Bi/ncETRqDuYzkXSIl+oGvaT6xawp4B70m6d1QohWPqoeT/x2Dne44R4J9hAgR5X0XXinJnZJlXrfFUi7C84eFhb33UwPQD0sJa2Aa97Pu4Zh7im4J7IGd/01Ra7+6Ovm8LRnkI5CMcd3dBfZuX6IuBpUSu+0YtMN6M=
-Coefficient: 5lP9IFknvFgaXKCs8MproehHSFhFTWac4557HIn03KrnlGOKDcY6DC/vgu1e42bEZ4J0RU0EELp5u4tAEYcumIaIVhfzRsajYRGln2mHe6o6nTO+FbANKuhyVmBEvTVczPOcYLrFXKVTglKAs+8W96dYIMDhiAwxi9zijLKKQ1k="
- }
- ]
- end
- false
- end
-
- ## Example lookup
- ## Returns SOA, MX, NS and A records for example.com
- ## also static A record for test.example.com
- ## and dynamic A record for anything else in example.com domain
- def do_lookup(args)
- if args["qname"] == "example.com" and args["qtype"].downcase == "soa"
- return [
- record("SOA","example.com", "sns.dns.icann.org noc.dns.icann.org 2013012485 7200 3600 1209600 3600"),
- ]
- elsif args["qname"] == "example.com" and args["qtype"].downcase == "any"
- return [
- record("SOA","example.com", "sns.dns.icann.org noc.dns.icann.org 2013012485 7200 3600 1209600 3600"),
- record("NS","example.com","sns.dns.icann.org"),
- record("MX","example.com","10 test.example.com")
- ]
- elsif args["qname"] == "test.example.com" and args["qtype"].downcase == "any"
- return [
- record("A","test.example.com","127.0.0.1")
- ]
- elsif args["qname"] =~ /(.*)\.example\.com$/ and args["qtype"].downcase == "any"
- ip = 0
- $1.downcase.each_byte do |b| ip = ip + b end
- ip_2 = ip/256
- ip = ip%256
- return [
- record("A",args["qname"], "127.0.#{ip_2}.#{ip}")
- ]
- end
- false
- end
-
- ## AXFR support
- ## Do note that having AXFR here is somewhat stupid since
- ## we generate records above. But it is still included
- ## for sake of having an example. Do not do this in production.
- def do_list(args)
- if args["zonename"] == "example.com"
- return [
- record("SOA","example.com", "sns.dns.icann.org noc.dns.icann.org 2013012485 7200 3600 1209600 3600"),
- record("NS","example.com","sns.dns.icann.org"),
- record("MX","example.com","10 test.example.com"),
- record("A","test.example.com","127.0.0.1")
- ]
- end
- false
- end
-
- ## Please see https://doc.powerdns.com/authoritative/backends/remote.html for methods to add here
- ## Just remember to prefix them with do_
-
- ## Some helpers after this
-
- def record_ttl(qtype,qname,content,ttl)
- {:qtype => qtype, :qname => qname, :content => content, :ttl => ttl, :auth => 1}
- end
-
- def record(qtype,qname,content)
- record_ttl(qtype,qname,content,@default_ttl,)
- end
-
- def log(message)
- @_log << message
- end
-
- ## Flushes log array and returns it.
- def consume_log
- ret = @_log
- @_log = []
- ret
- end
-
-public
- def run
- STDOUT.sync=true
- STDIN.each_line do |line|
- # So far rapidjson has managed to follow RFC4627
- # and hasn't done formatted json so there should be
- # no newlines.
- msg = JSON.parse(line)
-
- # it's a good idea to prefix methods with do_ to prevent
- # clashes with initialize et al, and downcase it for simplicity
- method = "do_#{msg["method"].downcase}".to_sym
- if self.respond_to? method
- result = self.send method, msg["parameters"]
- else
- log "Method #{msg["method"]} not implemented"
- result = false
- end
- # build and emit result
- reply = { :result => result, :log => consume_log }
- puts reply.to_json
- end
- end
-end
-
-begin
- RequestHandler.new.run
-rescue Interrupt
- # ignore this exception, caused by ctrl+c in foreground mode
-end
}
module_remotebackend_test_sources_extra = files(
- 'example.rb',
- 'Gemfile',
- 'Gemfile.lock',
- 'unittest_http.rb',
- 'unittest_json.rb',
- 'unittest_pipe.rb',
- 'unittest_post.rb',
- 'unittest.rb',
- 'unittest_zeromq.rb',
+ 'requirements.txt',
+ 'pdns_unittest.py',
+ 'unittest_http.py',
+ 'unittest_json.py',
+ 'unittest_pipe.py',
+ 'unittest_post.py',
+ 'unittest_zeromq.py',
)
endif
--- /dev/null
+import pdns.remotebackend
+import sys
+import io
+import time
+
+
+# define a simple $domain
+
+ID_DOMAIN = {
+ 1: 'unit.test.',
+}
+
+DOMAINS = {
+ 'unit.test.': {
+ 'id': 1,
+ 'ttl': 300,
+ 'name': 'unit.test.',
+ 'notified_serial': 0,
+ 'meta': {},
+ 'keys': {},
+ 'rr': {
+ 'unit.test.' : {
+ 'SOA': ["ns.unit.test. hostmaster.unit.test. 1 2 3 4 5"],
+ 'NS': ["ns1.unit.test.", "ns2.unit.test."],
+ },
+ 'ns1.unit.test.': {
+ 'A': ["10.0.0.1"]
+ },
+ 'ns2.unit.test.': {
+ 'A': ["10.0.0.2"]
+ },
+ 'empty.unit.test.': {}
+ },
+ 'kind': 'native',
+ },
+ 'master.test.': {
+ 'id': 2,
+ 'ttl': 300,
+ 'name': 'master.test.',
+ 'notified_serial': 2,
+ 'meta': {},
+ 'keys': {},
+ 'rr': {
+ 'master.test.': {
+ 'SOA': ["ns.master.test. hostmaster.master.test. 1 2 3 4 5"],
+ }
+ },
+ 'kind': 'master',
+ },
+}
+
+TSIG_KEYS = {
+ 'test.': {
+ 'name': 'test.',
+ 'algorithm': 'NULL.',
+ 'content': 'NULL',
+ }
+}
+
+MASTERS = {
+ 'ns1.unit.test.': {
+ 'ip': '10.0.0.1'
+ }
+}
+
+class Handler(pdns.remotebackend.Handler):
+ def get_domain(self, domain):
+ if not domain.endswith("."):
+ domain = domain + "."
+ while len(domain) > 0:
+ if domain in DOMAINS:
+ return DOMAINS[domain]
+ p = domain.find(".")
+ if p == -1:
+ break
+ domain = domain[p+1:]
+ return None
+
+ def do_lookup(self, qname='', qtype='', **kwargs):
+ domain = self.get_domain(qname)
+ if domain:
+ self.result = []
+ rrset = domain['rr'].get(qname, {'qtype': []})
+ rr = rrset.get(qtype, [])
+ for r in rr:
+ self.result.append(self.record(qname=qname, qtype=qtype, content=r, ttl=domain['ttl']))
+
+ def do_list(self, zonename="", **kwargs):
+ domain = self.get_domain(zonename)
+ if domain:
+ self.result = []
+ for qname, rrset in domain['rr'].items():
+ for qtype, rr in rrset.items():
+ for r in rr:
+ self.result.append(self.record(qname=qname, qtype=qtype, content=r, ttl=domain['ttl']))
+
+ def do_getalldomainmetadata(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ self.result = domain['meta']
+
+ def do_getdomainmetadata(self, name='', kind='', **kwargs):
+ self.do_getalldomainmetadata(name=name)
+ if self.result:
+ self.result = self.result[kind]
+
+ def do_setdomainmetadata(self, name='', kind='', value=None, **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ if value is None:
+ del domain['meta'][kind]
+ else:
+ domain['meta'][kind] = value
+ self.result = True
+
+ def do_adddomainkey(self, name='', key={}, **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ k_id = len(domain['keys']) + 1
+ key['id'] = k_id
+ domain['keys'][k_id] = key
+ self.result = k_id
+
+ def do_getdomainkeys(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ self.result = []
+ for k_id, k in domain['keys'].items():
+ self.result.append(k)
+
+ def do_activatedomainkey(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ key = domain['keys'].get(int(kwargs['id']))
+ if key:
+ key['active'] = True
+ self.result = True
+
+ def do_deactivatedomainkey(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ key = domain['keys'].get(int(kwargs['id']))
+ if key:
+ key['active'] = False
+ self.result = True
+
+ def do_publishdomainkey(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ key = domain['keys'].get(int(kwargs['id']))
+ if key:
+ key['published'] = True
+ self.result = True
+
+ def do_unpublishdomainkey(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ key = domain['keys'].get(int(kwargs['id']))
+ if key:
+ key['published'] = False
+ self.result = True
+
+ def do_removedomainkey(self, name='', **kwargs):
+ domain = self.get_domain(name)
+ if domain:
+ k_id = int(kwargs['id'])
+ if k_id in domain['keys']:
+ del domain['keys'][k_id]
+ self.result = True
+
+ def do_getbeforeandafternamesabsolute(self, qname='', **kwargs):
+ if qname == 'middle.unit.test.':
+ self.result = {
+ 'unhashed': 'middle.',
+ 'before': 'begin.',
+ 'after': 'stop.',
+ }
+
+ def do_setnotified(self, **kwargs):
+ d_id = int(kwargs['id'])
+ domain_name = ID_DOMAIN.get(d_id)
+ domain = DOMAINS.get(domain_name)
+ if domain:
+ domain['notified_serial'] = kwargs['serial']
+ self.result = True
+
+ def fill_domaininfo(self, name=""):
+ domain = self.get_domain(name)
+ if domain:
+ self.result.append({
+ 'id': domain['id'],
+ 'zone': domain['name'],
+ 'masters': MASTERS,
+ 'notified_serial': domain['notified_serial'],
+ 'serial': domain['notified_serial'],
+ 'last_check': int(time.time()),
+ 'kind': domain['kind']
+ })
+
+ def do_getdomaininfo(self, name="", **kwargs):
+ self.result = []
+ self.fill_domaininfo(name)
+ if self.result:
+ self.result = self.result[0]
+
+ def do_ismaster(self, name='', ip='', **kwargs):
+ ips = MASTERS.get(name, [])
+ if ip in ips:
+ self.result = True
+
+ def do_supermasterbackend(self, domain='', nsset=[], **kwargs):
+ d_id = len(DOMAINS) + 1
+ dom = domain.lower()
+ domain = {
+ 'id': d_id,
+ 'name': dom,
+ 'kind': 'slave',
+ 'notified_serial': 0,
+ 'meta': {},
+ 'keys': {},
+ 'rr': {
+ dom: {
+ 'SOA': ["ns.%s hostmaster.%s 1 2 3 4 5" % (dom, dom)],
+ }
+ },
+ 'ttl': 300,
+ }
+
+ nsset = []
+ for rr in nsset:
+ nsset.append(self.record(qname=rr['qname'], qtype=rr['qtype'], content=rr['content'], ttl=rr['ttl']))
+
+ domain['rr'][dom]['NS'] = nsset
+ DOMAINS[dom] = domain
+
+ self.result = [{
+ 'nameserver': 'ns.%s' % dom,
+ 'account': ''
+ }]
+
+
+ def do_createslavedomain(self, domain='', **kwargs):
+ d_id = len(DOMAINS) + 1
+ dom = domain.lower()
+ domain = {
+ 'id': d_id,
+ 'name': dom,
+ 'kind': 'slave',
+ 'notified_serial': 0,
+ 'ttl': 300,
+ 'meta': {},
+ 'keys': {},
+ 'rr': {
+ }
+ }
+ self.result = True
+
+ def do_feedrecord(self, rr={}, **kwargs):
+ qname = rr['qname']
+ qtype = rr['qtype']
+ domain = self.get_domain(qname)
+ if domain:
+ if not qname in domain['rr']:
+ domain['rr'][qname] = {qtype: []}
+ elif not qtype in domain['rr'][qname]:
+ domain['rr'][qname][qtype] = []
+ domain['rr'][qname][qtype].append(self.record(
+ qname=qname,
+ qtype=qtype,
+ content=rr['content'],
+ ttl=rr.get('ttl', domain['ttl']))
+ )
+ self.result = True
+
+ def do_replacerrset(self, qname='', qtype='', rrset=[], **kwargs):
+ domain = self.get_domain(qname)
+ if domain and qname in domain['rr']:
+ if qtype in domain['rr'][qname]:
+ del domain['rr'][qname][qtype]
+ for row in rrset:
+ self.do_feedrecord(rr=row)
+
+ def do_feedents(self, **kwargs):
+ self.result = True
+
+ def do_feedents3(self, **kwargs):
+ self.result = True
+
+ def do_gettsigkey(self, name='', **kwargs):
+ if name in TSIG_KEYS:
+ self.result = TSIG_KEYS[name]
+
+ def do_settsigkey(self, name='', algorithm='', content='', **kwargs):
+ TSIG_KEYS[name] = {
+ 'name': name,
+ 'algorithm': algorithm,
+ 'content': content,
+ }
+ self.result = True
+
+ def do_gettsigkeys(self, **kwargs):
+ self.result = []
+ for name, key in TSIG_KEYS.items():
+ self.result.append(key)
+
+ def do_deletetsigkey(self, name='', **kwargs):
+ if name in TSIG_KEYS:
+ del TSIG_KEYS[name]
+ self.result = True
+
+ def do_starttransaction(self, **kwargs):
+ self.result = True
+
+ def do_committransaction(self, **kwargs):
+ self.result = True
+
+ def do_aborttransaction(self, **kwargs):
+ self.result = True
+
+ def do_directbackendcmd(self, query='', **kwargs):
+ self.result = query
+
+ def do_getalldomains(self, **kwargs):
+ self.result = []
+ for name in DOMAINS.keys():
+ self.fill_domaininfo(name=name)
+
+ def do_getupdatedmasters(self, **kwargs):
+ self.result = []
+ for name in DOMAINS.keys():
+ if DOMAINS[name]['kind'] == 'master':
+ self.fill_domaininfo(name=name)
--- /dev/null
+Pdns-Remotebackend==0.8.0
+pyzmq==25.1.2
new RemoteLoader();
BackendMakers().launch("remote");
// then get us a instance of it
- ::arg().set("remote-connection-string") = "pipe:command=unittest_pipe.rb";
+ ::arg().set("remote-connection-string") = "pipe:command=unittest_pipe.py";
::arg().set("remote-dnssec") = "yes";
backendUnderTest = std::move(BackendMakers().all()[0]);
// load few record types to help out
#!/usr/bin/env bash
+set -eu
+
new_api=0
-mode=$1
+mode=${1-}
+
+progdir=${abs_srcdir-$PWD}
+
+if [ ! -d venv ]; then
+ flock .create_testenv bash -c "python3 -m venv venv && source venv/bin/activate && pip install wheel && pip install -r ${progdir}/requirements.txt"
+ rm .create_testenv
+fi
+
+source venv/bin/activate
# we could be ran with new API
-while [ "$1" != "" ]
+while [ "${1-}" != "" ]
do
if [ "$1" == "--" ]; then
new_api=1
shift
done
-webrick_pid=""
+httpd_pid=""
socat_pid=""
zeromq_pid=""
socat=$(which socat)
function start_web() {
local service_logfile="${mode_name%\.test}_server.log"
- ./unittest_"${1}".rb >> "${service_logfile}" 2>&1 &
- webrick_pid=$!
+ ${progdir}/unittest_"${1}".py >> "${service_logfile}" 2>&1 &
+ httpd_pid=$!
local timeout=0
while [ ${timeout} -lt 20 ]; do
local res
- res=$(curl http://localhost:62434/ping 2>/dev/null)
+ res=$(curl http://localhost:62434/ping 2>/dev/null || true)
if [ "$res" == "pong" ]; then
# server is up and running
return 0
(( timeout=timeout+1 ))
done
- if kill -0 ${webrick_pid} 2>/dev/null; then
+ if kill -0 ${httpd_pid} 2>/dev/null; then
# if something is wrong with curl (i.e. curl isn't installed, localhost is firewalled ...)
# the status check will fail -- cleanup required!
echo >&2 "WARNING: Timeout (${timeout}s) reached: \"${1}\" test service process is running but status check failed"
- kill -KILL ${webrick_pid} 2>/dev/null
+ kill -KILL ${httpd_pid} 2>/dev/null
fi
echo >&2 "ERROR: A timeout (${timeout}s) was reached while waiting for \"${1}\" test service to start!"
}
function stop_web() {
- if [ -z "${webrick_pid}" ]; then
+ if [ -z "${httpd_pid}" ]; then
# should never happen - why was stop_web() called?
echo >&2 "ERROR: Unable to stop \"${1}\" test service: Did we ever start the service?"
exit 99
fi
- if ! kill -0 ${webrick_pid} 2>/dev/null; then
+ if ! kill -0 ${httpd_pid} 2>/dev/null; then
# should never happen - did the test crashed the service?
- echo >&2 "ERROR: Unable to stop \"${1}\" test service: service (${webrick_pid}) not running"
+ echo >&2 "ERROR: Unable to stop \"${1}\" test service: service (${httpd_pid}) not running"
exit 69
fi
- kill -TERM ${webrick_pid}
+ kill -TERM ${httpd_pid}
local timeout=0
while [ ${timeout} -lt 5 ]; do
- if ! kill -0 ${webrick_pid} 2>/dev/null; then
+ if ! kill -0 ${httpd_pid} 2>/dev/null; then
# service was stopped
return 0
fi
(( timeout=timeout+1 ))
done
- if kill -0 ${webrick_pid} 2>/dev/null; then
+ if kill -0 ${httpd_pid} 2>/dev/null; then
echo >&2 "WARNING: Timeout (${timeout}s) reached - killing \"${1}\" test service ..."
- kill -KILL ${webrick_pid} 2>/dev/null
+ kill -KILL ${httpd_pid} 2>/dev/null
return $?
fi
}
local service_logfile="${mode_name%\.test}_server.log"
- ./unittest_zeromq.rb >> "${service_logfile}" 2>&1 &
+ ${progdir}/unittest_zeromq.py >> "${service_logfile}" 2>&1 &
zeromq_pid=$!
+ echo "ZeroMQ running as $zeromq_pid"
local timeout=0
while [ ${timeout} -lt 5 ]; do
exit 77
fi
- $socat unix-listen:/tmp/remotebackend.sock exec:./unittest_pipe.rb &
+ $socat unix-listen:/tmp/remotebackend.sock exec:${progdir}/unittest_pipe.py &
socat_pid=$!
local timeout=0
}
function run_test() {
+ rv=0
if [ $new_api -eq 0 ]; then
- ./"$mode_name"
+ ./"$mode_name" || rv=$?
else
- $mode
+ $mode || rv=$?
fi
}
;;
remotebackend_unix.test)
start_unix
- run_test ; rv=$?
+ run_test
stop_unix
;;
remotebackend_http.test)
start_web "http"
- run_test ; rv=$?
+ run_test
stop_web "http"
;;
remotebackend_post.test)
start_web "post"
- run_test ; rv=$?
+ run_test
stop_web "post"
;;
remotebackend_json.test)
start_web "json"
- run_test ; rv=$?
+ run_test
stop_web "json"
;;
remotebackend_zeromq.test)
start_zeromq
- run_test ; rv=$?
+ run_test
stop_zeromq
;;
*)
+++ /dev/null
-require 'rubygems'
-require 'bundler/setup'
-require 'json'
-
-# define a simple $domain
-
-$ttl = 300
-$notified_serial = 1
-
-$domain = {
- "unit.test." => {
- "SOA" => ["ns.unit.test. hostmaster.unit.test. 1 2 3 4 5"],
- "NS" => ["ns1.unit.test.", "ns2.unit.test."],
- },
- "ns1.unit.test." => {
- "A" => ["10.0.0.1"]
- },
- "ns2.unit.test." => {
- "A" => ["10.0.0.2"]
- },
- "empty.unit.test." => {}
-}
-
-$meta = {}
-
-$keys = {}
-
-$tsigkeys = { "test." => {:name => "test.", :algorithm => "NULL.", :content => "NULL"} }
-
-$masters = { :name => "ns1.unit.test.", :ip => "10.0.0.1" }
-
-class Handler
- def initialize
- end
-
- 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
-
- def do_initialize(*args)
- return true, "Test bench initialized"
- end
-
- def do_lookup(args)
- ret = []
- if $domain.has_key?(args["qname"])
- if $domain[args["qname"]].has_key?(args["qtype"])
- $domain[args["qname"]][args["qtype"]].each do |rd|
- ret << rr(args["qname"], args["qtype"], rd, $ttl)
- end
- elsif args["qtype"] == 'ANY'
- $domain[args["qname"]].each do |qt,qr|
- qr.each do |rd|
- ret << rr(args["qname"], qt, rd, $ttl)
- end
- end
- end
- end
- [false] unless ret.size>0 and args["qname"] != "empty.unit.test"
- [ret]
- end
-
- def do_list(args)
- ret = []
- if args["zonename"] == "unit.test."
- $domain.each do |qname,rdata|
- rdata.each do |rtype,rc|
- rc.each do |rd|
- ret << rr(qname,rtype,rd,$ttl)
- end
- end
- end
- end
- [false] unless ret.size>0
- [ret]
- end
-
- def do_getalldomainmetadata(args)
- return [ $meta[args["name"]] ] if $meta.has_key?(args["name"])
- return [false]
- end
-
- def do_getdomainmetadata(args)
- return [ $meta[args["name"]][args["kind"]] ] if $meta.has_key?(args["name"]) and $meta[args["name"]].has_key?(args["kind"])
- return [false]
- end
-
- def do_setdomainmetadata(args)
- $meta[args["name"].to_s] = {} unless $meta.has_key? args["name"]
- $meta[args["name"].to_s][args["kind"].to_s] = args["value"].to_a
- [true]
- end
-
- def do_adddomainkey(args)
- $keys[args["name"]] = [] unless $keys.has_key? args["name"]
- id=$keys[args["name"]].size + 1
- args["key"]["id"] = id
- $keys[args["name"]] << args["key"]
- [id]
- end
-
- def do_getdomainkeys(args)
- if $keys.has_key? args["name"]
- return [ $keys[args["name"]] ]
- end
- [false]
- end
-
- def do_activatedomainkey(args)
- args["id"] = args["id"].to_i
- if $keys.has_key? args["name"]
- if $keys[args["name"]][args["id"]-1]
- $keys[args["name"]][args["id"]-1]["active"] = true
- return [true]
- end
- end
- [false]
- end
-
- def do_deactivatedomainkey(args)
- args["id"] = args["id"].to_i
- if $keys.has_key? args["name"]
- if $keys[args["name"]][args["id"]-1]
- $keys[args["name"]][args["id"]-1]["active"] = false
- return [true]
- end
- end
- [false]
- end
-
- def do_publishdomainkey(args)
- args["id"] = args["id"].to_i
- if $keys.has_key? args["name"]
- if $keys[args["name"]][args["id"]-1]
- $keys[args["name"]][args["id"]-1]["published"] = true
- return [true]
- end
- end
- [false]
- end
-
- def do_unpublishdomainkey(args)
- args["id"] = args["id"].to_i
- if $keys.has_key? args["name"]
- if $keys[args["name"]][args["id"]-1]
- $keys[args["name"]][args["id"]-1]["published"] = false
- return [true]
- end
- end
- [false]
- end
-
- def do_removedomainkey(args)
- args["id"] = args["id"].to_i
- if $keys.has_key? args["name"]
- if $keys[args["name"]][args["id"]-1]
- $keys[args["name"]].delete_at args["id"]-1
- return [true]
- end
- end
- [false]
- end
-
- def do_getbeforeandafternamesabsolute(args)
- return [ { :unhashed => "middle.", :before => "begin.", :after => "stop." } ] if args["qname"] == 'middle.unit.test.'
- [false]
- end
-
- def do_gettsigkey(args)
- if $tsigkeys.has_key? args["name"]
- return [{:algorithm => $tsigkeys[args["name"]][:algorithm], :content => $tsigkeys[args["name"]][:content] }]
- end
- [false]
- end
-
- def do_setnotified(args)
- if args["id"].to_i == 1
- $notified_serial = args["serial"].to_i
- return [true]
- end
- [false]
- end
-
- def do_getdomaininfo(args)
- if args["name"] == "unit.test."
- return [{
- :id => 1,
- :zone => "unit.test.",
- :masters => ["10.0.0.1"],
- :notified_serial => $notified_serial,
- :serial => $notified_serial,
- :last_check => Time.now.to_i,
- :kind => 'native'
- }]
- end
- if args["name"] == "master.test."
- return [{
- :id => 2,
- :zone => "master.test.",
- :masters => ["10.0.0.1"],
- :notified_serial => $notified_serial,
- :serial => $notified_serial,
- :last_check => Time.now.to_i,
- :kind => 'master'
- }]
- end
- [false]
- end
-
- def do_ismaster(args)
- $masters[:name] == args["name"] && $masters[:ip] == args["ip"]
- end
-
- def do_supermasterbackend(args)
- $domain[args["domain"]] = {
- "NS" => args["nsset"]
- }
- [true]
- end
-
- def do_createslavedomain(args)
- $domain[args["domain"]] = {
- }
- [true]
- end
-
- def do_feedrecord(args)
- args.delete "trxid"
- rr = args["rr"]
- name = rr["qname"]
- qtype = rr["qtype"]
- $domain[name] = {} unless $domain.has_key? name
- $domain[name][qtype] = [] unless $domain[name].has_key? qtype
- $domain[name][qtype] << rr["content"]
- [true]
- end
-
- def do_replacerrset(args)
- $domain[args["qname"]].delete args["qtype"] if $domain.has_key?(args["qname"]) and $domain[args["qname"]].has_key?(args["qtype"])
- args["rrset"] = args["rrset"].values if args["rrset"].is_a?(Hash)
- args["rrset"].each do |rr|
- self.do_feedrecord({"trxid" => args["trxid"], "rr" => rr})
- end
- [true]
- end
-
- def do_feedents(args)
- [true]
- end
-
- def do_feedents3(args)
- [true]
- end
-
- def do_settsigkey(args)
- $tsigkeys[args["name"]] = { :name => args["name"], :algorithm => args["algorithm"], :content => args["content"] }
- [true]
- end
-
- def do_deletetsigkey(args)
- $tsigkeys.delete args["name"] if $tsigkeys.has_key? args["name"]
- [true]
- end
-
- def do_gettsigkeys(*args)
- return [$tsigkeys.values]
- end
-
- def do_starttransaction(args)
- [true]
- end
-
- def do_committransaction(args)
- [true]
- end
-
- def do_aborttransaction(args)
- [true]
- end
-
- def do_directbackendcmd(args)
- [args["query"]]
- end
-
- def do_getalldomains(args)
- [do_getdomaininfo({'name'=>'unit.test.'})]
- end
-
- def do_getupdatedmasters(args)
- [do_getdomaininfo({'name'=>'master.test.'})]
- end
-end
-
--- /dev/null
+#!/usr/bin/env python
+
+import http.server
+import json
+import re
+
+from pdns_unittest import Handler
+from urllib.parse import parse_qsl, urlparse, unquote
+
+class DNSBackendServer(http.server.HTTPServer):
+ def __init__(self, *args, **kwargs):
+ self.handler = Handler()
+ 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)
+
+
+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'])
+
+ self.log_error("%r", self.args)
+ 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.log_error("%r", self.handler.result)
+ 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))
+
+
+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 'json'
-require 'thread'
-require 'webrick'
-require './unittest'
-
-class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
- def initialize(server, dnsbackend)
- @dnsbackend = dnsbackend
- @semaphore = Mutex.new
- @f = File.open("/tmp/remotebackend.txt.#{$$}","ab")
- @f.set_encoding 'UTF-8'
- 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.to_i,
- "name" => url.shift
- }
- when "adddomainkey", "gettsigkey", "getdomaininfo", "settsigkey", "deletetsigkey", "getalldomainmetadata"
- {
- "name" => url.shift
- }
- when "setnotified", "feedents"
- {
- "id" => url.shift.to_i
- }
- when "ismaster"
- {
- "name" => url.shift,
- "ip" => url.shift
- }
- when "supermasterbackend", "createslavedomain"
- {
- "ip" => url.shift,
- "domain" => url.shift
- }
- when "feedents3"
- {
- "id" => url.shift.to_i,
- "domain" => url.shift
- }
- when "starttransaction"
- {
- "id" => url.shift.to_i,
- "domain" => url.shift,
- "trxid" => url.shift.to_i
- }
- when "committransaction", "aborttransaction"
- {
- "trxid" => url.shift.to_i
- }
- when "replacerrset"
- {
- "id" => url.shift.to_i,
- "qname" => url.shift,
- "qtype" => 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)
-
- method = "do_#{method}"
-
- # get more arguments
- req.each do |k,v|
- attr = k[/X-RemoteBackend-(.*)/,1]
- if attr
- args[attr] = v
- 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
- begin
- @f.puts "#{Time.now.to_f} [http]: #{({:method=>method,:parameters=>args}).to_json}"
- rescue Encoding::UndefinedConversionError
- # this fails with encoding error for feedEnts3
- end
-
- @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
-
-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
-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
+
+import http.server
+import json
+from pdns_unittest import Handler
+
+
+class DNSBackendServer(http.server.HTTPServer):
+ def __init__(self, *args, **kwargs):
+ self.handler = Handler()
+ 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)
+
+
+class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
+ def __init__(self, *args, **kwargs):
+ self.handler = kwargs['handler']
+ super().__init__(*args)
+
+ def do_GET(self):
+ if self.path == '/ping':
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write("pong".encode())
+ return
+ self.send_error(404)
+
+ def do_POST(self):
+ if self.path != "/dns/endpoint.json":
+ self.send_error(404)
+ return
+
+ try:
+ length = int(self.headers.get('content-length'))
+ message = json.loads(self.rfile.read(length).decode())
+
+ method = "do_" + message['method'].lower()
+ args = message['parameters']
+ self.handler.result = False
+ self.handler.log = []
+
+ if callable(getattr(self.handler, method, None)):
+ getattr(self.handler, method)(**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:
+ self.send_error(400, message=str(e))
+
+
+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 'json'
-require 'thread'
-require 'webrick'
-require './unittest'
-
-class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
- def initialize(server, dnsbackend)
- @dnsbackend = dnsbackend
- @semaphore = Mutex.new
- @f = File.open("/tmp/remotebackend.txt.#{$$}","a")
- @f.sync
- end
-
- def do_POST(req,res)
- req.continue
-
- return 400, "Bad request" unless req.path == "/dns/endpoint.json"
-
- tmp = JSON::parse(req.body)
- method = tmp["method"].downcase
- method = "do_#{method}"
- args = tmp["parameters"]
-
- @f.puts "#{Time.now.to_f} [http/json]: #{({: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/json]: #{res.body}"
- end
- end
-end
-
-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
-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 python3
+
+from pdns_unittest import Handler
+from pdns.remotebackend import PipeConnector
+import os
+
+connector = PipeConnector(Handler)
+connector.run()
+++ /dev/null
-#!/usr/bin/env ruby
-
-require 'rubygems'
-require 'bundler/setup'
-require 'json'
-require './unittest'
-
-h = Handler.new()
-f = File.open "/tmp/remotebackend.txt.#{$$}","a"
-f.sync
-
-STDOUT.sync = true
-begin
- STDIN.each_line do |line|
- f.puts "#{Time.now.to_f}: [pipe] #{line}"
- # expect json
- input = {}
- line = line.strip
- next if line.empty?
- begin
- input = JSON.parse(line)
- method = "do_#{input["method"].downcase}"
- args = input["parameters"] || []
-
- if h.respond_to?(method.to_sym) == false
- res = false
- else
- res, log = h.send(method,args)
- 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
+
+import http.server
+import json
+from urllib.parse import parse_qs, urlparse
+from pdns_unittest import Handler
+
+
+class DNSBackendServer(http.server.HTTPServer):
+ def __init__(self, *args, **kwargs):
+ self.handler = Handler()
+ 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)
+
+
+class DNSBackendHandler(http.server.BaseHTTPRequestHandler):
+ def __init__(self, *args, **kwargs):
+ self.handler = kwargs['handler']
+ super().__init__(*args)
+
+ def do_GET(self):
+ if self.path == '/ping':
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write("pong".encode())
+ return
+ self.send_error(404)
+
+ def do_POST(self):
+ path = urlparse(self.path).path
+ if not path.startswith('/dns/'):
+ self.send_error(404)
+ return
+
+ try:
+ length = int(self.headers.get('content-length'))
+ args = json.loads(parse_qs(self.rfile.read(length).decode())['parameters'][0])
+ method = "do_%s" % path[5:].lower()
+ self.log_error("%r", args)
+
+ self.handler.result = False
+ self.handler.log = []
+
+ if callable(getattr(self.handler, method, None)):
+ getattr(self.handler, method)(**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:
+ self.send_error(400, message=str(e))
+
+
+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 'json'
-require 'thread'
-require 'webrick'
-require './unittest'
-
-class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
- def initialize(server, dnsbackend)
- @dnsbackend = dnsbackend
- @semaphore = Mutex.new
- @f = File.open("/tmp/remotebackend.txt.#{$$}","a")
- @f.sync
- end
-
- def do_POST(req,res)
- req.continue
-
- tmp = req.path[/dns\/(.*)/,1]
- return 400, "Bad request" if (tmp.nil?)
-
- url = tmp.split('/')
- method = url.shift.downcase
- method = "do_#{method}"
- args = JSON::parse(req.query["parameters"])
-
- @f.puts "#{Time.now.to_f} [http/post]: #{({: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/post]: #{res.body}"
- end
- end
-end
-
-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
-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
+
+import zmq
+import json
+import os
+from urllib.parse import parse_qs, urlparse
+from pdns_unittest import Handler
+
+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():
+ context = zmq.Context()
+ socket = context.socket(zmq.REP)
+ socket.bind("ipc:///tmp/remotebackend.0")
+ handler = Handler()
+ print("Listening on ipc:///tmp/remotebackend.0")
+
+ 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'
-require './unittest'
-
-h = Handler.new()
-f = File.open "/tmp/remotebackend.txt.#{$$}","a"
-f.sync = true
-
-runcond=true
-
-trap('INT') { runcond = false }
-trap('TERM') { runcond = false }
-
-begin
- context = ZeroMQ::Context.new
- socket = context.socket ZMQ::REP
- socket.bind("ipc:///tmp/remotebackend.0")
-
- print "[#{Time.now.to_s}] ZeroMQ unit test responder running\n"
-
- while(runcond) 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)
- method = "do_#{input["method"].downcase}"
- args = input["parameters"] || []
-
- if h.respond_to?(method.to_sym) == false
- res = false
- else
- res, log = h.send(method,args)
- end
- socket.send_string ({:result => res, :log => log}).to_json + "\n" , 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 + "\n";
- f.puts "#{Time.now.to_f} [zmq]: #{({:result => false, :log => "Cannot parse input #{line}"}).to_json}"
- next
- end
- end
-rescue SystemExit, Interrupt, Errno::EINTR
-end
-
-print "[#{Time.now.to_s}] ZeroMQ unit test responder ended\n"