Compare commits
3 Commits
cb62ac2f63
...
390eaebd90
Author | SHA1 | Date |
---|---|---|
LeRoyce Pearson | 390eaebd90 | |
LeRoyce Pearson | ff7e3c70bf | |
LeRoyce Pearson | fc166b9ee9 |
|
@ -3,6 +3,7 @@ pub const HBox = @import("./Element/HBox.zig");
|
|||
pub const Link = @import("./Element/Link.zig");
|
||||
pub const Page = @import("./Element/Page.zig");
|
||||
pub const Pile = @import("./Element/Pile.zig");
|
||||
pub const ServerList = @import("./Element/ServerList.zig");
|
||||
pub const Text = @import("./Element/Text.zig");
|
||||
pub const View = @import("./Element/View.zig");
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
allocator: std.mem.Allocator,
|
||||
discovered: *Discovered,
|
||||
|
||||
pub const Discovered = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
servers: std.StringArrayHashMapUnmanaged(void) = .{},
|
||||
};
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator, discovered: *Discovered) !*@This() {
|
||||
const this = try allocator.create(@This());
|
||||
errdefer allocator.destroy(this);
|
||||
|
||||
this.* = .{
|
||||
.allocator = allocator,
|
||||
.discovered = discovered,
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
pub fn element(this: *@This()) Element {
|
||||
return Element{
|
||||
.pointer = this,
|
||||
.interface = &Element.Interface{
|
||||
.destroy = &element_destroy,
|
||||
.minimum_size = &element_minimum_size,
|
||||
.render = &element_render,
|
||||
.event = &element_event,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn element_destroy(pointer: ?*anyopaque) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||
this.allocator.destroy(this);
|
||||
}
|
||||
|
||||
pub fn element_minimum_size(pointer: ?*anyopaque, render_resources: Element.RenderResources) [2]f32 {
|
||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||
_ = this;
|
||||
return render_resources.font.textSize("example_server.xyz:5711", 1);
|
||||
}
|
||||
|
||||
pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||
_ = max;
|
||||
_ = render_resources;
|
||||
|
||||
var pos = min;
|
||||
|
||||
this.discovered.mutex.lock();
|
||||
defer this.discovered.mutex.unlock();
|
||||
for (this.discovered.servers.keys()) |url| {
|
||||
const text_size = canvas.writeText(pos, url, .{});
|
||||
pos[1] += text_size[1];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
|
||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||
_ = this;
|
||||
_ = event;
|
||||
return null;
|
||||
}
|
||||
|
||||
const Element = @import("../Element.zig");
|
||||
const assets = @import("../assets.zig");
|
||||
|
||||
const seizer = @import("seizer");
|
||||
const gl = seizer.gl;
|
||||
const std = @import("std");
|
174
src/LocalUI.zig
174
src/LocalUI.zig
|
@ -24,9 +24,18 @@ fn main_menu_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
|||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||
}
|
||||
|
||||
const SERVICE_NAME = "_rummy._udp.local.";
|
||||
const SIOCGIFCONF = 0x8912;
|
||||
const SIOCGIFADDR = 0x8915;
|
||||
|
||||
const JoinMultiplayerGame = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
discovery_thread: std.Thread,
|
||||
running: *std.atomic.Value(bool),
|
||||
|
||||
discovered: *Element.ServerList.Discovered,
|
||||
|
||||
const Server = struct { url: []const u8, port: u16 };
|
||||
|
||||
pub const INTERFACE = &Handler.Interface{
|
||||
.create = &create,
|
||||
|
@ -38,11 +47,21 @@ const JoinMultiplayerGame = struct {
|
|||
const this = try allocator.create(@This());
|
||||
errdefer allocator.destroy(this);
|
||||
|
||||
const discovery_thread = std.Thread.spawn(.{}, find_local_games_thread, .{allocator}) catch return error.OutOfMemory;
|
||||
const discovered = try allocator.create(Element.ServerList.Discovered);
|
||||
errdefer allocator.destroy(discovered);
|
||||
discovered.* = .{ .allocator = allocator };
|
||||
|
||||
const running = try allocator.create(std.atomic.Value(bool));
|
||||
errdefer allocator.destroy(running);
|
||||
running.store(true, .Monotonic);
|
||||
|
||||
const discovery_thread = std.Thread.spawn(.{}, find_local_games_thread, .{ running, discovered }) catch return error.OutOfMemory;
|
||||
|
||||
this.* = .{
|
||||
.allocator = allocator,
|
||||
.discovery_thread = discovery_thread,
|
||||
.running = running,
|
||||
.discovered = discovered,
|
||||
};
|
||||
|
||||
return this;
|
||||
|
@ -50,15 +69,17 @@ const JoinMultiplayerGame = struct {
|
|||
|
||||
fn handler(pointer: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||
_ = this;
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(request.allocator);
|
||||
errdefer arena.deinit();
|
||||
|
||||
var text = try Element.Text.create(arena.allocator(), try std.fmt.allocPrint(arena.allocator(), "Looking for multiplayer games", .{}));
|
||||
|
||||
var server_list = try Element.ServerList.create(arena.allocator(), this.discovered);
|
||||
|
||||
var page = try Element.Page.create(request.allocator);
|
||||
try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, text.element());
|
||||
try page.addElement(.{ 0.5, 0.7 }, .{ 0.5, 0.5 }, server_list.element());
|
||||
|
||||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||
}
|
||||
|
@ -66,12 +87,23 @@ const JoinMultiplayerGame = struct {
|
|||
fn deinit(pointer: ?*anyopaque) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||
|
||||
this.running.store(false, .Monotonic);
|
||||
|
||||
this.discovery_thread.join();
|
||||
for (this.discovered.servers.keys()) |url| {
|
||||
this.discovered.allocator.free(url);
|
||||
}
|
||||
this.discovered.servers.deinit(this.discovered.allocator);
|
||||
|
||||
this.allocator.destroy(this.discovered);
|
||||
this.allocator.destroy(this.running);
|
||||
|
||||
this.allocator.destroy(this);
|
||||
}
|
||||
|
||||
fn find_local_games_thread(allocator: std.mem.Allocator) !void {
|
||||
const buffer = try allocator.alloc(u8, 2048);
|
||||
defer allocator.free(buffer);
|
||||
fn find_local_games_thread(running: *std.atomic.Value(bool), discovered: *Element.ServerList.Discovered) !void {
|
||||
const buffer = try discovered.allocator.alloc(u8, 2048);
|
||||
defer discovered.allocator.free(buffer);
|
||||
|
||||
const log = std.log.scoped(.mdns);
|
||||
log.info("mdns discovery service started", .{});
|
||||
|
@ -84,15 +116,14 @@ const JoinMultiplayerGame = struct {
|
|||
const mdns_ipv6_socket = c.mdns_socket_open_ipv6(null);
|
||||
defer c.mdns_socket_close(mdns_ipv6_socket);
|
||||
|
||||
const service_name = "rummy._udp.local.";
|
||||
for (&[_]c_int{ mdns_ipv4_socket, mdns_ipv6_socket }) |socket| {
|
||||
switch (c.mdns_query_send(socket, c.MDNS_RECORDTYPE_PTR, service_name.ptr, service_name.len, buffer.ptr, buffer.len, 0)) {
|
||||
switch (c.mdns_query_send(socket, c.MDNS_RECORDTYPE_PTR, SERVICE_NAME.ptr, SERVICE_NAME.len, buffer.ptr, buffer.len, 0)) {
|
||||
0 => {},
|
||||
else => |err_code| log.warn("Failed to send question; error code = {}", .{err_code}),
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (running.load(.Monotonic)) {
|
||||
var pollfds = [_]std.os.pollfd{
|
||||
.{ .fd = mdns_ipv4_socket, .events = std.os.POLL.IN, .revents = undefined },
|
||||
.{ .fd = mdns_ipv6_socket, .events = std.os.POLL.IN, .revents = undefined },
|
||||
|
@ -100,11 +131,11 @@ const JoinMultiplayerGame = struct {
|
|||
_ = std.os.poll(&pollfds, 1000) catch break;
|
||||
for (pollfds) |pollfd| {
|
||||
if (pollfd.revents & std.os.POLL.IN == 0) continue;
|
||||
_ = c.mdns_query_recv(pollfd.fd, buffer.ptr, buffer.len, &find_local_games_thread_query_callback, null, 0);
|
||||
_ = c.mdns_query_recv(pollfd.fd, buffer.ptr, buffer.len, &find_local_games_thread_query_callback, discovered, 0);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("mdns service stopping", .{});
|
||||
log.info("mdns discovery service stopping", .{});
|
||||
}
|
||||
|
||||
fn find_local_games_thread_query_callback(
|
||||
|
@ -126,8 +157,9 @@ const JoinMultiplayerGame = struct {
|
|||
) callconv(.C) c_int {
|
||||
const log = std.log.scoped(.mdns);
|
||||
|
||||
const discovered: *Element.ServerList.Discovered = @ptrCast(@alignCast(userdata));
|
||||
|
||||
_ = rclass;
|
||||
_ = userdata;
|
||||
_ = ttl;
|
||||
_ = name_length;
|
||||
_ = sock;
|
||||
|
@ -159,9 +191,6 @@ const JoinMultiplayerGame = struct {
|
|||
c.MDNS_RECORDTYPE_TXT => "TXT",
|
||||
else => "???",
|
||||
};
|
||||
if (rtype == c.MDNS_RECORDTYPE_PTR) {
|
||||
log.info("{?} : {s} {s} \"{}\"", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(name) });
|
||||
}
|
||||
|
||||
switch (rtype) {
|
||||
c.MDNS_RECORDTYPE_PTR => {
|
||||
|
@ -169,6 +198,30 @@ const JoinMultiplayerGame = struct {
|
|||
const namestr = c.mdns_record_parse_ptr(data_ptr_opaque, data_len, record_offset, record_length, &namebuffer, namebuffer.len);
|
||||
log.info("{?} : {s} {s} \"{}\"", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(namestr.str[0..namestr.length]) });
|
||||
},
|
||||
c.MDNS_RECORDTYPE_SRV => {
|
||||
var strbuffer: [128]u8 = undefined;
|
||||
const record = c.mdns_record_parse_srv(data_ptr_opaque, data_len, record_offset, record_length, &strbuffer, strbuffer.len);
|
||||
log.info("{?} : {s} {s} \"{}\":{}", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(record.name.str[0..record.name.length]), record.port });
|
||||
|
||||
const url = std.fmt.allocPrint(discovered.allocator, "{s}:{}", .{ record.name.str[0..record.name.length], record.port }) catch unreachable;
|
||||
discovered.mutex.lock();
|
||||
defer discovered.mutex.unlock();
|
||||
|
||||
const gop = discovered.servers.getOrPut(discovered.allocator, url) catch unreachable;
|
||||
if (gop.found_existing) {
|
||||
discovered.allocator.free(url);
|
||||
}
|
||||
},
|
||||
c.MDNS_RECORDTYPE_A => {
|
||||
var std_address: std.net.Ip4Address = undefined;
|
||||
_ = c.mdns_record_parse_a(data_ptr_opaque, data_len, record_offset, record_length, @ptrCast(&std_address.sa));
|
||||
log.info("{?} : {s} {s} \"{}\" -> {}", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(name), std_address });
|
||||
},
|
||||
c.MDNS_RECORDTYPE_AAAA => {
|
||||
var std_address: std.net.Ip6Address = undefined;
|
||||
_ = c.mdns_record_parse_aaaa(data_ptr_opaque, data_len, record_offset, record_length, @ptrCast(&std_address.sa));
|
||||
log.info("{?} : {s} {s} \"{}\" -> {}", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(name), std_address });
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return 0;
|
||||
|
@ -213,6 +266,7 @@ const HostMultiplayerGame = struct {
|
|||
if (server == null) {
|
||||
return error.OutOfMemory; // TODO: add better error message
|
||||
}
|
||||
errdefer c.enet_host_destroy(server);
|
||||
|
||||
// setup mDNS discovery service
|
||||
|
||||
|
@ -315,22 +369,6 @@ const HostMultiplayerGame = struct {
|
|||
interface: c_int,
|
||||
};
|
||||
|
||||
// TODO: get ipv4 and ipv6 adress of server to send out over mDNS
|
||||
// const SIOCGIFADDR = 0x8915;
|
||||
// var ifr: std.os.ifreq = undefined;
|
||||
// ifr.ifru.addr.family = std.os.AF.INET;
|
||||
// const interface_name = "wlp170s0";
|
||||
// @memset(&ifr.ifrn.name, 0);
|
||||
// @memcpy(ifr.ifrn.name[0..interface_name.len], interface_name);
|
||||
|
||||
// const ifr_ret_value = std.os.linux.getErrno(std.os.linux.ioctl(mdns_socket6, SIOCGIFADDR, @intFromPtr(&ifr)));
|
||||
// std.log.scoped(.mdns).debug("ioctl({}, SIOCGIFADDR, {*}) = {}", .{ mdns_socket6, &ifr, ifr_ret_value });
|
||||
|
||||
// std.os.ioctl_SIOCGIFINDEX(mdns_socket6, &ifr) catch unreachable;
|
||||
// const interface = ifr.ifru.ivalue;
|
||||
|
||||
// const mdns_ipv6_multicast_group = std.net.Address.parseIp6("ff02::fb", c.MDNS_PORT) catch unreachable;
|
||||
// var socklen = mdns_sock_addr6.getOsSockLen();
|
||||
std.os.setsockopt(
|
||||
mdns_socket6,
|
||||
std.os.IPPROTO.IPV6,
|
||||
|
@ -344,7 +382,6 @@ const HostMultiplayerGame = struct {
|
|||
const mdns_sock_addr6 = std.net.Address.parseIp6("::", c.MDNS_PORT) catch unreachable;
|
||||
var socklen = mdns_sock_addr6.getOsSockLen();
|
||||
std.os.bind(mdns_socket6, &mdns_sock_addr6.any, socklen) catch unreachable;
|
||||
// std.os.listen(mdns_socket6, self.kernel_backlog) catch unreachable;
|
||||
|
||||
var mdns_listen_addr: std.net.Address = undefined;
|
||||
std.os.getsockname(mdns_socket, &mdns_listen_addr.any, &socklen) catch unreachable;
|
||||
|
@ -355,12 +392,7 @@ const HostMultiplayerGame = struct {
|
|||
std.os.getsockname(mdns_socket6, &mdns_listen_addr6.any, &socklen) catch unreachable;
|
||||
const ipv6_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr6});
|
||||
|
||||
// // const SIOCGIFNAME = 0x8910;
|
||||
|
||||
// const ipv4_sockaddr: std.os.sockaddr.in = @bitCast(ifr.ifru.addr);
|
||||
// // const ipv4_bytes: [4]u8 = @bitCast(ipv4_sockaddr.addr);
|
||||
|
||||
const service_string = try allocator.dupe(u8, "rummy._udp.local.");
|
||||
const service_string = try allocator.dupe(u8, SERVICE_NAME);
|
||||
|
||||
var hostname_buffer: [std.os.HOST_NAME_MAX]u8 = undefined;
|
||||
const hostname = std.os.gethostname(&hostname_buffer) catch unreachable;
|
||||
|
@ -404,18 +436,36 @@ const HostMultiplayerGame = struct {
|
|||
.ttl = 0,
|
||||
});
|
||||
|
||||
// TODO: Add AAAA and A records to mdns service
|
||||
// // DNS A that maps "<hostname>.local." to the an ip address
|
||||
// const mdns_a_record = c.mdns_record_t{
|
||||
// .name = .{ .str = qualified_hostname.ptr, .length = qualified_hostname.len },
|
||||
// .type = c.MDNS_RECORDTYPE_A,
|
||||
// .data = .{ .a = .{
|
||||
// .addr = @bitCast(ipv4_sockaddr),
|
||||
// } },
|
||||
// .rclass = 0,
|
||||
// .ttl = 0,
|
||||
// };
|
||||
// try mdns_records.append(mdns_a_record);
|
||||
// get a list of addresses to populate A and AAAA DNS records
|
||||
const addresses = @import("./multiplayer.zig").getIPAddresses(allocator) catch return error.OutOfMemory;
|
||||
defer this.allocator.free(addresses);
|
||||
|
||||
for (addresses) |ip_address| {
|
||||
switch (ip_address) {
|
||||
.ipv4 => |ipv4| {
|
||||
try mdns_records.append(c.mdns_record_t{
|
||||
.name = .{ .str = qualified_hostname.ptr, .length = qualified_hostname.len },
|
||||
.type = c.MDNS_RECORDTYPE_A,
|
||||
.data = .{ .a = .{ .addr = .{
|
||||
.sin_addr = @bitCast(ipv4),
|
||||
} } },
|
||||
.rclass = 0,
|
||||
.ttl = 0,
|
||||
});
|
||||
},
|
||||
.ipv6 => |ipv6| {
|
||||
try mdns_records.append(c.mdns_record_t{
|
||||
.name = .{ .str = qualified_hostname.ptr, .length = qualified_hostname.len },
|
||||
.type = c.MDNS_RECORDTYPE_AAAA,
|
||||
.data = .{ .aaaa = .{ .addr = .{
|
||||
.sin6_addr = @bitCast(ipv6),
|
||||
} } },
|
||||
.rclass = 0,
|
||||
.ttl = 0,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const mdns_record_slice = try mdns_records.toOwnedSlice();
|
||||
errdefer allocator.free(mdns_record_slice);
|
||||
|
@ -482,6 +532,14 @@ const HostMultiplayerGame = struct {
|
|||
this.allocator.destroy(this);
|
||||
}
|
||||
|
||||
const nlmsghdr = extern struct {
|
||||
len: u32,
|
||||
type: u16,
|
||||
flags: u16,
|
||||
seq: u32,
|
||||
pid: u32,
|
||||
};
|
||||
|
||||
fn mdns_thread_main(this: *@This()) !void {
|
||||
const buffer = try this.allocator.alloc(u8, 2048);
|
||||
defer this.allocator.free(buffer);
|
||||
|
@ -516,7 +574,8 @@ const HostMultiplayerGame = struct {
|
|||
.{ .fd = this.mdns_socket6, .events = std.os.POLL.IN, .revents = undefined },
|
||||
};
|
||||
_ = std.os.poll(&pollfds, 1000) catch break;
|
||||
for (pollfds) |pollfd| {
|
||||
// listen for mdns messages
|
||||
for (pollfds[0..2]) |pollfd| {
|
||||
if (pollfd.revents & std.os.POLL.IN == 0) continue;
|
||||
_ = c.mdns_socket_listen(pollfd.fd, buffer.ptr, buffer.len, &mdns_thread_service_callback, this);
|
||||
}
|
||||
|
@ -562,7 +621,7 @@ const HostMultiplayerGame = struct {
|
|||
|
||||
const std_from: ?std.net.Address = if (from) |f| std.net.Address{ .any = @bitCast(f.*) } else null;
|
||||
|
||||
if (std.mem.eql(u8, name, DNS_SD)) {
|
||||
if (std.mem.eql(u8, name, DNS_SD) and (rtype == c.MDNS_RECORDTYPE_PTR or rtype == c.MDNS_RECORDTYPE_ANY)) {
|
||||
const answer = c.mdns_record_t{
|
||||
.name = .{ .str = name.ptr, .length = name.len },
|
||||
.type = c.MDNS_RECORDTYPE_PTR,
|
||||
|
@ -605,7 +664,7 @@ const HostMultiplayerGame = struct {
|
|||
log.warn("{s}:{} failed to answer query, error code = {}", .{ @src().file, @src().line, ret_val });
|
||||
}
|
||||
}
|
||||
} else if (std.mem.eql(u8, name, this.service_string)) {
|
||||
} else if (std.mem.eql(u8, name, this.service_string) and (rtype == c.MDNS_RECORDTYPE_PTR or rtype == c.MDNS_RECORDTYPE_ANY)) {
|
||||
const answer = this.mdns_records[0];
|
||||
const additional = this.mdns_records[1..];
|
||||
|
||||
|
@ -649,8 +708,9 @@ const HostMultiplayerGame = struct {
|
|||
log.warn("{s}:{} failed to answer query with multicast, error code = {}", .{ @src().file, @src().line, ret_val });
|
||||
}
|
||||
}
|
||||
} else if (std.mem.eql(u8, name, this.service_instance_string)) {
|
||||
} else if (std.mem.eql(u8, name, this.service_instance_string) and (rtype == c.MDNS_RECORDTYPE_SRV or rtype == c.MDNS_RECORDTYPE_ANY)) {
|
||||
const answer = this.mdns_records[1];
|
||||
const additional = this.mdns_records[2..];
|
||||
|
||||
var sendbuffer: [1024]u8 = undefined;
|
||||
if (rclass & c.MDNS_UNICAST_RESPONSE != 0) {
|
||||
|
@ -667,8 +727,8 @@ const HostMultiplayerGame = struct {
|
|||
answer,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
additional.ptr,
|
||||
additional.len,
|
||||
);
|
||||
} else {
|
||||
_ = c.mdns_query_answer_multicast(
|
||||
|
@ -678,8 +738,8 @@ const HostMultiplayerGame = struct {
|
|||
answer,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
additional.ptr,
|
||||
additional.len,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
const IpAddress = union(enum) {
|
||||
ipv4: [4]u8,
|
||||
ipv6: [16]u8,
|
||||
|
||||
pub fn format(
|
||||
this: @This(),
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
switch (this) {
|
||||
.ipv4 => |ip| return std.fmt.format(writer, "{}.{}.{}.{}", .{ ip[0], ip[1], ip[2], ip[3] }),
|
||||
.ipv6 => |ip| {
|
||||
const sextets: [8][2]u8 = @bitCast(ip);
|
||||
for (sextets, 0..) |sextet, i| {
|
||||
if (i > 0) {
|
||||
try std.fmt.format(writer, ":{}", .{std.fmt.fmtSliceHexLower(&sextet)});
|
||||
} else {
|
||||
try std.fmt.format(writer, "{}", .{std.fmt.fmtSliceHexLower(&sextet)});
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getIPAddresses(allocator: std.mem.Allocator) ![]IpAddress {
|
||||
const rtnetlink_socket = try std.os.socket(std.os.AF.NETLINK, std.os.SOCK.DGRAM, std.os.linux.NETLINK.ROUTE);
|
||||
defer std.os.closeSocket(rtnetlink_socket);
|
||||
|
||||
const rtnetlink_addr = std.os.sockaddr.nl{
|
||||
.pid = @intCast(std.os.linux.getpid()),
|
||||
.groups = 0,
|
||||
};
|
||||
try std.os.bind(rtnetlink_socket, @ptrCast(&rtnetlink_addr), @sizeOf(std.os.sockaddr.nl));
|
||||
|
||||
var addresses = std.ArrayList(IpAddress).init(allocator);
|
||||
defer addresses.deinit();
|
||||
|
||||
const msg_get_links = addrmsg_t{
|
||||
.hdr = std.os.linux.nlmsghdr{
|
||||
.len = @sizeOf(addrmsg_t),
|
||||
.type = .RTM_GETADDR,
|
||||
.flags = std.os.linux.NLM_F_REQUEST | std.os.linux.NLM_F_DUMP,
|
||||
.seq = 2,
|
||||
.pid = @intCast(std.os.linux.getpid()),
|
||||
},
|
||||
.body = ifaddrmsg_t{
|
||||
.family = 0,
|
||||
.prefixlen = 0,
|
||||
.flags = 0,
|
||||
.scope = 0,
|
||||
.interface_index = 0,
|
||||
},
|
||||
};
|
||||
const iovs = [_]std.os.iovec_const{
|
||||
.{ .iov_base = &std.mem.toBytes(msg_get_links), .iov_len = @sizeOf(addrmsg_t) },
|
||||
};
|
||||
_ = try std.os.sendmsg(rtnetlink_socket, &std.os.msghdr_const{
|
||||
.name = @ptrCast(&std.os.sockaddr.nl{ .pid = 0, .groups = 0 }),
|
||||
.namelen = @sizeOf(std.os.sockaddr.nl),
|
||||
.iov = &iovs,
|
||||
.iovlen = iovs.len,
|
||||
.control = null,
|
||||
.controllen = 0,
|
||||
.flags = 0,
|
||||
}, 0);
|
||||
|
||||
// listen for netlink messages
|
||||
netlink_loop: while (true) {
|
||||
var buf: [8192]u8 = undefined;
|
||||
|
||||
var kernel_addr = std.os.sockaddr.nl{ .pid = 0, .groups = 0 };
|
||||
var recv_iovs = [_]std.os.iovec{
|
||||
.{ .iov_base = &buf, .iov_len = buf.len },
|
||||
};
|
||||
var recv_hdr = std.os.linux.msghdr{
|
||||
.name = @ptrCast(&kernel_addr),
|
||||
.namelen = @sizeOf(std.os.sockaddr.nl),
|
||||
.iov = &recv_iovs,
|
||||
.iovlen = recv_iovs.len,
|
||||
.control = null,
|
||||
.controllen = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
const recv_msg_len = std.os.linux.recvmsg(rtnetlink_socket, &recv_hdr, 0);
|
||||
const recv_msg = buf[0..recv_msg_len];
|
||||
|
||||
var nlmsg_offset: usize = 0;
|
||||
while (nlmsg_offset < recv_msg.len) {
|
||||
const recv_nlmsghdr: *std.os.linux.nlmsghdr = @ptrCast(@alignCast(recv_msg[nlmsg_offset..][0..@sizeOf(std.os.linux.nlmsghdr)]));
|
||||
defer nlmsg_offset += recv_nlmsghdr.len;
|
||||
|
||||
const nlmsg_data = recv_msg[nlmsg_offset..][0..recv_nlmsghdr.len];
|
||||
|
||||
if (recv_nlmsghdr.type == .DONE) {
|
||||
break :netlink_loop;
|
||||
} else if (recv_nlmsghdr.type == .RTM_NEWADDR) {
|
||||
const address_message: *ifaddrmsg_t = @ptrCast(@alignCast(nlmsg_data[@sizeOf(std.os.linux.nlmsghdr)..][0..@sizeOf(ifaddrmsg_t)]));
|
||||
|
||||
switch (@as(RtScope, @enumFromInt(address_message.scope))) {
|
||||
.universe, .site, .link => {},
|
||||
// ignore IPs relevant only to the current machine
|
||||
.host, .nowhere => continue,
|
||||
}
|
||||
|
||||
var attr_offset: usize = std.mem.alignForward(usize, @sizeOf(ifaddrmsg_t) + @sizeOf(std.os.linux.nlmsghdr), @alignOf(std.os.linux.rtattr));
|
||||
while (attr_offset < nlmsg_data.len) {
|
||||
const attr_hdr: std.os.linux.rtattr = @bitCast(nlmsg_data[attr_offset..][0..@sizeOf(std.os.linux.rtattr)].*);
|
||||
|
||||
const attr_len_u32: u32 = @intCast(attr_hdr.len);
|
||||
const attr_len_aligned = std.mem.alignForward(usize, @sizeOf(std.os.linux.rtattr) + attr_len_u32, @alignOf(std.os.linux.rtattr));
|
||||
|
||||
const attr_data_offset = std.mem.alignForward(usize, attr_offset + @sizeOf(std.os.linux.rtattr), @alignOf(std.os.linux.rtattr));
|
||||
|
||||
switch (attr_hdr.type) {
|
||||
.ADDRESS => {
|
||||
const attr_data = nlmsg_data[attr_data_offset..][0 .. attr_hdr.len - @sizeOf(std.os.linux.rtattr)];
|
||||
switch (address_message.family) {
|
||||
std.os.linux.AF.INET => try addresses.append(.{ .ipv4 = attr_data[0..4].* }),
|
||||
std.os.linux.AF.INET6 => try addresses.append(.{ .ipv6 = attr_data[0..16].* }),
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
attr_offset += attr_len_aligned;
|
||||
}
|
||||
} else if (recv_nlmsghdr.type == .ERROR) {
|
||||
const nlmsgerr_t = extern struct {
|
||||
@"error": c_int,
|
||||
hdr: std.os.linux.nlmsghdr,
|
||||
};
|
||||
const err: *nlmsgerr_t = @ptrCast(@alignCast(nlmsg_data[@sizeOf(std.os.linux.nlmsghdr)..][0..@sizeOf(nlmsgerr_t)]));
|
||||
std.log.warn("errno = {}", .{err.@"error"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addresses.toOwnedSlice();
|
||||
}
|
||||
|
||||
const IFF = struct {
|
||||
const UP = 1 << 0;
|
||||
const BROADCAST = 1 << 1;
|
||||
const DEBUG = 1 << 2;
|
||||
const LOOPBACK = 1 << 3;
|
||||
const POINTOPOINT = 1 << 4;
|
||||
const NOTRAILERS = 1 << 5;
|
||||
const RUNNING = 1 << 6;
|
||||
const NOARP = 1 << 7;
|
||||
const PROMISC = 1 << 8;
|
||||
const ALLMULTI = 1 << 9;
|
||||
const MASTER = 1 << 10;
|
||||
const SLAVE = 1 << 11;
|
||||
const MULTICAST = 1 << 12;
|
||||
const PORTSEL = 1 << 13;
|
||||
const AUTOMEDIA = 1 << 14;
|
||||
const DYNAMIC = 1 << 15;
|
||||
};
|
||||
|
||||
const RtScope = enum(c_int) {
|
||||
universe = 0,
|
||||
site = 200,
|
||||
link = 253,
|
||||
host = 254,
|
||||
nowhere = 255,
|
||||
};
|
||||
|
||||
const ifaddrmsg_t = extern struct {
|
||||
family: u8,
|
||||
prefixlen: u8,
|
||||
flags: u8,
|
||||
scope: u8,
|
||||
interface_index: c_uint,
|
||||
};
|
||||
|
||||
const addrmsg_t = extern struct {
|
||||
hdr: std.os.linux.nlmsghdr,
|
||||
body: ifaddrmsg_t,
|
||||
};
|
||||
|
||||
const std = @import("std");
|
Loading…
Reference in New Issue