File: powerdns-example-script.lua

package info (click to toggle)
pdns-recursor 4.8.8-1%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 9,620 kB
  • sloc: cpp: 95,714; javascript: 20,651; sh: 4,679; makefile: 652; xml: 37
file content (132 lines) | stat: -rw-r--r-- 3,786 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
pdnslog("pdns-recursor Lua script starting!", pdns.loglevels.Warning)

blockset = newDS()
blockset:add{"powerdns.org", "xxx"}

dropset = newDS()
dropset:add("123.cn")

malwareset = newDS()
malwareset:add("nl")

magic2 = newDN("www.magic2.com")

magicMetric = getMetric("magic")

badips = newNMG()
badips:addMask("127.1.0.0/16")

-- this check is applied before any packet parsing is done
function ipfilter(rem, loc, dh)
  pdnslog("ipfilter called, rem: "..rem:toStringWithPort().." loc: "..loc:toStringWithPort().." match:"..tostring(badips:match(rem)))
  pdnslog("id: "..dh:getID().." aa: "..tostring(dh:getAA()).." ad: "..tostring(dh:getAD()).." arcount: "..dh:getARCOUNT())
  pdnslog("ports: "..rem:getPort().." "..loc:getPort())
  return badips:match(rem)
end

-- shows the various ways of blocking, dropping, changing questions
-- return false to say you did not take over the question, but we'll still listen to 'variable'
-- to selectively disable the cache
function preresolve(dq)
  pdnslog("Got question for "..dq.qname:toString().." from "..dq.remoteaddr:toString().." to "..dq.localaddr:toString())

  local ednssubnet = dq:getEDNSSubnet()
  if ednssubnet then
    pdnslog("Packet EDNS subnet source: "..ednssubnet:toString()..", "..ednssubnet:getNetwork():toString())
  end

  local a = dq:getEDNSOption(3)
  if a then
    pdnslog("There is an EDNS option 3 present: "..a)
  end

  loc = newCA("127.0.0.1")
  if dq.remoteaddr:equal(loc) then
    pdnslog("Query from loopback")
  end

  -- note that the comparisons below are CaSe InSensiTivE and you don't have to worry about trailing dots
  if dq.qname:equal("magic.com") then
    magicMetric:inc()
    pdnslog("Magic!")
  else
    pdnslog("not magic..")
  end

  if dq.qname == magic2 then
    pdnslog("Faster magic") -- compares against existing DNSName
  end

  if blockset:check(dq.qname) then
    dq.variable = true      -- disable packet cache in any case
    if dq.qtype == pdns.A then
      dq:addAnswer(pdns.A, "1.2.3.4")
      dq:addAnswer(pdns.TXT, "\"Hello!\"", 3601) -- ttl
      return true
    end
  end

  if dropset:check(dq.qname) then
   pdnslog("dopping query")
   dq.appliedPolicy.policyKind = pdns.policykinds.Drop
   return false -- recursor still needs to handle the policy
  end

  if malwareset:check(dq.qname) then
    dq:addAnswer(pdns.CNAME, "blog.powerdns.com.")
    dq.rcode = 0
    dq.followupFunction = "followCNAMERecords"    -- this makes PowerDNS lookup your CNAME
    return true
  end

  return false
end

-- this implements DNS64

function nodata(dq)
  if dq.qtype == pdns.AAAA then
    dq.followupFunction = "getFakeAAAARecords"
    dq.followupName = dq.qname
    dq.followupPrefix="fe80::"
    return true
  end

  if dq.qtype == pdns.PTR then
    dq.followupFunction = "getFakePTRRecords"
    dq.followupName = dq.qname
    dq.followupPrefix = "fe80::"
    return true
  end
  return false
end

-- postresolve runs after the packet has been answered, and can be used to change things
-- or still drop
function postresolve(dq)
  pdnslog("postresolve called for "..dq.qname:toString())
  local records = dq:getRecords()
  for k,v in pairs(records) do
    pdnslog(k.." "..v.name:toString().." "..v:getContent())
    if v.type == pdns.A and v:getContent() == "185.31.17.73" then
      pdnslog("Changing content!")
      v:changeContent("130.161.252.29")
      v.ttl = 1
    end
  end
  dq:setRecords(records)
  return true
end

nxdomainsuffix = newDN("com")

function nxdomain(dq)
  pdnslog("nxdomain called for: "..dq.qname:toString())
  if dq.qname:isPartOf(nxdomainsuffix) then
    dq.rcode = 0 -- make it a normal answer
    dq:addAnswer(pdns.CNAME, "ourhelpfulservice.com")
    dq:addAnswer(pdns.A, "1.2.3.4", 60, "ourhelpfulservice.com")
    return true
  end
  return false
end