aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-10-29 02:19:22 -0400
committerAndrew Kelley <andrew@ziglang.org>2019-10-29 02:19:22 -0400
commit67058b9b7089446e16eee3c03ab3f8f9a5d13529 (patch)
tree6c70752e5ad29ce53088f30fa431833c58f36b04 /lib/std/os.zig
parentd5865f5319305b6d06502b64772f8207bae2d6a5 (diff)
downloadzig-67058b9b7089446e16eee3c03ab3f8f9a5d13529.tar.gz
zig-67058b9b7089446e16eee3c03ab3f8f9a5d13529.zip
basic DNS address resolution for linux without libc
Diffstat (limited to 'lib/std/os.zig')
-rw-r--r--lib/std/os.zig259
1 files changed, 251 insertions, 8 deletions
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 40041c8ef7..d858f40106 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -1508,16 +1508,17 @@ pub const SocketError = error{
ProtocolNotSupported,
} || UnexpectedError;
-pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {
+pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
const rc = system.socket(domain, socket_type, protocol);
switch (errno(rc)) {
- 0 => return @intCast(i32, rc),
+ 0 => return @intCast(fd_t, rc),
EACCES => return error.PermissionDenied,
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
EINVAL => return error.ProtocolFamilyNotAvailable,
EMFILE => return error.ProcessFdQuotaExceeded,
ENFILE => return error.SystemFdQuotaExceeded,
- ENOBUFS, ENOMEM => return error.SystemResources,
+ ENOBUFS => return error.SystemResources,
+ ENOMEM => return error.SystemResources,
EPROTONOSUPPORT => return error.ProtocolNotSupported,
else => |err| return unexpectedErrno(err),
}
@@ -1559,17 +1560,17 @@ pub const BindError = error{
} || UnexpectedError;
/// addr is `*const T` where T is one of the sockaddr
-pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {
- const rc = system.bind(fd, addr, @sizeOf(sockaddr));
+pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
+ const rc = system.bind(sockfd, addr, len);
switch (errno(rc)) {
0 => return,
EACCES => return error.AccessDenied,
EADDRINUSE => return error.AddressInUse,
EBADF => unreachable, // always a race condition if this error is returned
- EINVAL => unreachable,
- ENOTSOCK => unreachable,
+ EINVAL => unreachable, // invalid parameters
+ ENOTSOCK => unreachable, // invalid `sockfd`
EADDRNOTAVAIL => return error.AddressNotAvailable,
- EFAULT => unreachable,
+ EFAULT => unreachable, // invalid `addr` pointer
ELOOP => return error.SymLinkLoop,
ENAMETOOLONG => return error.NameTooLong,
ENOENT => return error.FileNotFound,
@@ -2833,3 +2834,245 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
@compileError("TODO implement gethostname for this OS");
}
+
+pub fn res_mkquery(
+ op: u4,
+ dname: []const u8,
+ class: u8,
+ ty: u8,
+ data: []const u8,
+ newrr: ?[*]const u8,
+ buf: []u8,
+) usize {
+ var name = dname;
+ if (mem.endsWith(u8, name, ".")) name.len -= 1;
+ assert(name.len <= 253);
+ const n = 17 + name.len + @boolToInt(name.len != 0);
+
+ // Construct query template - ID will be filled later
+ var q: [280]u8 = undefined;
+ @memset(&q, 0, n);
+ q[2] = u8(op) * 8 + 1;
+ q[5] = 1;
+ mem.copy(u8, q[13..], name);
+ var i: usize = 13;
+ var j: usize = undefined;
+ while (q[i] != 0) : (i = j + 1) {
+ j = i;
+ while (q[j] != 0 and q[j] != '.') : (j += 1) {}
+ // TODO determine the circumstances for this and whether or
+ // not this should be an error.
+ if (j - i - 1 > 62) unreachable;
+ q[i - 1] = @intCast(u8, j - i);
+ }
+ q[i + 1] = ty;
+ q[i + 3] = class;
+
+ // Make a reasonably unpredictable id
+ var ts: timespec = undefined;
+ clock_gettime(CLOCK_REALTIME, &ts) catch {};
+ const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count);
+ const unsec = @bitCast(UInt, ts.tv_nsec);
+ const id = @truncate(u32, unsec + unsec / 65536);
+ q[0] = @truncate(u8, id / 256);
+ q[1] = @truncate(u8, id);
+
+ mem.copy(u8, buf, q[0..n]);
+ return n;
+}
+
+pub const SendError = error{
+ /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
+ /// on the destination socket file, or search permission is denied for one of the
+ /// directories the path prefix. (See path_resolution(7).)
+ /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
+ /// it was a unicast address.
+ AccessDenied,
+
+ /// The socket is marked nonblocking and the requested operation would block, and
+ /// there is no global event loop configured.
+ /// It's also possible to get this error under the following condition:
+ /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
+ /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
+ /// determined that all port numbers in the ephemeral port range are currently in use. See
+ /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ WouldBlock,
+
+ /// Another Fast Open is already in progress.
+ FastOpenAlreadyInProgress,
+
+ /// Connection reset by peer.
+ ConnectionResetByPeer,
+
+ /// The socket type requires that message be sent atomically, and the size of the message
+ /// to be sent made this impossible. The message is not transmitted.
+ ///
+ MessageTooBig,
+
+ /// The output queue for a network interface was full. This generally indicates that the
+ /// interface has stopped sending, but may be caused by transient congestion. (Normally,
+ /// this does not occur in Linux. Packets are just silently dropped when a device queue
+ /// overflows.)
+ /// This is also caused when there is not enough kernel memory available.
+ SystemResources,
+
+ /// The local end has been shut down on a connection oriented socket. In this case, the
+ /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
+ BrokenPipe,
+} || UnexpectedError;
+
+/// Transmit a message to another socket.
+///
+/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
+/// recipient is known). The following call
+///
+/// send(sockfd, buf, len, flags);
+///
+/// is equivalent to
+///
+/// sendto(sockfd, buf, len, flags, NULL, 0);
+///
+/// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments
+/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
+/// that the socket was actually connected.
+/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
+///
+/// If the message is too long to pass atomically through the underlying protocol,
+/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
+///
+/// There is no indication of failure to deliver.
+///
+/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
+/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
+/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
+/// possible to send more data.
+pub fn sendto(
+ /// The file descriptor of the sending socket.
+ sockfd: fd_t,
+ /// Message to send.
+ buf: []const u8,
+ flags: u32,
+ dest_addr: ?*const sockaddr,
+ addrlen: socklen_t,
+) SendError!usize {
+ while (true) {
+ const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
+ switch (errno(rc)) {
+ 0 => return rc,
+
+ EACCES => return error.AccessDenied,
+ EAGAIN => if (std.event.Loop.instance) |loop| {
+ loop.waitUntilFdWritable(sockfd) catch return error.WouldBlock;
+ continue;
+ } else {
+ return error.WouldBlock;
+ },
+ EALREADY => return error.FastOpenAlreadyInProgress,
+ EBADF => unreachable, // always a race condition
+ ECONNRESET => return error.ConnectionResetByPeer,
+ EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
+ EFAULT => unreachable, // An invalid user space address was specified for an argument.
+ EINTR => continue,
+ EINVAL => unreachable, // Invalid argument passed.
+ EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
+ EMSGSIZE => return error.MessageTooBig,
+ ENOBUFS => return error.SystemResources,
+ ENOMEM => return error.SystemResources,
+ ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
+ ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
+ EPIPE => return error.BrokenPipe,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+}
+
+/// Transmit a message to another socket.
+///
+/// The `send` call may be used only when the socket is in a connected state (so that the intended
+/// recipient is known). The only difference between `send` and `write` is the presence of
+/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
+/// call
+///
+/// send(sockfd, buf, len, flags);
+///
+/// is equivalent to
+///
+/// sendto(sockfd, buf, len, flags, NULL, 0);
+///
+/// There is no indication of failure to deliver.
+///
+/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
+/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
+/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
+/// possible to send more data.
+pub fn send(
+ /// The file descriptor of the sending socket.
+ sockfd: fd_t,
+ buf: []const u8,
+ flags: u32,
+) SendError!usize {
+ return sendto(sockfd, buf, flags, null, 0);
+}
+
+pub const PollError = error{
+ /// The kernel had no space to allocate file descriptor tables.
+ SystemResources,
+} || UnexpectedError;
+
+pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
+ while (true) {
+ const rc = system.poll(fds.ptr, fds.len, timeout);
+ switch (errno(rc)) {
+ 0 => return rc,
+ EFAULT => unreachable,
+ EINTR => continue,
+ EINVAL => unreachable,
+ ENOMEM => return error.SystemResources,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+}
+
+pub const RecvFromError = error{
+ /// The socket is marked nonblocking and the requested operation would block, and
+ /// there is no global event loop configured.
+ WouldBlock,
+
+ /// A remote host refused to allow the network connection, typically because it is not
+ /// running the requested service.
+ ConnectionRefused,
+
+ /// Could not allocate kernel memory.
+ SystemResources,
+} || UnexpectedError;
+
+pub fn recvfrom(
+ sockfd: fd_t,
+ buf: []u8,
+ flags: u32,
+ src_addr: ?*sockaddr,
+ addrlen: ?*socklen_t,
+) RecvFromError!usize {
+ while (true) {
+ const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
+ switch (errno(rc)) {
+ 0 => return rc,
+ EBADF => unreachable, // always a race condition
+ EFAULT => unreachable,
+ EINVAL => unreachable,
+ ENOTCONN => unreachable,
+ ENOTSOCK => unreachable,
+ EINTR => continue,
+ EAGAIN => if (std.event.Loop.instance) |loop| {
+ loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
+ continue;
+ } else {
+ return error.WouldBlock;
+ },
+ ENOMEM => return error.SystemResources,
+ ECONNREFUSED => return error.ConnectionRefused,
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+}