Something went wrong. Try again.
htop for your network: a real-time IP traffic monitor for the terminal, ported to Zig from iptop
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566// SPDX-FileCopyrightText: © 2026 Francesc Muñoz// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Socket addresses as the tools print them: `192.0.2.1:443`, `[fe80::1]:5353`,//! `*:5353`. They are kept as the strings they arrived as, since that is also//! what is drawn, and taken apart only where a part is needed.
const std = @import("std");const z46 = @import("z46");
pub const HostPort = struct { ip: []const u8, port: []const u8,};
pub fn splitHostPort(addr: []const u8) HostPort { if (addr.len > 0 and addr[0] == '[') { const end = std.mem.findScalar(u8, addr, ']') orelse return .{ .ip = addr[1..], .port = "" }; return .{ .ip = addr[1..end], .port = if (end + 2 <= addr.len) addr[end + 2 ..] else "" }; } const sep = std.mem.findScalarLast(u8, addr, ':') orelse return .{ .ip = addr, .port = "" }; return .{ .ip = addr[0..sep], .port = addr[sep + 1 ..] };}
/// The remote port of an endpoint, or null when it has none worth the name./// The last colon is the separator, which also does the right thing for a/// bracketed IPv6 address.pub fn port(remote: []const u8) ?u16 { const i = std.mem.findScalarLast(u8, remote, ':') orelse return null; const p = std.fmt.parseInt(u16, remote[i + 1 ..], 10) catch return null; return if (p > 0) p else null;}
/// Whether an address is one only this network can reach, which is to say/// one there is no point asking anyone on the Internet about.pub fn isPrivate(ip: []const u8) bool { const address = z46.Address.parse(ip) catch return false; return address.isPrivate() or address.isLoopback() or address.isLinkLocal();}
const testing = std.testing;
test "splitHostPort" { try testing.expectEqualDeep(HostPort{ .ip = "192.0.2.1", .port = "443" }, splitHostPort("192.0.2.1:443")); try testing.expectEqualDeep(HostPort{ .ip = "fe80::1", .port = "5353" }, splitHostPort("[fe80::1]:5353")); try testing.expectEqualDeep(HostPort{ .ip = "192.0.2.1", .port = "" }, splitHostPort("192.0.2.1")); try testing.expectEqualDeep(HostPort{ .ip = "*", .port = "5353" }, splitHostPort("*:5353"));}
test "port parses IPv4 and IPv6 remotes" { try testing.expectEqual(@as(?u16, 443), port("1.2.3.4:443")); try testing.expectEqual(@as(?u16, 443), port("[2a00:1450::5]:443")); try testing.expectEqual(@as(?u16, null), port("")); try testing.expectEqual(@as(?u16, null), port("1.2.3.4:"));}
test "isPrivate" { for ([_][]const u8{ "10.1.2.3", "192.168.1.1", "172.16.0.1", "172.31.255.255", "127.0.0.1", "169.254.1.1", "::1", "fe80::1", "fd00::1", "fc00::1" }) |ip| { try testing.expect(isPrivate(ip)); } for ([_][]const u8{ "1.1.1.1", "172.32.0.1", "2606:4700:4700::1111", "not an address" }) |ip| { try testing.expect(!isPrivate(ip)); }}