diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2019-10-29 02:19:22 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2019-10-29 02:19:22 -0400 |
| commit | 67058b9b7089446e16eee3c03ab3f8f9a5d13529 (patch) | |
| tree | 6c70752e5ad29ce53088f30fa431833c58f36b04 /lib/std/os.zig | |
| parent | d5865f5319305b6d06502b64772f8207bae2d6a5 (diff) | |
| download | zig-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.zig | 259 |
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), + } + } +} |
