2017-12-23 19:08:53 -08:00
|
|
|
const std = @import("index.zig");
|
|
|
|
const linux = std.os.linux;
|
|
|
|
const assert = std.debug.assert;
|
|
|
|
const endian = std.endian;
|
2016-05-03 20:48:53 -07:00
|
|
|
|
2018-01-08 21:07:01 -08:00
|
|
|
// TODO don't trust this file, it bit rotted. start over
|
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
const Connection = struct {
|
2016-05-04 14:52:15 -07:00
|
|
|
socket_fd: i32,
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn send(c: Connection, buf: []const u8) !usize {
|
2016-05-13 09:23:03 -07:00
|
|
|
const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
|
2016-08-16 22:42:50 -07:00
|
|
|
const send_err = linux.getErrno(send_ret);
|
2016-05-13 09:23:03 -07:00
|
|
|
switch (send_err) {
|
|
|
|
0 => return send_ret,
|
2017-08-27 02:15:24 -07:00
|
|
|
linux.EINVAL => unreachable,
|
|
|
|
linux.EFAULT => unreachable,
|
|
|
|
linux.ECONNRESET => return error.ConnectionReset,
|
|
|
|
linux.EINTR => return error.SigInterrupt,
|
2016-05-13 09:23:03 -07:00
|
|
|
// TODO there are more possible errors
|
|
|
|
else => return error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn recv(c: Connection, buf: []u8) ![]u8 {
|
2016-05-13 09:23:03 -07:00
|
|
|
const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
|
2016-08-16 22:42:50 -07:00
|
|
|
const recv_err = linux.getErrno(recv_ret);
|
2016-05-13 09:23:03 -07:00
|
|
|
switch (recv_err) {
|
2017-05-19 07:39:59 -07:00
|
|
|
0 => return buf[0..recv_ret],
|
2017-08-27 02:15:24 -07:00
|
|
|
linux.EINVAL => unreachable,
|
|
|
|
linux.EFAULT => unreachable,
|
|
|
|
linux.ENOTSOCK => return error.NotSocket,
|
|
|
|
linux.EINTR => return error.SigInterrupt,
|
2017-10-09 11:21:35 -07:00
|
|
|
linux.ENOMEM => return error.OutOfMemory,
|
2017-08-27 02:15:24 -07:00
|
|
|
linux.ECONNREFUSED => return error.ConnectionRefused,
|
|
|
|
linux.EBADF => return error.BadFd,
|
2016-05-13 09:23:03 -07:00
|
|
|
// TODO more error values
|
|
|
|
else => return error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn close(c: Connection) !void {
|
2016-08-16 22:42:50 -07:00
|
|
|
switch (linux.getErrno(linux.close(c.socket_fd))) {
|
2016-05-04 14:52:15 -07:00
|
|
|
0 => return,
|
2017-08-27 02:15:24 -07:00
|
|
|
linux.EBADF => unreachable,
|
|
|
|
linux.EINTR => return error.SigInterrupt,
|
|
|
|
linux.EIO => return error.Io,
|
2016-05-04 14:52:15 -07:00
|
|
|
else => return error.Unexpected,
|
|
|
|
}
|
|
|
|
}
|
2016-12-18 16:40:26 -08:00
|
|
|
};
|
2016-05-04 14:52:15 -07:00
|
|
|
|
2016-12-18 16:40:26 -08:00
|
|
|
const Address = struct {
|
2016-05-10 15:00:59 -07:00
|
|
|
family: u16,
|
|
|
|
scope_id: u32,
|
|
|
|
addr: [16]u8,
|
|
|
|
sort_key: i32,
|
2016-12-18 16:40:26 -08:00
|
|
|
};
|
2016-05-04 14:52:15 -07:00
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
|
2016-05-10 15:00:59 -07:00
|
|
|
if (hostname.len == 0) {
|
|
|
|
|
2017-12-21 21:50:30 -08:00
|
|
|
unreachable; // TODO
|
2016-05-10 15:00:59 -07:00
|
|
|
}
|
|
|
|
|
2017-12-21 21:50:30 -08:00
|
|
|
unreachable; // TODO
|
2016-05-04 14:52:15 -07:00
|
|
|
}
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn connectAddr(addr: &Address, port: u16) !Connection {
|
2016-05-10 15:00:59 -07:00
|
|
|
const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
|
2016-08-16 22:42:50 -07:00
|
|
|
const socket_err = linux.getErrno(socket_ret);
|
2016-05-04 14:52:15 -07:00
|
|
|
if (socket_err > 0) {
|
|
|
|
// TODO figure out possible errors from socket()
|
|
|
|
return error.Unexpected;
|
|
|
|
}
|
|
|
|
const socket_fd = i32(socket_ret);
|
|
|
|
|
2017-12-21 21:50:30 -08:00
|
|
|
const connect_ret = if (addr.family == linux.AF_INET) x: {
|
2016-05-10 15:00:59 -07:00
|
|
|
var os_addr: linux.sockaddr_in = undefined;
|
|
|
|
os_addr.family = addr.family;
|
2016-08-17 20:11:04 -07:00
|
|
|
os_addr.port = endian.swapIfLe(u16, port);
|
2016-05-10 15:00:59 -07:00
|
|
|
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
|
2016-12-31 15:25:10 -08:00
|
|
|
@memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));
|
2017-12-21 21:50:30 -08:00
|
|
|
break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));
|
|
|
|
} else if (addr.family == linux.AF_INET6) x: {
|
2016-05-10 15:00:59 -07:00
|
|
|
var os_addr: linux.sockaddr_in6 = undefined;
|
|
|
|
os_addr.family = addr.family;
|
2016-08-17 20:11:04 -07:00
|
|
|
os_addr.port = endian.swapIfLe(u16, port);
|
2016-05-10 15:00:59 -07:00
|
|
|
os_addr.flowinfo = 0;
|
|
|
|
os_addr.scope_id = addr.scope_id;
|
|
|
|
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
|
2017-12-21 21:50:30 -08:00
|
|
|
break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
|
2016-05-10 15:00:59 -07:00
|
|
|
} else {
|
2017-12-21 21:50:30 -08:00
|
|
|
unreachable;
|
2016-05-10 15:00:59 -07:00
|
|
|
};
|
2016-08-16 22:42:50 -07:00
|
|
|
const connect_err = linux.getErrno(connect_ret);
|
2016-05-04 14:52:15 -07:00
|
|
|
if (connect_err > 0) {
|
2016-05-10 15:00:59 -07:00
|
|
|
switch (connect_err) {
|
2017-08-27 02:15:24 -07:00
|
|
|
linux.ETIMEDOUT => return error.TimedOut,
|
2016-05-10 15:00:59 -07:00
|
|
|
else => {
|
|
|
|
// TODO figure out possible errors from connect()
|
|
|
|
return error.Unexpected;
|
|
|
|
},
|
|
|
|
}
|
2016-05-04 14:52:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return Connection {
|
|
|
|
.socket_fd = socket_fd,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn connect(hostname: []const u8, port: u16) !Connection {
|
2016-05-04 14:52:15 -07:00
|
|
|
var addrs_buf: [1]Address = undefined;
|
2018-01-07 13:51:46 -08:00
|
|
|
const addrs_slice = try lookup(hostname, addrs_buf[0..]);
|
2016-05-04 14:52:15 -07:00
|
|
|
const main_addr = &addrs_slice[0];
|
|
|
|
|
2016-08-16 22:42:50 -07:00
|
|
|
return connectAddr(main_addr, port);
|
2016-05-03 20:48:53 -07:00
|
|
|
}
|
2016-05-10 15:00:59 -07:00
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
pub fn parseIpLiteral(buf: []const u8) !Address {
|
2016-05-10 15:00:59 -07:00
|
|
|
|
|
|
|
return error.InvalidIpLiteral;
|
|
|
|
}
|
|
|
|
|
2018-01-25 01:10:11 -08:00
|
|
|
fn hexDigit(c: u8) u8 {
|
2016-05-10 15:00:59 -07:00
|
|
|
// TODO use switch with range
|
2017-03-26 02:21:28 -07:00
|
|
|
if ('0' <= c and c <= '9') {
|
2017-12-21 21:50:30 -08:00
|
|
|
return c - '0';
|
2017-03-26 02:21:28 -07:00
|
|
|
} else if ('A' <= c and c <= 'Z') {
|
2017-12-21 21:50:30 -08:00
|
|
|
return c - 'A' + 10;
|
2017-03-26 02:21:28 -07:00
|
|
|
} else if ('a' <= c and c <= 'z') {
|
2017-12-21 21:50:30 -08:00
|
|
|
return c - 'a' + 10;
|
2016-05-10 15:00:59 -07:00
|
|
|
} else {
|
2017-12-21 21:50:30 -08:00
|
|
|
return @maxValue(u8);
|
2016-05-10 15:00:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
fn parseIp6(buf: []const u8) !Address {
|
2016-05-10 15:00:59 -07:00
|
|
|
var result: Address = undefined;
|
|
|
|
result.family = linux.AF_INET6;
|
|
|
|
result.scope_id = 0;
|
2017-05-19 07:39:59 -07:00
|
|
|
const ip_slice = result.addr[0..];
|
2016-05-10 15:00:59 -07:00
|
|
|
|
|
|
|
var x: u16 = 0;
|
|
|
|
var saw_any_digits = false;
|
|
|
|
var index: u8 = 0;
|
|
|
|
var scope_id = false;
|
|
|
|
for (buf) |c| {
|
|
|
|
if (scope_id) {
|
2017-03-26 02:21:28 -07:00
|
|
|
if (c >= '0' and c <= '9') {
|
2016-05-10 15:00:59 -07:00
|
|
|
const digit = c - '0';
|
2016-08-16 22:42:50 -07:00
|
|
|
if (@mulWithOverflow(u32, result.scope_id, 10, &result.scope_id)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
return error.Overflow;
|
|
|
|
}
|
2016-08-16 22:42:50 -07:00
|
|
|
if (@addWithOverflow(u32, result.scope_id, digit, &result.scope_id)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
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 {
|
2016-08-16 22:42:50 -07:00
|
|
|
const digit = hexDigit(c);
|
|
|
|
if (digit == @maxValue(u8)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
return error.InvalidChar;
|
|
|
|
}
|
2016-08-16 22:42:50 -07:00
|
|
|
if (@mulWithOverflow(u16, x, 16, &x)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
return error.Overflow;
|
|
|
|
}
|
2016-08-16 22:42:50 -07:00
|
|
|
if (@addWithOverflow(u16, x, digit, &x)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-01-31 19:48:40 -08:00
|
|
|
fn parseIp4(buf: []const u8) !u32 {
|
2016-05-10 15:00:59 -07:00
|
|
|
var result: u32 = undefined;
|
2017-05-19 07:39:59 -07:00
|
|
|
const out_ptr = ([]u8)((&result)[0..1]);
|
2016-05-10 15:00:59 -07:00
|
|
|
|
|
|
|
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;
|
2017-03-26 02:21:28 -07:00
|
|
|
} else if (c >= '0' and c <= '9') {
|
2016-05-10 15:00:59 -07:00
|
|
|
saw_any_digits = true;
|
|
|
|
const digit = c - '0';
|
2016-08-16 22:42:50 -07:00
|
|
|
if (@mulWithOverflow(u8, x, 10, &x)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
return error.Overflow;
|
|
|
|
}
|
2016-08-16 22:42:50 -07:00
|
|
|
if (@addWithOverflow(u8, x, digit, &x)) {
|
2016-05-10 15:00:59 -07:00
|
|
|
return error.Overflow;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return error.InvalidChar;
|
|
|
|
}
|
|
|
|
}
|
2017-03-26 02:21:28 -07:00
|
|
|
if (index == 3 and saw_any_digits) {
|
2016-05-10 15:00:59 -07:00
|
|
|
out_ptr[index] = x;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return error.Incomplete;
|
|
|
|
}
|