Skip to content

Instantly share code, notes, and snippets.

@cr0nx
Created January 15, 2025 12:58
Show Gist options
  • Select an option

  • Save cr0nx/05e41c079af0311e44360961700ed265 to your computer and use it in GitHub Desktop.

Select an option

Save cr0nx/05e41c079af0311e44360961700ed265 to your computer and use it in GitHub Desktop.
nslookup.zig - Simple Linux nslookup BOF ready to use with bof-launcher
const std = @import("std");
const beacon = @import("bof_api").beacon;
const posix = @import("bof_api").posix;
const net = std.net;
const mem = std.mem;
const DNS_PORT = 53;
const MAX_DNS_PACKET_SIZE = 512;
const DNS_QUERY_CLASS_IN = 1;
const DNS_QUERY_TYPE_A = 1;
const DNS_HEADER_SIZE = 12;
const DnsHeader = extern struct {
id: u16,
flags: u16,
qdcount: u16,
ancount: u16,
nscount: u16,
arcount: u16,
};
fn buildDnsQuery(allocator: mem.Allocator, domain: []const u8) ![]u8 {
var packet = std.ArrayList(u8).init(allocator);
defer packet.deinit();
// DNS Header
const header = DnsHeader{
.id = 0x1234,
.flags = 0x0100, // Standard query
.qdcount = 0x0100, // One question
.ancount = 0x0000,
.nscount = 0x0000,
.arcount = 0x0000,
};
// Write header
try packet.appendSlice(mem.asBytes(&header));
// Convert domain to DNS format (length-prefixed labels)
var labels = mem.split(u8, domain, ".");
while (labels.next()) |label| {
try packet.append(@as(u8, @intCast(label.len)));
try packet.appendSlice(label);
}
try packet.append(0);
// Query type (A record)
try packet.append(0);
try packet.append(DNS_QUERY_TYPE_A);
// Query class (IN)
try packet.append(0);
try packet.append(DNS_QUERY_CLASS_IN);
return packet.toOwnedSlice();
}
fn parseDnsResponse(response: []const u8) void {
if (response.len < DNS_HEADER_SIZE) {
_ = beacon.printf(0, "Response too short\n");
return;
}
const header = @as(*const DnsHeader, @ptrCast(@alignCast(response.ptr))).*;
const answer_count = @byteSwap(header.ancount);
if (answer_count == 0) {
_ = beacon.printf(0, "No answers found\n");
return;
}
var pos: usize = DNS_HEADER_SIZE;
// Skip question section
while (pos < response.len and response[pos] != 0) {
pos += @as(usize, @intCast(response[pos])) + 1;
}
pos += 5; // Skip null terminator and QTYPE/QCLASS
// Parse answers
var i: u16 = 0;
while (i < answer_count and pos + 12 <= response.len) : (i += 1) {
// Skip name field (compressed or uncompressed)
if ((response[pos] & 0xC0) == 0xC0) {
pos += 2;
} else {
while (pos < response.len and response[pos] != 0) {
pos += @as(usize, @intCast(response[pos])) + 1;
}
pos += 1;
}
// Read type, class, TTL, and data length
const rtype = (@as(u16, response[pos]) << 8) | response[pos + 1];
pos += 8; // Skip to rdlength
const rdlength = (@as(u16, response[pos]) << 8) | response[pos + 1];
pos += 2;
// Handle A records
if (rtype == DNS_QUERY_TYPE_A and rdlength == 4 and pos + 4 <= response.len) {
_ = beacon.printf(0, "IP Address: %d.%d.%d.%d\n",
response[pos],
response[pos + 1],
response[pos + 2],
response[pos + 3]
);
}
pos += rdlength;
}
}
pub export fn go(args: ?[*]u8, args_len: i32) callconv(.C) u8 {
if (args_len == 0) {
return 1; // err 1: no argument provided
}
var opt_len: i32 = 0;
const allocator = std.heap.page_allocator;
var parser = beacon.datap{};
// Parse domain argument
beacon.dataParse(&parser, args, args_len);
const domain = beacon.dataExtract(&parser, &opt_len);
const sDomain = domain.?[0..@as(usize, @intCast(opt_len - 1))];
// Create UDP socket
const sockfd = std.posix.socket(
std.posix.AF.INET,
std.posix.SOCK.DGRAM,
0,
) catch return 1;
defer std.posix.close(sockfd);
// Set up DNS server address (using 8.8.8.8 as example)
var server_addr = net.Address.initIp4([4]u8{ 8, 8, 8, 8 }, DNS_PORT);
// Build DNS query
const query = buildDnsQuery(allocator, sDomain) catch return 2;
defer allocator.free(query);
_ = beacon.printf(0, "Looking up %s...\n", sDomain.ptr);
// Send query
_ = std.posix.sendto(
sockfd,
query,
0,
&server_addr.any,
server_addr.getOsSockLen(),
) catch return 3;
// Receive response
var response_buf: [MAX_DNS_PACKET_SIZE]u8 = undefined;
var src_addr: net.Address = undefined;
var addrlen: std.posix.socklen_t = @sizeOf(net.Address);
const received = std.posix.recvfrom(
sockfd,
&response_buf,
0,
&src_addr.any,
&addrlen,
) catch return 4;
if (received > 0) {
parseDnsResponse(response_buf[0..received]);
} else {
_ = beacon.printf(0, "No response received\n");
return 5;
}
return 0;
}
@cr0nx
Copy link
Author

cr0nx commented Jun 4, 2025

Find more interesting stuff here: https://edu.defensive-security.com/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment