os: usingnamespace fixes for std.x.os.Socket and std.os.TCP

Extract existing constants to do with TCP socket options into a 'TCP'
namespace.

Export 'MSG' and 'TCP' from std.os.{linux, windows} into std.c.

Fix compile errors to do with std.x.os.Socket methods related to setting
TCP socket options.

Handle errors in the case that an interface could not be resolved in an
IPv6 address on Windows. Tested using Wine with the loopback interface
disabled.

Have all instantiations of std.x.os.Socket on Windows instantiate an
overlapped socket descriptor. Fixes the '1ms read timeout' test in
std.x.net.tcp.Client. The test would previously deadlock, as read
timeouts only apply to overlapped sockets.

Windows documentation by default recommends that most instantiations of
sockets on Windows be overlapped sockets (s.t. they may operate in both
blocking or nonblocking mode when operated with WSA* syscalls). Refer to
the documentation for WSASocketA for more info.
This commit is contained in:
Kenta Iwasaki 2021-09-11 19:03:14 +09:00 committed by Andrew Kelley
parent 2118566931
commit c4f97d3365
8 changed files with 135 additions and 112 deletions

View File

@ -31,6 +31,7 @@ pub const MAP = struct {
pub const FAILED = @intToPtr(*c_void, maxInt(usize));
};
pub const MMAP2_UNIT = linux.MMAP2_UNIT;
pub const MSG = linux.MSG;
pub const NAME_MAX = linux.NAME_MAX;
pub const O = linux.O;
pub const PATH_MAX = linux.PATH_MAX;
@ -54,6 +55,7 @@ pub const STDIN_FILENO = linux.STDIN_FILENO;
pub const STDOUT_FILENO = linux.STDOUT_FILENO;
pub const SYS = linux.SYS;
pub const Sigaction = linux.Sigaction;
pub const TCP = linux.TCP;
pub const VDSO = linux.VDSO;
pub const W = linux.W;
pub const W_OK = linux.W_OK;

View File

