From 22888ca5246acb7dc7afac602488b17f01790bfb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 May 2016 15:00:59 -0700 Subject: [PATCH] some work in progress networking code also, casting to or from a u8 slice makes a function impure --- src/analyze.cpp | 1 + std/hash_map.zig | 6 + std/index.zig | 1 - std/linux.zig | 67 +++++++++-- std/list.zig | 4 +- std/net.zig | 288 +++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 346 insertions(+), 21 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 4e7a749031..b7f325bde3 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4260,6 +4260,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const || !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const)) { + mark_impure_fn(context); return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true); } diff --git a/std/hash_map.zig b/std/hash_map.zig index f4f9d455a2..86cd3daee8 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -6,6 +6,12 @@ const Allocator = mem.Allocator; const want_modification_safety = !@compile_var("is_release"); const debug_u32 = if (want_modification_safety) u32 else void; +/* +pub fn HashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool) { + SmallHashMap(K, V, hash, eql, 8); +} +*/ + pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b: K)->bool, STATIC_SIZE: isize) { entries: []Entry, size: isize, diff --git a/std/index.zig b/std/index.zig index 78786cd9d0..90c82519d5 100644 --- a/std/index.zig +++ b/std/index.zig @@ -12,4 +12,3 @@ pub const mem = @import("mem.zig"); pub fn assert(b: bool) { if (!b) unreachable{} } - diff --git a/std/linux.zig b/std/linux.zig index 797a7d1c70..ec4988a70b 100644 --- a/std/linux.zig +++ b/std/linux.zig @@ -312,13 +312,10 @@ fn restore_signals(set: &sigset_t) { } -pub type sa_family_t = u16; -pub type socklen_t = u32; -pub type in_addr_t = u32; - -export struct in_addr { - s_addr: in_addr_t, -} +pub const sa_family_t = u16; +pub const socklen_t = u32; +pub const in_addr = u32; +pub const in6_addr = [16]u8; export struct sockaddr { family: sa_family_t, @@ -341,15 +338,33 @@ export struct sockaddr_in6 { scope_id: u32, } -export struct in6_addr { - addr: [16]u8, -} - export struct iovec { iov_base: &u8, iov_len: usize, } +/* +const IF_NAMESIZE = 16; + +export struct ifreq { + ifrn_name: [IF_NAMESIZE]u8, + union { + ifru_addr: sockaddr, + ifru_dstaddr: sockaddr, + ifru_broadaddr: sockaddr, + ifru_netmask: sockaddr, + ifru_hwaddr: sockaddr, + ifru_flags: i16, + ifru_ivalue: i32, + ifru_mtu: i32, + ifru_map: ifmap, + ifru_slave: [IF_NAMESIZE]u8, + ifru_newname: [IF_NAMESIZE]u8, + ifru_data: &u8, + } ifr_ifru; +} +*/ + pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> isize { arch.syscall3(arch.SYS_getsockname, fd, isize(addr), isize(len)) } @@ -415,3 +430,33 @@ pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) -> i pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: i32) -> isize { arch.syscall4(arch.SYS_accept4, fd, isize(addr), isize(len), flags) } + +/* +pub error NameTooLong; +pub error SystemResources; +pub error Io; + +pub fn if_nametoindex(name: []u8) -> %u32 { + var ifr: ifreq = undefined; + + if (name.len >= ifr.ifr_name.len) { + return error.NameTooLong; + } + + const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + const socket_err = get_errno(socket_ret); + if (socket_err > 0) { + return error.SystemResources; + } + const socket_fd = i32(socket_ret); + @memcpy(&ifr.ifr_name[0], &name[0], name.len); + ifr.ifr_name[name.len] = 0; + const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr); + close(socket_fd); + const ioctl_err = get_errno(ioctl_ret); + if (ioctl_err > 0) { + return error.Io; + } + return ifr.ifr_ifindex; +} +*/ diff --git a/std/list.zig b/std/list.zig index b2bfbed4f9..923a177345 100644 --- a/std/list.zig +++ b/std/list.zig @@ -3,8 +3,8 @@ const mem = @import("mem.zig"); const Allocator = mem.Allocator; /* -fn List(T: type) -> type { - List(T, 8) +pub fn List(T: type) -> type { + SmallList(T, 8) } */ diff --git a/std/net.zig b/std/net.zig index 778e5800b9..dafa31f277 100644 --- a/std/net.zig +++ b/std/net.zig @@ -1,9 +1,11 @@ const linux = @import("linux.zig"); const errno = @import("errno.zig"); +const assert = @import("index.zig").assert; pub error SigInterrupt; pub error Unexpected; pub error Io; +pub error TimedOut; struct Connection { socket_fd: i32, @@ -20,17 +22,37 @@ struct Connection { } struct Address { - addr: linux.sockaddr, + family: u16, + scope_id: u32, + addr: [16]u8, + sort_key: i32, } pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address { + if (hostname.len == 0) { + +/* + if (family != AF_INET6) + buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } }; + if (family != AF_INET) + buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } }; + */ + unreachable{} // TODO + } + + switch (parse_ip_literal(hostname)) { + Ok => |addr| { + out_addrs[0] = addr; + return out_addrs[0...1]; + }, + else => {}, + }; + unreachable{} // TODO } pub fn connect_addr(addr: &Address, port: u16) -> %Connection { - addr.addr.port = port; - - const socket_ret = linux.socket(linux.PF_INET, linux.SOCK_STREAM, linux.PROTO_tcp); + const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp); const socket_err = linux.get_errno(socket_ret); if (socket_err > 0) { // TODO figure out possible errors from socket() @@ -38,10 +60,33 @@ pub fn connect_addr(addr: &Address, port: u16) -> %Connection { } const socket_fd = i32(socket_ret); - const connect_err = linux.get_errno(linux.connect(socket_fd, &addr.addr, @sizeof(linux.sockaddr))); + const connect_ret = if (addr.family == linux.AF_INET) { + var os_addr: linux.sockaddr_in = undefined; + os_addr.family = addr.family; + os_addr.port = host_to_be(u16)(port); + @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4); + @memset(&os_addr.zero, 0, @sizeof(@typeof(os_addr.zero))); + linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in)) + } else if (addr.family == linux.AF_INET6) { + var os_addr: linux.sockaddr_in6 = undefined; + os_addr.family = addr.family; + os_addr.port = host_to_be(u16)(port); + os_addr.flowinfo = 0; + os_addr.scope_id = addr.scope_id; + @memcpy(&os_addr.addr[0], &addr.addr[0], 16); + linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeof(linux.sockaddr_in6)) + } else { + unreachable{} + }; + const connect_err = linux.get_errno(connect_ret); if (connect_err > 0) { - // TODO figure out possible errors from connect() - return error.Unexpected; + switch (connect_err) { + errno.ETIMEDOUT => return error.TimedOut, + else => { + // TODO figure out possible errors from connect() + return error.Unexpected; + }, + } } return Connection { @@ -56,3 +101,232 @@ pub fn connect(hostname: []const u8, port: u16) -> %Connection { return connect_addr(main_addr, port); } + +pub error InvalidIpLiteral; + +pub fn parse_ip_literal(buf: []const u8) -> %Address { + switch (parse_ip4(buf)) { + Ok => |ip4| { + var result: Address = undefined; + @memcpy(&result.addr[0], (&u8)(&ip4), @sizeof(u32)); + result.family = linux.AF_INET; + result.scope_id = 0; + return result; + }, + else => {}, + } + switch (parse_ip6(buf)) { + Ok => |addr| { + return addr; + }, + else => {}, + } + + return error.InvalidIpLiteral; +} + +fn hex_digit(c: u8) -> u8 { + // TODO use switch with range + if ('0' <= c && c <= '9') { + c - '0' + } else if ('A' <= c && c <= 'Z') { + c - 'A' + 10 + } else if ('a' <= c && c <= 'z') { + c - 'a' + 10 + } else { + @max_value(u8) + } +} + +error InvalidChar; +error Overflow; +error JunkAtEnd; +error Incomplete; + +#static_eval_enable(false) +fn parse_ip6(buf: []const u8) -> %Address { + var result: Address = undefined; + result.family = linux.AF_INET6; + 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' && c <= '9') { + const digit = c - '0'; + if (@mul_with_overflow(u32, result.scope_id, 10, &result.scope_id)) { + return error.Overflow; + } + if (@add_with_overflow(u32, result.scope_id, digit, &result.scope_id)) { + return error.Overflow; + } + } else { + return error.InvalidChar; + } + } else if (c == ':') { + if (!saw_any_digits) { + return error.InvalidChar; + } + if (index == 14) { + return error.JunkAtEnd; + } + 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.InvalidChar; + } + 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 = hex_digit(c); + if (digit == @max_value(u8)) { + return error.InvalidChar; + } + if (@mul_with_overflow(u16, x, 16, &x)) { + return error.Overflow; + } + if (@add_with_overflow(u16, x, digit, &x)) { + return error.Overflow; + } + saw_any_digits = true; + } + } + + if (!saw_any_digits) { + return error.Incomplete; + } + + /* + if (p) { + if (isdigit(*++p)) scopeid = strtoull(p, &z, 10); + else z = p-1; + if (*z) { + if (!IN6_IS_ADDR_LINKLOCAL(&a6) && + !IN6_IS_ADDR_MC_LINKLOCAL(&a6)) + return EAI_NONAME; + scopeid = if_nametoindex(p); + if (!scopeid) return EAI_NONAME; + } + if (scopeid > UINT_MAX) return EAI_NONAME; + } + */ + + 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; +} + +fn parse_ip4(buf: []const u8) -> %u32 { + var result: u32 = undefined; + const out_ptr = ([]u8)((&result)[0...1]); + + var x: u8 = 0; + var index: u8 = 0; + var saw_any_digits = false; + for (buf) |c| { + if (c == '.') { + if (!saw_any_digits) { + return error.InvalidChar; + } + if (index == 3) { + return error.JunkAtEnd; + } + out_ptr[index] = x; + index += 1; + x = 0; + saw_any_digits = false; + } else if (c >= '0' && c <= '9') { + saw_any_digits = true; + const digit = c - '0'; + if (@mul_with_overflow(u8, x, 10, &x)) { + return error.Overflow; + } + if (@add_with_overflow(u8, x, digit, &x)) { + return error.Overflow; + } + } else { + return error.InvalidChar; + } + } + if (index == 3 && saw_any_digits) { + out_ptr[index] = x; + return result; + } + + return error.Incomplete; +} + + +#attribute("test") +fn test_parse_ip4() { + assert(%%parse_ip4("127.0.0.1") == be_to_host(u32)(0x7f000001)); + switch (parse_ip4("256.0.0.1")) { Overflow => {}, else => unreachable {}, } + switch (parse_ip4("x.0.0.1")) { InvalidChar => {}, else => unreachable {}, } + switch (parse_ip4("127.0.0.1.1")) { JunkAtEnd => {}, else => unreachable {}, } + switch (parse_ip4("127.0.0.")) { Incomplete => {}, else => unreachable {}, } + switch (parse_ip4("100..0.1")) { InvalidChar => {}, else => unreachable {}, } +} + +#attribute("test") +fn test_parse_ip6() { + { + const addr = %%parse_ip6("FF01:0:0:0:0:0:0:FB"); + assert(addr.addr[0] == 0xff); + assert(addr.addr[1] == 0x01); + assert(addr.addr[2] == 0x00); + } +} + +#attribute("test") +fn test_lookup_simple_ip() { + { + var addrs_buf: [5]Address = undefined; + const addrs = %%lookup("192.168.1.1", addrs_buf); + assert(addrs.len == 1); + const addr = addrs[0]; + assert(addr.family == linux.AF_INET); + assert(addr.addr[0] == 192); + assert(addr.addr[1] == 168); + assert(addr.addr[2] == 1); + assert(addr.addr[3] == 1); + } +} + +const be_to_host = host_to_be; +fn host_to_be(T: type)(x: T) -> T { + if (@compile_var("is_big_endian")) x else endian_swap(T)(x) +} + +fn endian_swap(T: type)(x: T) -> T { + const x_slice = ([]u8)((&const x)[0...1]); + var result: T = undefined; + const result_slice = ([]u8)((&result)[0...1]); + for (result_slice) |*b, i| { + *b = x_slice[@sizeof(T) - i - 1]; + } + return result; +}