Parse v6 address correctly, first working version

This commit is contained in:
Armin Friedl 2024-07-07 19:49:55 +02:00
parent 056da26cc0
commit 1657a643ff
3 changed files with 148 additions and 17 deletions

View file

@ -20,8 +20,8 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.link_libc = true,
}); });
exe.addIncludePath(.{ .path = "lib" });
// This declares intent for the executable to be installed into the // This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default // standard location when the user invokes the "install" step (the default
@ -58,7 +58,6 @@ pub fn build(b: *std.Build) void {
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
sockets_unit_tests.addIncludePath(.{ .path = "lib" });
const run_sockets_unit_tests = b.addRunArtifact(sockets_unit_tests); const run_sockets_unit_tests = b.addRunArtifact(sockets_unit_tests);
run_sockets_unit_tests.has_side_effects = true; // needed so tests aren't cached run_sockets_unit_tests.has_side_effects = true; // needed so tests aren't cached
@ -70,7 +69,6 @@ pub fn build(b: *std.Build) void {
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
process_unit_tests.addIncludePath(.{ .path = "lib" });
const run_process_unit_tests = b.addRunArtifact(process_unit_tests); const run_process_unit_tests = b.addRunArtifact(process_unit_tests);
run_process_unit_tests.has_side_effects = true; // needed so tests aren't cached run_process_unit_tests.has_side_effects = true; // needed so tests aren't cached
@ -79,8 +77,8 @@ pub fn build(b: *std.Build) void {
.root_source_file = b.path("src/main.zig"), .root_source_file = b.path("src/main.zig"),
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
.link_libc = true,
}); });
exe_unit_tests.addIncludePath(.{ .path = "lib" });
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);

View file

@ -1,6 +1,11 @@
const std = @import("std"); const std = @import("std");
const sockets = @import("sockets.zig"); const sockets = @import("sockets.zig");
const process = @import("process.zig"); const process = @import("process.zig");
const c = @cImport({
@cInclude("pwd.h");
@cInclude("arpa/inet.h");
@cInclude("signal.h");
});
pub const std_options = .{ .log_level = .info }; pub const std_options = .{ .log_level = .info };
@ -14,19 +19,124 @@ pub fn main() !void {
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const pids = try print_clogs(alloc, port);
defer alloc.free(pids);
if (pids.len == 0) {
return;
}
const kill_pids = try choose_kill();
kill(&[_]std.posix.pid_t{
pids[kill_pids[0]],
});
}
fn kill(pids: []const std.posix.pid_t) void {
for (pids) |pid| {
if (c.kill(pid, c.SIGTERM) == 0) {
// Wait briefly for process to exit
std.time.sleep(100000000); // 100ms
// Check if process still exists
if (c.kill(pid, 0) == 0) {
_ = c.kill(pid, c.SIGKILL); // now try with force
}
}
}
}
fn choose_kill() ![]usize {
var stdin = std.io.getStdIn().reader();
var stdout = std.io.getStdOut().writer();
try stdout.writeAll("Kill? ");
var buf: [2]u8 = undefined;
_ = try stdin.readUntilDelimiter(&buf, '\n');
var kills = [_]usize{
(try std.fmt.parseInt(usize, buf[0..1], 10)) - 1,
};
return &kills;
}
fn print_clogs(alloc: std.mem.Allocator, port: u16) ![]std.posix.pid_t {
var writer = std.io.getStdOut().writer();
var pids = std.ArrayList(std.posix.pid_t).init(alloc);
defer pids.deinit(); // noop after re-owning memory at end
const clog_sockets = try sockets.parse(alloc, null); const clog_sockets = try sockets.parse(alloc, null);
defer alloc.free(clog_sockets); defer alloc.free(clog_sockets);
for (clog_sockets) |clog_socket| { var any: bool = false;
if (clog_socket.port == port) { for (clog_sockets) |cs| {
const p = try process.find_by_inode(alloc, clog_socket.inode, null); if (cs.port == port) any = true;
defer p.deinit(); }
switch (clog_socket.protocol_data) { if (!any) {
.tcp_v4 => |v4| std.log.info("Found process {any} clogging address {any}", .{ p, v4.addr }), try writer.print("Port {d} looks unclogged\n", .{port});
.udp_v4 => |v4| std.log.info("Found process {any} clogging address {any}", .{ p, v4.addr }), return &[_]std.posix.pid_t{};
.tcp_v6 => |v6| std.log.info("Found process {any} clogging address {any}", .{ p, v6.addr }), }
.udp_v6 => |v6| std.log.info("Found process {any} clogging address {any}", .{ p, v6.addr }),
try writer.print("{s: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {s: <6} {s: <5}\n", .{ "#", "Command", "Path", "Protocol", "Address", "User", "Inode", "Port" });
var idx: usize = 1;
for (clog_sockets) |cs| {
if (cs.port == port) {
const clogs = try process.find_by_inode(alloc, cs.inode, null);
defer clogs.deinit();
const user = c.getpwuid(cs.uid);
for (clogs.items) |clog| {
switch (cs.protocol_data) {
// zig fmt: off
.tcp_v4 => |proto| {
var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined;
_ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN);
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
idx,
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)],
"TCP/IPv4", std.mem.sliceTo(&addr, 0),
user.*.pw_name, cs.inode, cs.port });
},
.tcp_v6 => |proto| {
var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined;
_ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN);
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
idx,
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)],
"TCP/IPv6", std.mem.sliceTo(&addr, 0),
user.*.pw_name, cs.inode, cs.port });
},
.udp_v4 => |proto| {
var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined;
_ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN);
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
idx,
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)],
"UDP/IPv4", std.mem.sliceTo(&addr, 0),
user.*.pw_name, cs.inode, cs.port });
},
.udp_v6 => |proto| {
var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined;
_ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN);
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
idx,
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)],
"UDP/IPv6", std.mem.sliceTo(&addr, 0),
user.*.pw_name, cs.inode, cs.port });
},
// zig fmt: on
}
try pids.append(clog.pid);
idx += 1;
} }
} }
} }
return pids.toOwnedSlice();
} }

