1#!/usr/bin/env ruby
2
3require 'rubygems'
4require 'bundler/setup'
5require 'json'
6require 'thread'
7require 'webrick'
8require './unittest'
9
10class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
11   def initialize(server, dnsbackend)
12     @dnsbackend = dnsbackend
13     @semaphore = Mutex.new
14     @f = File.open("/tmp/remotebackend.txt.#{$$}","ab")
15     @f.set_encoding 'UTF-8'
16   end
17
18   def parse_arrays(params)
19     newparams = {}
20     params.each do |key,val|
21         if key=~/^(.*)\[(.*)\]\[(.*)\]/
22             newparams[$1] = {} unless newparams.has_key? $1
23             newparams[$1][$2] = {} unless newparams[$1].has_key? $2
24             newparams[$1][$2][$3] = val
25             params.delete key
26         elsif key=~/^(.*)\[(.*)\]/
27           if $2 == ""
28             newparams[$1] = [] unless newparams.has_key? $1
29             newparams[$1] << val
30           else
31             newparams[$1] = {} unless newparams.has_key? $1
32             newparams[$1][$2] = val
33           end
34           params.delete key
35         end
36     end
37     params.merge newparams
38   end
39
40   def parse_url(url)
41     url = url.split('/')
42     method = url.shift.downcase
43
44     # do some determining based on method names
45     args = case method
46     when "lookup"
47         {
48          "qname" => url.shift,
49          "qtype" => url.shift
50         }
51     when "list"
52        {
53	  "id" => url.shift,
54          "zonename" => url.shift
55        }
56     when "getbeforeandafternamesabsolute", "getbeforeandafternames"
57        {
58           "id" => url.shift.to_i,
59           "qname" => url.shift
60        }
61     when "getdomainmetadata", "setdomainmetadata", "getdomainkeys"
62        {
63            "name" => url.shift,
64            "kind" => url.shift
65        }
66     when "removedomainkey", "activatedomainkey", "deactivatedomainkey"
67        {
68             "id" => url.shift.to_i,
69             "name" => url.shift
70        }
71     when "adddomainkey", "gettsigkey", "getdomaininfo", "settsigkey", "deletetsigkey", "getalldomainmetadata"
72        {
73             "name" => url.shift
74        }
75     when "setnotified", "feedents"
76        {
77             "id" => url.shift.to_i
78        }
79     when "ismaster"
80        {
81             "name" => url.shift,
82             "ip" => url.shift
83        }
84     when "supermasterbackend", "createslavedomain"
85        {
86             "ip" => url.shift,
87             "domain" => url.shift
88        }
89     when "feedents3"
90        {
91             "id" => url.shift.to_i,
92             "domain" => url.shift
93        }
94     when "starttransaction"
95        {
96             "id" => url.shift.to_i,
97             "domain" => url.shift,
98	     "trxid" => url.shift.to_i
99	}
100     when "committransaction", "aborttransaction"
101        {
102             "trxid" => url.shift.to_i
103        }
104     when "replacerrset"
105        {
106          "id" => url.shift.to_i,
107          "qname" => url.shift,
108          "qtype" => url.shift
109        }
110     else
111        {}
112     end
113
114     [method, args]
115   end
116
117   def do_GET(req,res)
118     req.continue
119
120     tmp = req.path[/dns\/(.*)/,1]
121     return 400, "Bad request" if (tmp.nil?)
122
123     method, args = parse_url(tmp)
124
125     method = "do_#{method}"
126
127     # get more arguments
128     req.each do |k,v|
129        attr = k[/X-RemoteBackend-(.*)/,1]
130        if attr
131          args[attr] = v
132        end
133     end
134
135     args = args.merge req.query
136
137     if method == "do_adddomainkey"
138        args["key"] = {
139           "flags" => args.delete("flags").to_i,
140           "active" => args.delete("active").to_i,
141           "published" => args.delete("published").to_i,
142           "content" => args.delete("content")
143        }
144     end
145
146     args = parse_arrays args
147     begin
148        @f.puts "#{Time.now.to_f} [http]: #{({:method=>method,:parameters=>args}).to_json}"
149     rescue Encoding::UndefinedConversionError
150        # this fails with encoding error for feedEnts3
151     end
152
153     @semaphore.synchronize do
154       if @dnsbackend.respond_to?(method.to_sym)
155          result, log = @dnsbackend.send(method.to_sym, args)
156          body = {:result => result, :log => log}
157          res.status = 200
158          res["Content-Type"] = "application/javascript; charset=utf-8"
159          res.body = body.to_json
160        else
161          res.status = 404
162          res["Content-Type"] = "application/javascript; charset=utf-8"
163          res.body = ({:result => false, :log => ["Method not found"]}).to_json
164        end
165
166        @f.puts "#{Time.now.to_f} [http]: #{res.body}"
167     end
168   end
169
170   def do_DELETE(req,res)
171     do_GET(req,res)
172   end
173
174   def do_POST(req,res)
175     do_GET(req,res)
176   end
177
178   def do_PATCH(req,res)
179     do_GET(req,res)
180   end
181
182   def do_PUT(req,res)
183     do_GET(req,res)
184   end
185end
186
187server = WEBrick::HTTPServer.new(
188	:Port=>62434,
189	:BindAddress=>"localhost",
190#	Logger: WEBrick::Log.new("remotebackend-server.log"),
191	:AccessLog=>[ [ File.open("remotebackend-access.log", "w"), WEBrick::AccessLog::COMBINED_LOG_FORMAT ] ]
192)
193
194be = Handler.new
195server.mount "/dns", DNSBackendHandler, be
196server.mount_proc("/ping"){ |req,resp| resp.body = "pong" }
197
198trap('INT') { server.stop }
199trap('TERM') { server.stop }
200
201server.start
202