summaryrefslogtreecommitdiffstats
path: root/src/output/dnscli.lua
blob: bfdaf365fecdaef5d5d584e36f1ed37ee79028bd (plain)
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
-- Copyright (c) 2018-2021, OARC, Inc.
-- All rights reserved.
--
-- This file is part of dnsjit.
--
-- dnsjit is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- dnsjit is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with dnsjit.  If not, see <http://www.gnu.org/licenses/>.

-- dnsjit.output.dnscli
-- DNS aware UDP/TCP/TLS client
--   local dnscli = require("dnsjit.output.dnscli")
-- .SS UDP Receiver Chain
--   local output = dnscli.new(dnscli.UDP)
--   output:connect("127.0.0.1", "53")
--   input:receiver(output)
-- .SS TCP Nonblocking
--   local output = dnscli.new(dnscli.TCP + dnscli.NONBLOCKING)
--   output:send(object)
--
-- The DNS client can a
-- .I core.object.dns
-- or a
-- .I core.object.payload
-- object via the receiver interface or using
-- .I send()
-- and send it as DNS query after which it can receive the response by using
-- the producer interface.
-- If the object being sent is a
-- .I core.object.dns
-- then it will look at
-- .I includes_dnslen
-- attribute and depending on the protocol it will disregard, include or send
-- the DNS length as an extra packet.
-- If the object being sent is a
-- .I core.object.payload
-- then no special handling will be done and it will be sent as is.
-- When receiving responses the producer interface will generate
-- .I core.object.payload
-- objects which may include the DNS length depending on the protocol used and
-- must be handled by the caller.
-- .SS MODES
-- These transport modes and options are available when creating a new Dnscli
-- output.
-- .TP
-- UDP
-- Create an output using UDP.
-- .TP
-- TCP
-- Create an output using TCP.
-- .TP
-- TLS
-- Create an output using TCP and encrypt it with TLS.
-- .TP
-- NONBLOCKING
-- Make the client nonblocking, see
-- .I send()
-- and
-- .IR produce() .
module(...,package.seeall)

require("dnsjit.output.dnscli_h")
local ffi = require("ffi")
local C = ffi.C

local t_name = "output_dnscli_t"
local output_dnscli_t = ffi.typeof(t_name)
local Dnscli = {
    NONBLOCKING = 0x1,
    UDP = 0x10,
    TCP = 0x20,
    TLS = 0x30,
}

-- Create a new Dnscli output.
function Dnscli.new(mode)
    local self = {
        obj = output_dnscli_t(),
    }
    C.output_dnscli_init(self.obj, mode)
    ffi.gc(self.obj, C.output_dnscli_destroy)
    return setmetatable(self, { __index = Dnscli })
end

-- Set or return the timeout used for sending and reciving, must be used before
-- .IR connect() .
function Dnscli:timeout(seconds, nanoseconds)
    if seconds == nil and nanoseconds == nil then
        return self.obj.timeout
    end
    if nanoseconds == nil then
        nanoseconds = 0
    end
    self.obj.timeout.sec = seconds
    self.obj.timeout.nsec = nanoseconds
end

-- Connect to the
-- .I host
-- and
-- .I port
-- and return 0 if successful.
function Dnscli:connect(host, port)
    return C.output_dnscli_connect(self.obj, host, port)
end

-- Return if nonblocking mode is on (true) or off (false).
function Dnscli:nonblocking()
    if self.obj.nonblocking == 1 then
        return true
    end
    return false
end

-- Send an object and optionally continue sending after
-- .I sent
-- bytes.
-- Unlike the receive interface this function lets you know if the sending was
-- successful or not which might be needed on nonblocking connections.
-- Returns -2 on error, -1 if interrupted, timed out or unable to send due to
-- nonblocking, or the number of bytes sent.
-- .B Note
-- the counters for sent, received, errors and timeouts are not affected by
-- this function.
function Dnscli:send(object, sent)
    if sent == nil then
        sent = 0
    end
    return C.output_dnscli_send(self.obj, object, sent)
end

-- Return the C functions and context for receiving objects, these objects
-- will be sent.
function Dnscli:receive()
    return C.output_dnscli_receiver(self.obj), self.obj
end

-- Return the C functions and context for producing objects, these objects
-- are received.
-- If nonblocking mode is enabled the producer will return a payload object
-- with length zero if there was nothing to receive.
-- If nonblocking mode is disabled the producer will wait for data and if
-- timed out (see
-- .IR timeout )
-- it will return a payload object with length zero.
-- The producer returns nil on error.
function Dnscli:produce()
    return C.output_dnscli_producer(self.obj), self.obj
end

-- Return the number of "packets" sent, actually the number of completely sent
-- payloads.
function Dnscli:packets()
    return tonumber(self.obj.pkts)
end

-- Return the number of "packets" received, actually the number of successful
-- calls to
-- .IR recvfrom (2)
-- that returned data.
function Dnscli:received()
    return tonumber(self.obj.pkts_recv)
end

-- Return the number of errors when sending or receiving.
function Dnscli:errors()
    return tonumber(self.obj.errs)
end

-- Return the number of timeouts when sending or receiving.
function Dnscli:timeouts()
    return tonumber(self.obj.timeouts)
end

-- core.object.dns (3),
-- core.object.payload (3),
-- core.timespec (3)
return Dnscli