View file

@ -124,15 +124,38 @@ fn parse_internal(path: []const u8, protocol: Protocol, buf: *std.ArrayList(Clog
.uid = try std.fmt.parseInt(std.posix.uid_t, uid, 10), .uid = try std.fmt.parseInt(std.posix.uid_t, uid, 10),
.protocol_data = switch (protocol) { .protocol_data = switch (protocol) {
.tcp_v4 => .{ .tcp_v4 = .{ .addr = try std.fmt.parseInt(u32, local_address[0..8], 16) } }, .tcp_v4 => .{ .tcp_v4 = .{ .addr = try std.fmt.parseInt(u32, local_address[0..8], 16) } },
.tcp_v6 => .{ .tcp_v6 = .{ .addr = try std.fmt.parseInt(u128, local_address[0..32], 16) } }, .tcp_v6 => .{ .tcp_v6 = .{ .addr = try parse_v6_address(local_address[0..32]) } },
.udp_v4 => .{ .udp_v4 = .{ .addr = try std.fmt.parseInt(u32, local_address[0..8], 16) } }, .udp_v4 => .{ .udp_v4 = .{ .addr = try std.fmt.parseInt(u32, local_address[0..8], 16) } },
.udp_v6 => .{ .udp_v6 = .{ .addr = try std.fmt.parseInt(u128, local_address[0..32], 16) } }, .udp_v6 => .{ .udp_v6 = .{ .addr = try parse_v6_address(local_address[0..32]) } },
}, },
}); });
} }
} }
// v6 addresses in /proc/net are not actually one big number, but an array of 4
// 32-bit (4 byte) numbers, each in base-16 little-endian (or just host byte order
// probably)
fn parse_v6_address(data: []const u8) !u128 {
var buf: [4]u32 = undefined;
buf[0] = try std.fmt.parseInt(u32, data[0..8], 16);
buf[1] = try std.fmt.parseInt(u32, data[8..16], 16);
buf[2] = try std.fmt.parseInt(u32, data[16..24], 16);
buf[3] = try std.fmt.parseInt(u32, data[24..32], 16);
// now do the fun stuff, just re-interpret the array as u128
return std.mem.bytesToValue(u128, &buf);
}
test "parse_v6" {
std.testing.log_level = .info;
const data = "00000000000000000000000001000000";
const r = try parse_v6_address(data);
std.log.info("Got {d}", .{r});
}
test "parse" { test "parse" {
var alloc = std.testing.allocator; var alloc = std.testing.allocator;