Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/rust/src/dns/lua.rs
Line
Count
Source
1
/* Copyright (C) 2017 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
use std::os::raw::c_int;
19
20
use crate::dns::dns::*;
21
use crate::dns::log::*;
22
use crate::lua::*;
23
24
#[no_mangle]
25
0
pub extern "C" fn SCDnsLuaGetTxId(clua: &mut CLuaState, tx: &mut DNSTransaction) -> c_int {
26
0
    let lua = LuaState { lua: clua };
27
28
0
    lua.pushinteger(tx.tx_id() as i64);
29
0
    return 1;
30
0
}
31
32
#[no_mangle]
33
0
pub extern "C" fn SCDnsLuaGetRrname(clua: &mut CLuaState, tx: &mut DNSTransaction) -> c_int {
34
0
    let lua = LuaState { lua: clua };
35
36
0
    if let Some(request) = &tx.request {
37
0
        if let Some(query) = request.queries.first() {
38
0
            lua.pushstring(&String::from_utf8_lossy(&query.name.value));
39
0
            return 1;
40
0
        }
41
0
    } else if let Some(response) = &tx.response {
42
0
        if let Some(query) = response.queries.first() {
43
0
            lua.pushstring(&String::from_utf8_lossy(&query.name.value));
44
0
            return 1;
45
0
        }
46
0
    }
47
48
0
    return 0;
49
0
}
50
51
#[no_mangle]
52
0
pub extern "C" fn SCDnsLuaGetRcode(clua: &mut CLuaState, tx: &mut DNSTransaction) -> c_int {
53
0
    let lua = LuaState { lua: clua };
54
0
    lua.pushinteger(tx.rcode() as i64);
55
0
    return 1;
56
0
}
57
58
#[no_mangle]
59
0
pub extern "C" fn SCDnsLuaGetRcodeString(clua: &mut CLuaState, tx: &mut DNSTransaction) -> c_int {
60
0
    let lua = LuaState { lua: clua };
61
0
    lua.pushstring(&dns_rcode_string(tx.rcode()));
62
0
    return 1;
63
0
}
64
65
#[no_mangle]
66
0
pub extern "C" fn SCDnsLuaGetQueryTable(clua: &mut CLuaState, tx: &mut DNSTransaction) -> c_int {
67
0
    let lua = LuaState { lua: clua };
68
69
0
    let mut i: i64 = 0;
70
71
    // Create table now to be consistent with C that always returns
72
    // table even in the absence of any authorities.
73
0
    lua.newtable();
74
75
    // We first look in the request for queries. However, if there is
76
    // no request, check the response for queries.
77
0
    if let Some(request) = &tx.request {
78
0
        for query in &request.queries {
79
0
            lua.pushinteger(i);
80
0
            i += 1;
81
0
82
0
            lua.newtable();
83
0
84
0
            lua.pushstring("type");
85
0
            lua.pushstring(&dns_rrtype_string(query.rrtype));
86
0
            lua.settable(-3);
87
0
88
0
            lua.pushstring("rrname");
89
0
            lua.pushstring(&String::from_utf8_lossy(&query.name.value));
90
0
            lua.settable(-3);
91
0
92
0
            lua.settable(-3);
93
0
        }
94
0
    } else if let Some(response) = &tx.response {
95
0
        for query in &response.queries {
96
0
            lua.pushinteger(i);
97
0
            i += 1;
98
0
99
0
            lua.newtable();
100
0
101
0
            lua.pushstring("type");
102
0
            lua.pushstring(&dns_rrtype_string(query.rrtype));
103
0
            lua.settable(-3);
104
0
105
0
            lua.pushstring("rrname");
106
0
            lua.pushstring(&String::from_utf8_lossy(&query.name.value));
107
0
            lua.settable(-3);
108
0
109
0
            lua.settable(-3);
110
0
        }
111
0
    }
112
113
    // Again, always return 1 to be consistent with C, even if the
114
    // table is empty.
115
0
    return 1;
116
0
}
117
118
#[no_mangle]
119
0
pub extern "C" fn SCDnsLuaGetAnswerTable(clua: &mut CLuaState, tx: &mut DNSTransaction) -> c_int {
120
0
    let lua = LuaState { lua: clua };
121
122
0
    let mut i: i64 = 0;
123
124
    // Create table now to be consistent with C that always returns
125
    // table even in the absence of any authorities.
126
0
    lua.newtable();
127
128
0
    if let Some(response) = &tx.response {
129
0
        for answer in &response.answers {
130
0
            lua.pushinteger(i);
131
0
            i += 1;
132
133
0
            lua.newtable();
134
0
            lua.pushstring("type");
135
0
            lua.pushstring(&dns_rrtype_string(answer.rrtype));
136
0
            lua.settable(-3);
137
138
0
            lua.pushstring("ttl");
139
0
            lua.pushinteger(answer.ttl as i64);
140
0
            lua.settable(-3);
141
142
0
            lua.pushstring("rrname");
143
0
            lua.pushstring(&String::from_utf8_lossy(&answer.name.value));
144
0
            lua.settable(-3);
145
146
            // All rdata types are pushed to "addr" for backwards compatibility
147
0
            match &answer.data {
148
0
                DNSRData::A(ref bytes) | DNSRData::AAAA(ref bytes) => {
149
0
                    if !bytes.is_empty() {
150
0
                        lua.pushstring("addr");
151
0
                        lua.pushstring(&dns_print_addr(bytes));
152
0
                        lua.settable(-3);
153
0
                    }
154
                }
155
0
                DNSRData::CNAME(name)
156
0
                | DNSRData::MX(name)
157
0
                | DNSRData::NS(name)
158
0
                | DNSRData::PTR(name) => {
159
0
                    if !name.value.is_empty() {
160
0
                        lua.pushstring("addr");
161
0
                        lua.pushstring(&String::from_utf8_lossy(&name.value));
162
0
                        lua.settable(-3);
163
0
                    }
164
                }
165
0
                DNSRData::TXT(ref txt) => {
166
0
                    if !txt.is_empty() {
167
0
                        lua.pushstring("addr");
168
0
                        let combined = txt
169
0
                            .iter()
170
0
                            .map(|s| String::from_utf8_lossy(s))
171
0
                            .collect::<Vec<_>>()
172
0
                            .join(" ");
173
0
                        lua.pushstring(&combined);
174
0
                        lua.settable(-3);
175
0
                    }
176
                }
177
0
                DNSRData::NULL(ref bytes)
178
0
                | DNSRData::Unknown(ref bytes) => {
179
0
                    if !bytes.is_empty() {
180
0
                        lua.pushstring("addr");
181
0
                        lua.pushstring(&String::from_utf8_lossy(bytes));
182
0
                        lua.settable(-3);
183
0
                    }
184
                }
185
0
                DNSRData::SOA(ref soa) => {
186
0
                    if !soa.mname.value.is_empty() {
187
0
                        lua.pushstring("addr");
188
0
                        lua.pushstring(&String::from_utf8_lossy(&soa.mname.value));
189
0
                        lua.settable(-3);
190
0
                    }
191
                }
192
0
                DNSRData::SSHFP(ref sshfp) => {
193
0
                    lua.pushstring("addr");
194
0
                    lua.pushstring(&String::from_utf8_lossy(&sshfp.fingerprint));
195
0
                    lua.settable(-3);
196
0
                }
197
0
                DNSRData::SRV(ref srv) => {
198
0
                    lua.pushstring("addr");
199
0
                    lua.pushstring(&String::from_utf8_lossy(&srv.target.value));
200
0
                    lua.settable(-3);
201
0
                }
202
0
                DNSRData::OPT(ref opt) => {
203
0
                    if !opt.is_empty() {
204
0
                        lua.pushstring("addr");
205
0
                        for option in opt.iter() {
206
0
                            lua.pushstring(&String::from_utf8_lossy(&option.code.to_be_bytes()));
207
0
                            lua.pushstring(&String::from_utf8_lossy(&option.data));
208
0
                        }
209
0
                        lua.settable(-3);
210
0
                    }
211
                }
212
            }
213
0
            lua.settable(-3);
214
        }
215
0
    }
216
217
    // Again, always return 1 to be consistent with C, even if the
218
    // table is empty.
219
0
    return 1;
220
0
}
221
222
#[no_mangle]
223
0
pub extern "C" fn SCDnsLuaGetAuthorityTable(
224
0
    clua: &mut CLuaState, tx: &mut DNSTransaction,
225
0
) -> c_int {
226
0
    let lua = LuaState { lua: clua };
227
228
0
    let mut i: i64 = 0;
229
230
    // Create table now to be consistent with C that always returns
231
    // table even in the absence of any authorities.
232
0
    lua.newtable();
233
234
0
    if let Some(response) = &tx.response {
235
0
        for answer in &response.authorities {
236
0
            lua.pushinteger(i);
237
0
            i += 1;
238
0
239
0
            lua.newtable();
240
0
            lua.pushstring("type");
241
0
            lua.pushstring(&dns_rrtype_string(answer.rrtype));
242
0
            lua.settable(-3);
243
0
244
0
            lua.pushstring("ttl");
245
0
            lua.pushinteger(answer.ttl as i64);
246
0
            lua.settable(-3);
247
0
248
0
            lua.pushstring("rrname");
249
0
            lua.pushstring(&String::from_utf8_lossy(&answer.name.value));
250
0
            lua.settable(-3);
251
0
252
0
            lua.settable(-3);
253
0
        }
254
0
    }
255
256
    // Again, always return 1 to be consistent with C, even if the
257
    // table is empty.
258
0
    return 1;
259
0
}