Commit 22888ca524

Andrew Kelley <superjoe30@gmail.com>
2016-05-11 00:00:59
some work in progress networking code
also, casting to or from a u8 slice makes a function impure
1 parent d92ae20
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);
     }
 
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,
std/index.zig
@@ -12,4 +12,3 @@ pub const mem = @import("mem.zig");
 pub fn assert(b: bool) {
     if (!b) unreachable{}
 }
-
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;
+}
+*/
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)
 }
 */
 
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;
+}