feat(discovery): enhance handlePacket parser for real peer identification
- Parse DNS Answer section properly (skip Question section) - Extract PTR record RDATA with node_id in hex format - Convert hex string back to bytes for peer_id - Filter own node_id to avoid self-discovery - Add logging for peer discovery events - Fix peer_id size to match announce format ([8]u8) M0.0.1 Enhancement: Nodes now extract real peer IDs from mDNS packets instead of using mock_did. Ready for two-node discovery testing.
This commit is contained in:
parent
dfb10e52ef
commit
44defaacd9
|
|
@ -154,34 +154,138 @@ pub const DiscoveryService = struct {
|
|||
}
|
||||
|
||||
/// Parse an incoming mDNS packet and update the peer table
|
||||
/// Enhanced parser: Extracts real node_id from PTR RDATA
|
||||
pub fn handlePacket(self: *DiscoveryService, peer_table: anytype, buf: []const u8, sender: net.Address) !void {
|
||||
if (buf.len < 12) return; // Too small
|
||||
if (buf.len < 12) return; // Too small for DNS header
|
||||
|
||||
// Skip Header (12 bytes)
|
||||
// Parse DNS Header
|
||||
const answer_count = std.mem.readInt(u16, buf[6..8], .big);
|
||||
if (answer_count == 0) return;
|
||||
|
||||
// Skip Question section if any (simplified: we expect responses to our query or gratuitous responses)
|
||||
// For local discovery, we mostly care about Answers.
|
||||
// Skip Question section (after header)
|
||||
const question_count = std.mem.readInt(u16, buf[4..6], .big);
|
||||
var offset: usize = 12;
|
||||
|
||||
// This is a VERY MINIMAL parser for Week 28.
|
||||
// It looks for the "_libertaria._udp.local" string and assumes the following PTR.
|
||||
if (std.mem.indexOf(u8, buf, "_libertaria")) |_| {
|
||||
// Found a Libertaria record!
|
||||
// In a real implementation, we'd parse SRV/TXT for the actual port and DID.
|
||||
// For MVP, if we receive a Libertaria-tagged packet, we trust the sender's IP.
|
||||
// (Port is still tricky since discovery is on 5353 but service is on 8710).
|
||||
// Skip Question section
|
||||
var i: u16 = 0;
|
||||
while (i < question_count) : (i += 1) {
|
||||
// Skip NAME
|
||||
while (offset < buf.len) {
|
||||
const label_len = buf[offset];
|
||||
offset += 1;
|
||||
if (label_len == 0) break;
|
||||
if (label_len & 0xC0 == 0xC0) {
|
||||
// Pointer, skip 2 bytes total
|
||||
offset += 1;
|
||||
break;
|
||||
}
|
||||
offset += label_len;
|
||||
}
|
||||
// Skip TYPE(2) + CLASS(2)
|
||||
if (offset + 4 > buf.len) break;
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
// TODO: Extract DID from TXT record
|
||||
var mock_did = [_]u8{0} ** 8;
|
||||
@memcpy(mock_did[0..4], "NODE");
|
||||
// Parse Answer section
|
||||
i = 0;
|
||||
while (i < answer_count) : (i += 1) {
|
||||
if (offset + 10 > buf.len) break; // Need at least: NAME + TYPE(2) + CLASS(2) + TTL(4) + RDLEN(2)
|
||||
|
||||
// We assume the peer is running on its default port or we need SRV record.
|
||||
// For now, use the sender's IP but the standard port.
|
||||
var peer_addr = sender;
|
||||
peer_addr.setPort(self.port); // Fallback to our configured port if unknown
|
||||
// Parse NAME (could be pointer or full name)
|
||||
var found_libertaria = false;
|
||||
while (offset < buf.len) {
|
||||
const label_len = buf[offset];
|
||||
if (label_len == 0) {
|
||||
offset += 1;
|
||||
break;
|
||||
}
|
||||
if (label_len & 0xC0 == 0xC0) {
|
||||
// Pointer, skip 2 bytes total
|
||||
offset += 2;
|
||||
break;
|
||||
}
|
||||
offset += 1;
|
||||
// Check for "_libertaria" in label
|
||||
if (label_len >= 11 and offset + label_len <= buf.len) {
|
||||
const label = buf[offset..][0..label_len];
|
||||
if (std.mem.indexOf(u8, label, "_libertaria")) |_| {
|
||||
found_libertaria = true;
|
||||
}
|
||||
}
|
||||
offset += label_len;
|
||||
}
|
||||
|
||||
try peer_table.updatePeer(mock_did, peer_addr);
|
||||
// Read TYPE (2) + CLASS (2) + TTL (4) + RDLEN (2)
|
||||
if (offset + 10 > buf.len) break;
|
||||
const type_val = std.mem.readInt(u16, buf[offset..][0..2], .big);
|
||||
offset += 2; // Skip TYPE
|
||||
offset += 2; // Skip CLASS
|
||||
offset += 4; // Skip TTL
|
||||
const rdlen = std.mem.readInt(u16, buf[offset..][0..2], .big);
|
||||
offset += 2;
|
||||
|
||||
// Check if this is a PTR record with Libertaria
|
||||
if (found_libertaria and type_val == 12) { // Type 12 = PTR
|
||||
const rdata_start = offset;
|
||||
const rdata_end = offset + @as(usize, rdlen);
|
||||
if (rdata_end > buf.len) break;
|
||||
|
||||
// Extract node_id from PTR RDATA (format: dde9b2d5c247._libertaria._udp.local)
|
||||
var extracted_hex = [_]u8{0} ** 16;
|
||||
var hex_len: usize = 0;
|
||||
|
||||
var name_offset = rdata_start;
|
||||
while (name_offset < rdata_end and hex_len < 16) {
|
||||
const label_len = buf[name_offset];
|
||||
if (label_len == 0 or label_len & 0xC0 == 0xC0) break;
|
||||
if (label_len > 16) break; // Too long for hex
|
||||
|
||||
// Check if this is the hex-encoded node_id
|
||||
const label = buf[name_offset + 1 ..][0..label_len];
|
||||
var is_hex = true;
|
||||
for (label) |c| {
|
||||
if (!((c >= '0' and c <= '9') or (c >= 'a' and c <= 'f'))) {
|
||||
is_hex = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_hex) {
|
||||
@memcpy(extracted_hex[hex_len..][0..label.len], label);
|
||||
hex_len += label.len;
|
||||
}
|
||||
|
||||
name_offset += 1 + label_len;
|
||||
}
|
||||
|
||||
// Parse hex string back to bytes
|
||||
var peer_id = [_]u8{0} ** 8; // 8-byte peer ID (matches announce format)
|
||||
var byte_pos: usize = 0;
|
||||
for (0..@min(hex_len / 2, 8)) |j| {
|
||||
if (j * 2 + 1 < hex_len) {
|
||||
const high = try std.fmt.charToDigit(extracted_hex[j * 2], 16);
|
||||
const low = try std.fmt.charToDigit(extracted_hex[j * 2 + 1], 16);
|
||||
if (byte_pos < peer_id.len) {
|
||||
peer_id[byte_pos] = @as(u8, @truncate((high << 4) | low));
|
||||
byte_pos += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip our own node_id (compare first 8 bytes)
|
||||
if (!std.mem.eql(u8, &peer_id, self.node_id[0..8])) {
|
||||
// We assume the peer is running on its default port or we need SRV record.
|
||||
// For now, use the sender's IP but the standard port.
|
||||
var peer_addr = sender;
|
||||
peer_addr.setPort(self.port); // Fallback to our configured port if unknown
|
||||
|
||||
std.log.info("Discovery: Added peer {x} from port {d}", .{peer_id[0..byte_pos], sender.getPort()});
|
||||
try peer_table.updatePeer(peer_id, peer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip RDATA
|
||||
offset += @as(usize, rdlen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue