aboutsummaryrefslogtreecommitdiff
path: root/lib/std/net.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/net.zig')
-rw-r--r--lib/std/net.zig247
1 files changed, 247 insertions, 0 deletions
diff --git a/lib/std/net.zig b/lib/std/net.zig
new file mode 100644
index 0000000000..be9d18056c
--- /dev/null
+++ b/lib/std/net.zig
@@ -0,0 +1,247 @@
+const std = @import("std.zig");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const net = @This();
+const mem = std.mem;
+const os = std.os;
+
+pub const TmpWinAddr = struct {
+ family: u8,
+ data: [14]u8,
+};
+
+pub const OsAddress = switch (builtin.os) {
+ builtin.Os.windows => TmpWinAddr,
+ else => os.sockaddr,
+};
+
+pub const Address = struct {
+ os_addr: OsAddress,
+
+ pub fn initIp4(ip4: u32, _port: u16) Address {
+ return Address{
+ .os_addr = os.sockaddr{
+ .in = os.sockaddr_in{
+ .family = os.AF_INET,
+ .port = mem.nativeToBig(u16, _port),
+ .addr = ip4,
+ .zero = [_]u8{0} ** 8,
+ },
+ },
+ };
+ }
+
+ pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address {
+ return Address{
+ .os_addr = os.sockaddr{
+ .in6 = os.sockaddr_in6{
+ .family = os.AF_INET6,
+ .port = mem.nativeToBig(u16, _port),
+ .flowinfo = 0,
+ .addr = ip6.addr,
+ .scope_id = ip6.scope_id,
+ },
+ },
+ };
+ }
+
+ pub fn port(self: Address) u16 {
+ return mem.bigToNative(u16, self.os_addr.in.port);
+ }
+
+ pub fn initPosix(addr: os.sockaddr) Address {
+ return Address{ .os_addr = addr };
+ }
+
+ pub fn format(self: *const Address, out_stream: var) !void {
+ switch (self.os_addr.in.family) {
+ os.AF_INET => {
+ const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port);
+ const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]);
+ try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port);
+ },
+ os.AF_INET6 => {
+ const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port);
+ try out_stream.print("[TODO render ip6 address]:{}", native_endian_port);
+ },
+ else => try out_stream.write("(unrecognized address family)"),
+ }
+ }
+};
+
+pub fn parseIp4(buf: []const u8) !u32 {
+ var result: u32 = undefined;
+ const out_ptr = @sliceToBytes((*[1]u32)(&result)[0..]);
+
+ var x: u8 = 0;
+ var index: u8 = 0;
+ var saw_any_digits = false;
+ for (buf) |c| {
+ if (c == '.') {
+ if (!saw_any_digits) {
+ return error.InvalidCharacter;
+ }
+ if (index == 3) {
+ return error.InvalidEnd;
+ }
+ out_ptr[index] = x;
+ index += 1;
+ x = 0;
+ saw_any_digits = false;
+ } else if (c >= '0' and c <= '9') {
+ saw_any_digits = true;
+ const digit = c - '0';
+ if (@mulWithOverflow(u8, x, 10, &x)) {
+ return error.Overflow;
+ }
+ if (@addWithOverflow(u8, x, digit, &x)) {
+ return error.Overflow;
+ }
+ } else {
+ return error.InvalidCharacter;
+ }
+ }
+ if (index == 3 and saw_any_digits) {
+ out_ptr[index] = x;
+ return result;
+ }
+
+ return error.Incomplete;
+}
+
+pub const Ip6Addr = struct {
+ scope_id: u32,
+ addr: [16]u8,
+};
+
+pub fn parseIp6(buf: []const u8) !Ip6Addr {
+ var result: Ip6Addr = undefined;
+ result.scope_id = 0;
+ const ip_slice = result.addr[0..];
+
+ var x: u16 = 0;
+ var saw_any_digits = false;
+ var index: u8 = 0;
+ var scope_id = false;
+ for (buf) |c| {
+ if (scope_id) {
+ if (c >= '0' and c <= '9') {
+ const digit = c - '0';
+ if (@mulWithOverflow(u32, result.scope_id, 10, &result.scope_id)) {
+ return error.Overflow;
+ }
+ if (@addWithOverflow(u32, result.scope_id, digit, &result.scope_id)) {
+ return error.Overflow;
+ }
+ } else {
+ return error.InvalidCharacter;
+ }
+ } else if (c == ':') {
+ if (!saw_any_digits) {
+ return error.InvalidCharacter;
+ }
+ if (index == 14) {
+ return error.InvalidEnd;
+ }
+ ip_slice[index] = @truncate(u8, x >> 8);
+ index += 1;
+ ip_slice[index] = @truncate(u8, x);
+ index += 1;
+
+ x = 0;
+ saw_any_digits = false;
+ } else if (c == '%') {
+ if (!saw_any_digits) {
+ return error.InvalidCharacter;
+ }
+ if (index == 14) {
+ ip_slice[index] = @truncate(u8, x >> 8);
+ index += 1;
+ ip_slice[index] = @truncate(u8, x);
+ index += 1;
+ }
+ scope_id = true;
+ saw_any_digits = false;
+ } else {
+ const digit = try std.fmt.charToDigit(c, 16);
+ if (@mulWithOverflow(u16, x, 16, &x)) {
+ return error.Overflow;
+ }
+ if (@addWithOverflow(u16, x, digit, &x)) {
+ return error.Overflow;
+ }
+ saw_any_digits = true;
+ }
+ }
+
+ if (!saw_any_digits) {
+ return error.Incomplete;
+ }
+
+ if (scope_id) {
+ return result;
+ }
+
+ if (index == 14) {
+ ip_slice[14] = @truncate(u8, x >> 8);
+ ip_slice[15] = @truncate(u8, x);
+ return result;
+ }
+
+ return error.Incomplete;
+}
+
+test "std.net.parseIp4" {
+ assert((try parseIp4("127.0.0.1")) == mem.bigToNative(u32, 0x7f000001));
+
+ testParseIp4Fail("256.0.0.1", error.Overflow);
+ testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
+ testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
+ testParseIp4Fail("127.0.0.", error.Incomplete);
+ testParseIp4Fail("100..0.1", error.InvalidCharacter);
+}
+
+fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void {
+ if (parseIp4(buf)) |_| {
+ @panic("expected error");
+ } else |e| {
+ assert(e == expected_err);
+ }
+}
+
+test "std.net.parseIp6" {
+ const addr = try parseIp6("FF01:0:0:0:0:0:0:FB");
+ assert(addr.addr[0] == 0xff);
+ assert(addr.addr[1] == 0x01);
+ assert(addr.addr[2] == 0x00);
+}
+
+pub fn connectUnixSocket(path: []const u8) !std.fs.File {
+ const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;
+ const sockfd = try os.socket(
+ os.AF_UNIX,
+ os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,
+ 0,
+ );
+ errdefer os.close(sockfd);
+
+ var sock_addr = os.sockaddr{
+ .un = os.sockaddr_un{
+ .family = os.AF_UNIX,
+ .path = undefined,
+ },
+ };
+
+ if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong;
+ mem.copy(u8, sock_addr.un.path[0..], path);
+ const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
+ if (std.event.Loop.instance) |loop| {
+ try os.connect_async(sockfd, &sock_addr, size);
+ try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
+ try os.getsockoptError(sockfd);
+ } else {
+ try os.connect(sockfd, &sock_addr, size);
+ }
+
+ return std.fs.File.openHandle(sockfd);
+}