@ -204,7 +204,9 @@ pub const in_addr = u32;
pub const addrinfo = ws2_32.addrinfo;
pub const AF = ws2_32.AF;
pub const MSG = ws2_32.MSG;
pub const SOCK = ws2_32.SOCK;
pub const TCP = ws2_32.TCP;
pub const IPPROTO = ws2_32.IPPROTO;
pub const BTHPROTO_RFCOMM = ws2_32.BTHPROTO_RFCOMM;
@ -214,7 +216,6 @@ pub const POLL = ws2_32.POLL;
pub const SOL = ws2_32.SOL;
pub const SO = ws2_32.SO;
pub const PVD_CONFIG = ws2_32.PVD_CONFIG;
pub const TCP_NODELAY = ws2_32.TCP_NODELAY;
pub const O = struct {
pub const RDONLY = 0o0;

View File

@ -115,6 +115,7 @@ pub const SYS = system.SYS;
pub const Sigaction = system.Sigaction;
pub const Stat = system.Stat;
pub const TCSA = system.TCSA;
pub const TCP = system.TCP;
pub const VDSO = system.VDSO;
pub const W = system.W;
pub const addrinfo = system.addrinfo;

View File

@ -2007,6 +2007,82 @@ pub const SOCK = struct {
pub const NONBLOCK = if (is_mips) 0o200 else 0o4000;
};
pub const TCP = struct {
/// Turn off Nagle's algorithm
pub const NODELAY = 1;
/// Limit MSS
pub const MAXSEG = 2;
/// Never send partially complete segments.
pub const CORK = 3;
/// Start keeplives after this period, in seconds
pub const KEEPIDLE = 4;
/// Interval between keepalives
pub const KEEPINTVL = 5;
/// Number of keepalives before death
pub const KEEPCNT = 6;
/// Number of SYN retransmits
pub const SYNCNT = 7;
/// Life time of orphaned FIN-WAIT-2 state
pub const LINGER2 = 8;
/// Wake up listener only when data arrive
pub const DEFER_ACCEPT = 9;
/// Bound advertised window
pub const WINDOW_CLAMP = 10;
/// Information about this connection.
pub const INFO = 11;
/// Block/reenable quick acks
pub const QUICKACK = 12;
/// Congestion control algorithm
pub const CONGESTION = 13;
/// TCP MD5 Signature (RFC2385)
pub const MD5SIG = 14;
/// Use linear timeouts for thin streams
pub const THIN_LINEAR_TIMEOUTS = 16;
/// Fast retrans. after 1 dupack
pub const THIN_DUPACK = 17;
/// How long for loss retry before timeout
pub const USER_TIMEOUT = 18;
/// TCP sock is under repair right now
pub const REPAIR = 19;
pub const REPAIR_QUEUE = 20;
pub const QUEUE_SEQ = 21;
pub const REPAIR_OPTIONS = 22;
/// Enable FastOpen on listeners
pub const FASTOPEN = 23;
pub const TIMESTAMP = 24;
/// limit number of unsent bytes in write queue
pub const NOTSENT_LOWAT = 25;
/// Get Congestion Control (optional) info
pub const CC_INFO = 26;
/// Record SYN headers for new connections
pub const SAVE_SYN = 27;
/// Get SYN headers recorded for connection
pub const SAVED_SYN = 28;
/// Get/set window parameters
pub const REPAIR_WINDOW = 29;
/// Attempt FastOpen with connect
pub const FASTOPEN_CONNECT = 30;
/// Attach a ULP to a TCP connection
pub const ULP = 31;
/// TCP MD5 Signature with extensions
pub const MD5SIG_EXT = 32;
/// Set the key for Fast Open (cookie)
pub const FASTOPEN_KEY = 33;
/// Enable TFO without a TFO cookie
pub const FASTOPEN_NO_COOKIE = 34;
pub const ZEROCOPY_RECEIVE = 35;
/// Notify bytes available to read as a cmsg on read
pub const INQ = 36;
pub const CM_INQ = INQ;
/// delay outgoing packets by XX usec
pub const TX_DELAY = 37;
pub const REPAIR_ON = 1;
pub const REPAIR_OFF = 0;
/// Turn off without window probes
pub const REPAIR_OFF_NO_WP = -1;
};
pub const PF = struct {
pub const UNSPEC = 0;
pub const LOCAL = 1;
@ -3606,80 +3682,6 @@ pub const RR = struct {
pub const AAAA = 28;
};
/// Turn off Nagle's algorithm
pub const TCP_NODELAY = 1;
/// Limit MSS
pub const TCP_MAXSEG = 2;
/// Never send partially complete segments.
pub const TCP_CORK = 3;
/// Start keeplives after this period, in seconds
pub const TCP_KEEPIDLE = 4;
/// Interval between keepalives
pub const TCP_KEEPINTVL = 5;
/// Number of keepalives before death
pub const TCP_KEEPCNT = 6;
/// Number of SYN retransmits
pub const TCP_SYNCNT = 7;
/// Life time of orphaned FIN-WAIT-2 state
pub const TCP_LINGER2 = 8;
/// Wake up listener only when data arrive
pub const TCP_DEFER_ACCEPT = 9;
/// Bound advertised window
pub const TCP_WINDOW_CLAMP = 10;
/// Information about this connection.
pub const TCP_INFO = 11;
/// Block/reenable quick acks
pub const TCP_QUICKACK = 12;
/// Congestion control algorithm
pub const TCP_CONGESTION = 13;
/// TCP MD5 Signature (RFC2385)
pub const TCP_MD5SIG = 14;
/// Use linear timeouts for thin streams
pub const TCP_THIN_LINEAR_TIMEOUTS = 16;
/// Fast retrans. after 1 dupack
pub const TCP_THIN_DUPACK = 17;
/// How long for loss retry before timeout
pub const TCP_USER_TIMEOUT = 18;
/// TCP sock is under repair right now
pub const TCP_REPAIR = 19;
pub const TCP_REPAIR_QUEUE = 20;
pub const TCP_QUEUE_SEQ = 21;
pub const TCP_REPAIR_OPTIONS = 22;
/// Enable FastOpen on listeners
pub const TCP_FASTOPEN = 23;
pub const TCP_TIMESTAMP = 24;
/// limit number of unsent bytes in write queue
pub const TCP_NOTSENT_LOWAT = 25;
/// Get Congestion Control (optional) info
pub const TCP_CC_INFO = 26;
/// Record SYN headers for new connections
pub const TCP_SAVE_SYN = 27;
/// Get SYN headers recorded for connection
pub const TCP_SAVED_SYN = 28;
/// Get/set window parameters
pub const TCP_REPAIR_WINDOW = 29;
/// Attempt FastOpen with connect
pub const TCP_FASTOPEN_CONNECT = 30;
/// Attach a ULP to a TCP connection
pub const TCP_ULP = 31;
/// TCP MD5 Signature with extensions
pub const TCP_MD5SIG_EXT = 32;
/// Set the key for Fast Open (cookie)
pub const TCP_FASTOPEN_KEY = 33;
/// Enable TFO without a TFO cookie
pub const TCP_FASTOPEN_NO_COOKIE = 34;
pub const TCP_ZEROCOPY_RECEIVE = 35;
/// Notify bytes available to read as a cmsg on read
pub const TCP_INQ = 36;
pub const TCP_CM_INQ = TCP_INQ;
/// delay outgoing packets by XX usec
pub const TCP_TX_DELAY = 37;
pub const TCP_REPAIR_ON = 1;
pub const TCP_REPAIR_OFF = 0;
/// Turn off without window probes
pub const TCP_REPAIR_OFF_NO_WP = -1;
pub const tcp_repair_opt = extern struct {
opt_code: u32,
opt_val: u32,

View File

@ -442,27 +442,33 @@ pub const PROTECTION_LEVEL_EDGERESTRICTED = 20;
pub const PROTECTION_LEVEL_RESTRICTED = 30;
pub const INET_ADDRSTRLEN = 22;
pub const INET6_ADDRSTRLEN = 65;
pub const TCP_OFFLOAD_NO_PREFERENCE = 0;
pub const TCP_OFFLOAD_NOT_PREFERRED = 1;
pub const TCP_OFFLOAD_PREFERRED = 2;
pub const TCP_EXPEDITED_1122 = 2;
pub const TCP_KEEPALIVE = 3;
pub const TCP_MAXSEG = 4;
pub const TCP_MAXRT = 5;
pub const TCP_STDURG = 6;
pub const TCP_NOURG = 7;
pub const TCP_ATMARK = 8;
pub const TCP_NOSYNRETRIES = 9;
pub const TCP_TIMESTAMPS = 10;
pub const TCP_OFFLOAD_PREFERENCE = 11;
pub const TCP_CONGESTION_ALGORITHM = 12;
pub const TCP_DELAY_FIN_ACK = 13;
pub const TCP_MAXRTMS = 14;
pub const TCP_FASTOPEN = 15;
pub const TCP_KEEPCNT = 16;
pub const TCP_KEEPINTVL = 17;
pub const TCP_FAIL_CONNECT_ON_ICMP_ERROR = 18;
pub const TCP_ICMP_ERROR_INFO = 19;
pub const TCP = struct {
pub const NODELAY = 1;
pub const EXPEDITED_1122 = 2;
pub const OFFLOAD_NO_PREFERENCE = 0;
pub const OFFLOAD_NOT_PREFERRED = 1;
pub const OFFLOAD_PREFERRED = 2;
pub const KEEPALIVE = 3;
pub const MAXSEG = 4;
pub const MAXRT = 5;
pub const STDURG = 6;
pub const NOURG = 7;
pub const ATMARK = 8;
pub const NOSYNRETRIES = 9;
pub const TIMESTAMPS = 10;
pub const OFFLOAD_PREFERENCE = 11;
pub const CONGESTION_ALGORITHM = 12;
pub const DELAY_FIN_ACK = 13;
pub const MAXRTMS = 14;
pub const FASTOPEN = 15;
pub const KEEPCNT = 16;
pub const KEEPINTVL = 17;
pub const FAIL_CONNECT_ON_ICMP_ERROR = 18;
pub const ICMP_ERROR_INFO = 19;
pub const BSDURGENT = 28672;
};
pub const UDP_SEND_MSG_SIZE = 2;
pub const UDP_RECV_MAX_COALESCED_SIZE = 3;
pub const UDP_COALESCED_INFO = 3;
@ -578,7 +584,6 @@ pub const SO = struct {
};
pub const WSK_SO_BASE = 16384;
pub const TCP_NODELAY = 1;
pub const IOC_UNIX = 0;
pub const IOC_WS2 = 134217728;
pub const IOC_PROTOCOL = 268435456;
@ -846,7 +851,6 @@ pub const POLL = struct {
pub const NVAL = 4;
};
pub const TCP_BSDURGENT = 28672;
pub const TF_DISCONNECT = 1;
pub const TF_REUSE_SOCKET = 2;
pub const TF_WRITE_BEHIND = 4;

View File

@ -195,7 +195,7 @@ pub const Client = struct {
pub fn setNoDelay(self: Client, enabled: bool) !void {
if (@hasDecl(os.TCP, "NODELAY")) {
const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled)));
return self.socket.setOption(os.IPPROTO.TCP, os.TCP_NODELAY, bytes);
return self.socket.setOption(os.IPPROTO.TCP, os.TCP.NODELAY, bytes);
}
return error.UnsupportedSocketOption;
}
@ -204,7 +204,7 @@ pub const Client = struct {
/// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK.
pub fn setQuickACK(self: Client, enabled: bool) !void {
if (@hasDecl(os.TCP, "QUICKACK")) {
return self.socket.setOption(os.IPPROTO.TCP, os.TCP_QUICKACK, mem.asBytes(&@as(u32, @boolToInt(enabled))));
return self.socket.setOption(os.IPPROTO.TCP, os.TCP.QUICKACK, mem.asBytes(&@as(u32, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}
@ -305,7 +305,7 @@ pub const Listener = struct {
/// support TCP Fast Open.
pub fn setFastOpen(self: Listener, enabled: bool) !void {
if (@hasDecl(os.TCP, "FASTOPEN")) {
return self.socket.setOption(os.IPPROTO.TCP, os.TCP_FASTOPEN, mem.asBytes(&@as(u32, @boolToInt(enabled))));
return self.socket.setOption(os.IPPROTO.TCP, os.TCP.FASTOPEN, mem.asBytes(&@as(u32, @boolToInt(enabled))));
}
return error.UnsupportedSocketOption;
}

View File

@ -11,16 +11,20 @@ const have_ifnamesize = @hasDecl(os.system, "IFNAMESIZE");
/// Resolves a network interface name into a scope/zone ID. It returns
/// an error if either resolution fails, or if the interface name is
/// too long.
pub fn resolveScopeID(name: []const u8) !u32 {
pub fn resolveScopeId(name: []const u8) !u32 {
if (have_ifnamesize) {
if (name.len >= os.IFNAMESIZE - 1) return error.NameTooLong;
if (name.len >= os.IFNAMESIZE) return error.NameTooLong;
if (native_os.tag == .windows) {
var interface_name: [os.IFNAMESIZE]u8 = undefined;
var interface_name: [os.IFNAMESIZE:0]u8 = undefined;
mem.copy(u8, &interface_name, name);
interface_name[name.len] = 0;
return os.windows.ws2_32.if_nametoindex(@ptrCast([*:0]const u8, &interface_name));
const rc = os.windows.ws2_32.if_nametoindex(@ptrCast([*:0]const u8, &interface_name));
if (rc == 0) {
return error.InterfaceNotFound;
}
return rc;
}
const fd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM, 0);
@ -35,7 +39,7 @@ pub fn resolveScopeID(name: []const u8) !u32 {
return @bitCast(u32, f.ifru.ivalue);
}
return error.Unsupported;
return error.InterfaceNotFound;
}
/// An IPv4 address comprised of 4 bytes.
@ -403,7 +407,8 @@ pub const IPv6 = extern struct {
/// address.
pub const ParseError = error{
MalformedV4Mapping,
BadScopeID,
InterfaceNotFound,
UnknownScopeId,
} || IPv4.ParseError;
/// Parses an arbitrary IPv6 address, including link-local addresses.
@ -412,12 +417,15 @@ pub const IPv6 = extern struct {
const ip_slice = buf[0..index];
const scope_id_slice = buf[index + 1 ..];
if (scope_id_slice.len == 0) return error.BadScopeID;
if (scope_id_slice.len == 0) return error.UnknownScopeId;
const scope_id: u32 = switch (scope_id_slice[0]) {
'0'...'9' => fmt.parseInt(u32, scope_id_slice, 10),
else => resolveScopeID(scope_id_slice),
} catch return error.BadScopeID;
else => resolveScopeId(scope_id_slice) catch |err| switch (err) {
error.InterfaceNotFound => return error.InterfaceNotFound,
else => err,
},
} catch return error.UnknownScopeId;
return parseWithScopeID(ip_slice, scope_id);
}
@ -568,6 +576,11 @@ test "ipv6: parse & format addresses with scope ids" {
};
for (inputs) |input, i| {
try testing.expectFmt(outputs[i], "{}", .{try IPv6.parse(input)});
const parsed = IPv6.parse(input) catch |err| switch (err) {
error.InterfaceNotFound => continue,
else => return err,
};
try testing.expectFmt(outputs[i], "{}", .{parsed});
}
}

View File

@ -11,7 +11,7 @@ pub fn Mixin(comptime Socket: type) type {
return struct {
/// Open a new socket.
pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
var raw_flags: u32 = 0;
var raw_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
const set = std.EnumSet(Socket.InitFlags).init(flags);
if (set.contains(.close_on_exec)) raw_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;