master
1const builtin = @import("builtin");
2const native_os = builtin.os.tag;
3const std = @import("../std.zig");
4const Io = std.Io;
5const assert = std.debug.assert;
6
7pub const HostName = @import("net/HostName.zig");
8
9/// Source of truth: Internet Assigned Numbers Authority (IANA)
10pub const Protocol = enum(u32) {
11 hopopts = 0,
12 icmp = 1,
13 igmp = 2,
14 ipip = 4,
15 tcp = 6,
16 egp = 8,
17 pup = 12,
18 udp = 17,
19 idp = 22,
20 tp = 29,
21 dccp = 33,
22 ipv6 = 41,
23 routing = 43,
24 fragment = 44,
25 rsvp = 46,
26 gre = 47,
27 esp = 50,
28 ah = 51,
29 icmpv6 = 58,
30 none = 59,
31 dstopts = 60,
32 mtp = 92,
33 beetph = 94,
34 encap = 98,
35 pim = 103,
36 comp = 108,
37 sctp = 132,
38 mh = 135,
39 udplite = 136,
40 mpls = 137,
41 ethernet = 143,
42 raw = 255,
43 mptcp = 262,
44};
45
46/// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
47/// first release to support them.
48pub const has_unix_sockets = switch (native_os) {
49 .windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false,
50 .wasi => false,
51 else => true,
52};
53
54pub const default_kernel_backlog = 128;
55
56pub const IpAddress = union(enum) {
57 ip4: Ip4Address,
58 ip6: Ip6Address,
59
60 pub const Family = @typeInfo(IpAddress).@"union".tag_type.?;
61
62 pub const ParseLiteralError = error{ InvalidAddress, InvalidPort };
63
64 /// Parse an IP address which may include a port.
65 ///
66 /// For IPv4, this is written `address:port`.
67 ///
68 /// For IPv6, RFC 3986 defines this as an "IP literal", and the port is
69 /// differentiated from the address by surrounding the address part in
70 /// brackets "[addr]:port". Even if the port is not given, the brackets are
71 /// mandatory.
72 pub fn parseLiteral(text: []const u8) ParseLiteralError!IpAddress {
73 if (text.len == 0) return error.InvalidAddress;
74 if (text[0] == '[') {
75 const addr_end = std.mem.findScalar(u8, text, ']') orelse
76 return error.InvalidAddress;
77 const addr_text = text[1..addr_end];
78 const port: u16 = p: {
79 if (addr_end == text.len - 1) break :p 0;
80 if (text[addr_end + 1] != ':') return error.InvalidAddress;
81 break :p std.fmt.parseInt(u16, text[addr_end + 2 ..], 10) catch return error.InvalidPort;
82 };
83 return parseIp6(addr_text, port) catch error.InvalidAddress;
84 }
85 if (std.mem.findScalar(u8, text, ':')) |i| {
86 const addr = Ip4Address.parse(text[0..i], 0) catch return error.InvalidAddress;
87 return .{ .ip4 = .{
88 .bytes = addr.bytes,
89 .port = std.fmt.parseInt(u16, text[i + 1 ..], 10) catch return error.InvalidPort,
90 } };
91 }
92 return parseIp4(text, 0) catch error.InvalidAddress;
93 }
94
95 /// Parse the given IP address string into an `IpAddress` value.
96 ///
97 /// This is a pure function but it cannot handle IPv6 addresses that have
98 /// scope ids ("%foo" at the end). To also handle those, `resolve` must be
99 /// called instead.
100 pub fn parse(text: []const u8, port: u16) !IpAddress {
101 if (parseIp4(text, port)) |ip4| return ip4 else |err| switch (err) {
102 error.Overflow,
103 error.InvalidEnd,
104 error.InvalidCharacter,
105 error.Incomplete,
106 error.NonCanonical,
107 => {},
108 }
109
110 return parseIp6(text, port);
111 }
112
113 pub fn parseIp4(text: []const u8, port: u16) Ip4Address.ParseError!IpAddress {
114 return .{ .ip4 = try Ip4Address.parse(text, port) };
115 }
116
117 /// This is a pure function but it cannot handle IPv6 addresses that have
118 /// scope ids ("%foo" at the end). To also handle those, `resolveIp6` must be
119 /// called instead.
120 pub fn parseIp6(text: []const u8, port: u16) Ip6Address.ParseError!IpAddress {
121 return .{ .ip6 = try Ip6Address.parse(text, port) };
122 }
123
124 /// This function requires an `Io` parameter because it must query the operating
125 /// system to convert interface name to index. For example, in
126 /// "fe80::e0e:76ff:fed4:cf22%eno1", "eno1" must be resolved to an index by
127 /// creating a socket and then using an `ioctl` syscall.
128 ///
129 /// For a pure function that cannot handle scopes, see `parse`.
130 pub fn resolve(io: Io, text: []const u8, port: u16) !IpAddress {
131 if (parseIp4(text, port)) |ip4| return ip4 else |err| switch (err) {
132 error.Overflow,
133 error.InvalidEnd,
134 error.InvalidCharacter,
135 error.Incomplete,
136 error.NonCanonical,
137 => {},
138 }
139
140 return resolveIp6(io, text, port);
141 }
142
143 pub fn resolveIp6(io: Io, text: []const u8, port: u16) Ip6Address.ResolveError!IpAddress {
144 return .{ .ip6 = try Ip6Address.resolve(io, text, port) };
145 }
146
147 /// Returns the port in native endian.
148 pub fn getPort(a: IpAddress) u16 {
149 return switch (a) {
150 inline .ip4, .ip6 => |x| x.port,
151 };
152 }
153
154 /// `port` is native-endian.
155 pub fn setPort(a: *IpAddress, port: u16) void {
156 switch (a) {
157 inline .ip4, .ip6 => |*x| x.port = port,
158 }
159 }
160
161 /// Includes the optional scope ("%foo" at the end) in IPv6 addresses.
162 ///
163 /// See `format` for an alternative that omits scopes and does
164 /// not require an `Io` parameter.
165 pub fn formatResolved(a: IpAddress, io: Io, w: *Io.Writer) Ip6Address.FormatError!void {
166 switch (a) {
167 .ip4 => |x| return x.format(w),
168 .ip6 => |x| return x.formatResolved(io, w),
169 }
170 }
171
172 /// See `formatResolved` for an alternative that additionally prints the optional
173 /// scope at the end of IPv6 addresses and requires an `Io` parameter.
174 pub fn format(a: IpAddress, w: *Io.Writer) Io.Writer.Error!void {
175 switch (a) {
176 inline .ip4, .ip6 => |x| return x.format(w),
177 }
178 }
179
180 pub fn eql(a: *const IpAddress, b: *const IpAddress) bool {
181 return switch (a.*) {
182 .ip4 => |a_ip4| switch (b.*) {
183 .ip4 => |b_ip4| a_ip4.eql(b_ip4),
184 else => false,
185 },
186 .ip6 => |a_ip6| switch (b.*) {
187 .ip6 => |b_ip6| a_ip6.eql(b_ip6),
188 else => false,
189 },
190 };
191 }
192
193 pub const ListenError = error{
194 /// The address is already taken. Can occur when bound port is 0 but
195 /// all ephemeral ports are already in use.
196 AddressInUse,
197 /// A nonexistent interface was requested or the requested address was not local.
198 AddressUnavailable,
199 /// The local network interface used to reach the destination is offline.
200 NetworkDown,
201 /// Insufficient memory or other resource internal to the operating system.
202 SystemResources,
203 /// Per-process limit on the number of open file descriptors has been reached.
204 ProcessFdQuotaExceeded,
205 /// System-wide limit on the total number of open files has been reached.
206 SystemFdQuotaExceeded,
207 /// The requested address family (IPv4 or IPv6) is not supported by the operating system.
208 AddressFamilyUnsupported,
209 ProtocolUnsupportedBySystem,
210 ProtocolUnsupportedByAddressFamily,
211 SocketModeUnsupported,
212 /// One of the `ListenOptions` is not supported by the Io
213 /// implementation.
214 OptionUnsupported,
215 } || Io.UnexpectedError || Io.Cancelable;
216
217 pub const ListenOptions = struct {
218 /// How many connections the kernel will accept on the application's behalf.
219 /// If more than this many connections pool in the kernel, clients will start
220 /// seeing "Connection refused".
221 kernel_backlog: u31 = default_kernel_backlog,
222 /// Sets SO_REUSEADDR and SO_REUSEPORT on POSIX.
223 /// Sets SO_REUSEADDR on Windows, which is roughly equivalent.
224 reuse_address: bool = false,
225 /// Only connection-oriented modes may be used here, which includes:
226 /// * `Socket.Mode.stream`
227 /// * `Socket.Mode.seqpacket`
228 mode: Socket.Mode = .stream,
229 /// Only connection-oriented protocols may be used here, which includes:
230 /// * `Protocol.tcp`
231 /// * `Protocol.tp`
232 /// * `Protocol.dccp`
233 /// * `Protocol.sctp`
234 protocol: Protocol = .tcp,
235 };
236
237 /// Waits for a TCP connection. When using this API, `bind` does not need
238 /// to be called. The returned `Server` has an open `stream`.
239 pub fn listen(address: IpAddress, io: Io, options: ListenOptions) ListenError!Server {
240 return io.vtable.netListenIp(io.userdata, address, options);
241 }
242
243 pub const BindError = error{
244 /// The address is already taken. Can occur when bound port is 0 but
245 /// all ephemeral ports are already in use.
246 AddressInUse,
247 /// A nonexistent interface was requested or the requested address was not local.
248 AddressUnavailable,
249 /// The address is not valid for the address family of socket.
250 AddressFamilyUnsupported,
251 /// Insufficient memory or other resource internal to the operating system.
252 SystemResources,
253 /// The local network interface used to reach the destination is offline.
254 NetworkDown,
255 ProtocolUnsupportedBySystem,
256 ProtocolUnsupportedByAddressFamily,
257 /// Per-process limit on the number of open file descriptors has been reached.
258 ProcessFdQuotaExceeded,
259 /// System-wide limit on the total number of open files has been reached.
260 SystemFdQuotaExceeded,
261 SocketModeUnsupported,
262 /// One of the `BindOptions` is not supported by the Io
263 /// implementation.
264 OptionUnsupported,
265 } || Io.UnexpectedError || Io.Cancelable;
266
267 pub const BindOptions = struct {
268 /// The socket is restricted to sending and receiving IPv6 packets only.
269 /// In this case, an IPv4 and an IPv6 application can bind to a single port
270 /// at the same time.
271 ip6_only: bool = false,
272 mode: Socket.Mode,
273 protocol: ?Protocol = null,
274 };
275
276 /// Associates an address with a `Socket` which can be used to receive UDP
277 /// packets and other kinds of non-streaming messages. See `listen` for a
278 /// streaming alternative.
279 ///
280 /// One bound `Socket` can be used to receive messages from multiple
281 /// different addresses.
282 pub fn bind(address: *const IpAddress, io: Io, options: BindOptions) BindError!Socket {
283 return io.vtable.netBindIp(io.userdata, address, options);
284 }
285
286 pub const ConnectError = error{
287 AddressUnavailable,
288 AddressFamilyUnsupported,
289 /// Insufficient memory or other resource internal to the operating system.
290 SystemResources,
291 ConnectionPending,
292 ConnectionRefused,
293 ConnectionResetByPeer,
294 HostUnreachable,
295 NetworkUnreachable,
296 Timeout,
297 /// One of the `ConnectOptions` is not supported by the Io
298 /// implementation.
299 OptionUnsupported,
300 /// Per-process limit on the number of open file descriptors has been reached.
301 ProcessFdQuotaExceeded,
302 /// System-wide limit on the total number of open files has been reached.
303 SystemFdQuotaExceeded,
304 ProtocolUnsupportedBySystem,
305 ProtocolUnsupportedByAddressFamily,
306 SocketModeUnsupported,
307 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
308 /// the connection request failed because of a local firewall rule.
309 AccessDenied,
310 /// Non-blocking was requested and the operation cannot return immediately.
311 WouldBlock,
312 NetworkDown,
313 } || Io.Timeout.Error || Io.UnexpectedError || Io.Cancelable;
314
315 pub const ConnectOptions = struct {
316 mode: Socket.Mode,
317 protocol: ?Protocol = null,
318 timeout: Io.Timeout = .none,
319 };
320
321 /// Initiates a connection-oriented network stream.
322 pub fn connect(address: IpAddress, io: Io, options: ConnectOptions) ConnectError!Stream {
323 return io.vtable.netConnectIp(io.userdata, &address, options);
324 }
325};
326
327/// An IPv4 address in binary memory layout.
328pub const Ip4Address = struct {
329 bytes: [4]u8,
330 port: u16,
331
332 pub fn loopback(port: u16) Ip4Address {
333 return .{
334 .bytes = .{ 127, 0, 0, 1 },
335 .port = port,
336 };
337 }
338
339 pub fn unspecified(port: u16) Ip4Address {
340 return .{
341 .bytes = .{ 0, 0, 0, 0 },
342 .port = port,
343 };
344 }
345
346 pub const ParseError = error{
347 Overflow,
348 InvalidEnd,
349 InvalidCharacter,
350 Incomplete,
351 NonCanonical,
352 };
353
354 pub fn parse(buffer: []const u8, port: u16) ParseError!Ip4Address {
355 var bytes: [4]u8 = @splat(0);
356 var index: u8 = 0;
357 var saw_any_digits = false;
358 var has_zero_prefix = false;
359 for (buffer) |c| switch (c) {
360 '.' => {
361 if (!saw_any_digits) return error.InvalidCharacter;
362 if (index == 3) return error.InvalidEnd;
363 index += 1;
364 saw_any_digits = false;
365 has_zero_prefix = false;
366 },
367 '0'...'9' => {
368 if (c == '0' and !saw_any_digits) {
369 has_zero_prefix = true;
370 } else if (has_zero_prefix) {
371 return error.NonCanonical;
372 }
373 saw_any_digits = true;
374 bytes[index] = try std.math.mul(u8, bytes[index], 10);
375 bytes[index] = try std.math.add(u8, bytes[index], c - '0');
376 },
377 else => return error.InvalidCharacter,
378 };
379 if (index == 3 and saw_any_digits) return .{
380 .bytes = bytes,
381 .port = port,
382 };
383 return error.Incomplete;
384 }
385
386 pub fn format(a: Ip4Address, w: *Io.Writer) Io.Writer.Error!void {
387 const bytes = &a.bytes;
388 try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], a.port });
389 }
390
391 pub fn eql(a: Ip4Address, b: Ip4Address) bool {
392 const a_int: u32 = @bitCast(a.bytes);
393 const b_int: u32 = @bitCast(b.bytes);
394 return a.port == b.port and a_int == b_int;
395 }
396};
397
398/// An IPv6 address in binary memory layout.
399pub const Ip6Address = struct {
400 /// Native endian
401 port: u16,
402 /// Big endian
403 bytes: [16]u8,
404 flow: u32 = 0,
405 interface: Interface = .none,
406
407 pub const Policy = struct {
408 addr: [16]u8,
409 len: u8,
410 mask: u8,
411 prec: u8,
412 label: u8,
413 };
414
415 pub fn loopback(port: u16) Ip6Address {
416 return .{
417 .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
418 .port = port,
419 };
420 }
421
422 pub fn unspecified(port: u16) Ip6Address {
423 return .{
424 .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
425 .port = port,
426 };
427 }
428
429 /// Constructs an IPv4-mapped IPv6 address.
430 pub fn fromIp4(ip4: Ip4Address) Ip6Address {
431 const b = &ip4.bytes;
432 return .{
433 .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, b[0], b[1], b[2], b[3] },
434 .port = ip4.port,
435 };
436 }
437
438 /// Given an `IpAddress`, converts it to an `Ip6Address` directly, or via
439 /// constructing an IPv4-mapped IPv6 address.
440 pub fn fromAny(addr: IpAddress) Ip6Address {
441 return switch (addr) {
442 .ip4 => |ip4| fromIp4(ip4),
443 .ip6 => |ip6| ip6,
444 };
445 }
446
447 /// An IPv6 address but with `Interface` as a name rather than index.
448 pub const Unresolved = struct {
449 /// Big endian
450 bytes: [16]u8,
451 /// Has not been checked to be a valid native interface name.
452 /// Externally managed memory.
453 interface_name: ?[]const u8,
454
455 pub const Parsed = union(enum) {
456 success: Unresolved,
457 invalid_byte: usize,
458 incomplete,
459 junk_after_end: usize,
460 interface_name_oversized: usize,
461 invalid_ip4_mapping: usize,
462 overflow: usize,
463 };
464
465 pub fn parse(text: []const u8) Parsed {
466 if (text.len < 2) return .incomplete;
467 const ip4_prefix = "::ffff:";
468 if (std.ascii.startsWithIgnoreCase(text, ip4_prefix)) {
469 const parsed = Ip4Address.parse(text[ip4_prefix.len..], 0) catch
470 return .{ .invalid_ip4_mapping = ip4_prefix.len };
471 const b = parsed.bytes;
472 return .{ .success = .{
473 .bytes = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, b[0], b[1], b[2], b[3] },
474 .interface_name = null,
475 } };
476 }
477 // Has to be u16 elements to handle 3-digit hex numbers from compression.
478 var parts: [8]u16 = @splat(0);
479 var parts_i: u8 = 0;
480 var text_i: u8 = 0;
481 var digit_i: u8 = 0;
482 var compress_start: ?u8 = null;
483 var interface_name_text: ?[]const u8 = null;
484 const State = union(enum) { digit, end };
485 state: switch (State.digit) {
486 .digit => c: switch (text[text_i]) {
487 'a'...'f' => |c| {
488 const digit = c - 'a' + 10;
489 parts[parts_i] = (std.math.mul(u16, parts[parts_i], 16) catch return .{
490 .overflow = text_i,
491 }) + digit;
492 if (digit_i == 4) return .{ .invalid_byte = text_i };
493 digit_i += 1;
494 text_i += 1;
495 if (text.len - text_i == 0) {
496 parts_i += 1;
497 continue :state .end;
498 }
499 continue :c text[text_i];
500 },
501 'A'...'F' => |c| continue :c c - 'A' + 'a',
502 '0'...'9' => |c| {
503 const digit = c - '0';
504 parts[parts_i] = (std.math.mul(u16, parts[parts_i], 16) catch return .{
505 .overflow = text_i,
506 }) + digit;
507 if (digit_i == 4) return .{ .invalid_byte = text_i };
508 digit_i += 1;
509 text_i += 1;
510 if (text.len - text_i == 0) {
511 parts_i += 1;
512 continue :state .end;
513 }
514 continue :c text[text_i];
515 },
516 ':' => {
517 if (digit_i == 0) {
518 if (compress_start != null) return .{ .invalid_byte = text_i };
519 if (text_i == 0) {
520 text_i += 1;
521 if (text[text_i] != ':') return .{ .invalid_byte = text_i };
522 assert(parts_i == 0);
523 }
524 compress_start = parts_i;
525 text_i += 1;
526 if (text.len - text_i == 0) continue :state .end;
527 continue :c text[text_i];
528 } else {
529 parts_i += 1;
530 if (parts.len - parts_i == 0) continue :state .end;
531 digit_i = 0;
532 text_i += 1;
533 if (text.len - text_i == 0) return .incomplete;
534 continue :c text[text_i];
535 }
536 },
537 '%' => {
538 if (digit_i == 0) return .{ .invalid_byte = text_i };
539 parts_i += 1;
540 text_i += 1;
541 const name = text[text_i..];
542 if (name.len == 0) return .incomplete;
543 interface_name_text = name;
544 text_i = @intCast(text.len);
545 continue :state .end;
546 },
547 else => return .{ .invalid_byte = text_i },
548 },
549 .end => {
550 if (text.len - text_i != 0) return .{ .junk_after_end = text_i };
551 const remaining = parts.len - parts_i;
552 if (compress_start) |s| {
553 const src = parts[s..parts_i];
554 @memmove(parts[parts.len - src.len ..], src);
555 @memset(parts[s..][0..remaining], 0);
556 } else {
557 if (remaining != 0) return .incomplete;
558 }
559
560 // Workaround that can be removed when this proposal is
561 // implemented https://github.com/ziglang/zig/issues/19755
562 if ((comptime @import("builtin").cpu.arch.endian()) != .big) {
563 for (&parts) |*part| part.* = @byteSwap(part.*);
564 }
565
566 return .{ .success = .{
567 .bytes = @bitCast(parts),
568 .interface_name = interface_name_text,
569 } };
570 },
571 }
572 }
573
574 pub const FromAddressError = Interface.NameError;
575
576 pub fn fromAddress(a: *const Ip6Address, io: Io) FromAddressError!Unresolved {
577 if (a.interface.isNone()) return .{
578 .bytes = a.bytes,
579 .interface_name = null,
580 };
581 return .{
582 .bytes = a.bytes,
583 .interface_name = try a.interface.name(io),
584 };
585 }
586
587 pub fn format(u: *const Unresolved, w: *Io.Writer) Io.Writer.Error!void {
588 const bytes = &u.bytes;
589 if (std.mem.eql(u8, bytes[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
590 try w.print("::ffff:{d}.{d}.{d}.{d}", .{ bytes[12], bytes[13], bytes[14], bytes[15] });
591 } else {
592 const parts: [8]u16 = .{
593 std.mem.readInt(u16, bytes[0..2], .big),
594 std.mem.readInt(u16, bytes[2..4], .big),
595 std.mem.readInt(u16, bytes[4..6], .big),
596 std.mem.readInt(u16, bytes[6..8], .big),
597 std.mem.readInt(u16, bytes[8..10], .big),
598 std.mem.readInt(u16, bytes[10..12], .big),
599 std.mem.readInt(u16, bytes[12..14], .big),
600 std.mem.readInt(u16, bytes[14..16], .big),
601 };
602
603 // Find the longest zero run
604 var longest_start: usize = 8;
605 var longest_len: usize = 0;
606 var current_start: usize = 0;
607 var current_len: usize = 0;
608
609 for (parts, 0..) |part, i| {
610 if (part == 0) {
611 if (current_len == 0) {
612 current_start = i;
613 }
614 current_len += 1;
615 if (current_len > longest_len) {
616 longest_start = current_start;
617 longest_len = current_len;
618 }
619 } else {
620 current_len = 0;
621 }
622 }
623
624 // Only compress if the longest zero run is 2 or more
625 if (longest_len < 2) {
626 longest_start = 8;
627 longest_len = 0;
628 }
629
630 var i: usize = 0;
631 var abbrv = false;
632 while (i < parts.len) : (i += 1) {
633 if (i == longest_start) {
634 // Emit "::" for the longest zero run
635 if (!abbrv) {
636 try w.writeAll(if (i == 0) "::" else ":");
637 abbrv = true;
638 }
639 i += longest_len - 1; // Skip the compressed range
640 continue;
641 }
642 if (abbrv) {
643 abbrv = false;
644 }
645 try w.print("{x}", .{parts[i]});
646 if (i != parts.len - 1) {
647 try w.writeAll(":");
648 }
649 }
650 }
651 if (u.interface_name) |n| try w.print("%{s}", .{n});
652 }
653 };
654
655 pub const ParseError = error{
656 /// If this is returned, more detailed diagnostics can be obtained by
657 /// calling `Ip6Address.Parsed.init`.
658 ParseFailed,
659 /// If this is returned, the IPv6 address had a scope id on it ("%foo"
660 /// at the end) which requires calling `resolve`.
661 UnresolvedScope,
662 };
663
664 /// This is a pure function but it cannot handle IPv6 addresses that have
665 /// scope ids ("%foo" at the end). To also handle those, `resolve` must be
666 /// called instead, or the lower level `Unresolved` API may be used.
667 pub fn parse(buffer: []const u8, port: u16) ParseError!Ip6Address {
668 switch (Unresolved.parse(buffer)) {
669 .success => |p| return .{
670 .bytes = p.bytes,
671 .port = port,
672 .interface = if (p.interface_name != null) return error.UnresolvedScope else .none,
673 },
674 else => return error.ParseFailed,
675 }
676 return .{ .ip6 = try Ip6Address.parse(buffer, port) };
677 }
678
679 pub const ResolveError = error{
680 /// If this is returned, more detailed diagnostics can be obtained by
681 /// calling the `Parsed.init` function.
682 ParseFailed,
683 /// The interface name is longer than the host operating system supports.
684 NameTooLong,
685 } || Interface.Name.ResolveError;
686
687 /// This function requires an `Io` parameter because it must query the operating
688 /// system to convert interface name to index. For example, in
689 /// "fe80::e0e:76ff:fed4:cf22%eno1", "eno1" must be resolved to an index by
690 /// creating a socket and then using an `ioctl` syscall.
691 pub fn resolve(io: Io, buffer: []const u8, port: u16) ResolveError!Ip6Address {
692 return switch (Unresolved.parse(buffer)) {
693 .success => |p| return .{
694 .bytes = p.bytes,
695 .port = port,
696 .interface = i: {
697 const text = p.interface_name orelse break :i .none;
698 const name: Interface.Name = try .fromSlice(text);
699 break :i try name.resolve(io);
700 },
701 },
702 else => return error.ParseFailed,
703 };
704 }
705
706 pub const FormatError = Io.Writer.Error || Unresolved.FromAddressError;
707
708 /// Includes the optional scope ("%foo" at the end).
709 ///
710 /// See `format` for an alternative that omits scopes and does
711 /// not require an `Io` parameter.
712 pub fn formatResolved(a: Ip6Address, io: Io, w: *Io.Writer) FormatError!void {
713 const u: Unresolved = try .fromAddress(io);
714 try w.print("[{f}]:{d}", .{ u, a.port });
715 }
716
717 /// See `formatResolved` for an alternative that additionally prints the optional
718 /// scope at the end of addresses and requires an `Io` parameter.
719 pub fn format(a: Ip6Address, w: *Io.Writer) Io.Writer.Error!void {
720 const u: Unresolved = .{
721 .bytes = a.bytes,
722 .interface_name = null,
723 };
724 try w.print("[{f}]:{d}", .{ u, a.port });
725 }
726
727 pub fn eql(a: Ip6Address, b: Ip6Address) bool {
728 return a.port == b.port and std.mem.eql(u8, &a.bytes, &b.bytes);
729 }
730
731 pub fn isMultiCast(a: Ip6Address) bool {
732 return a.bytes[0] == 0xff;
733 }
734
735 pub fn isLinkLocal(a: Ip6Address) bool {
736 const b = &a.bytes;
737 return b[0] == 0xfe and (b[1] & 0xc0) == 0x80;
738 }
739
740 pub fn isLoopBack(a: Ip6Address) bool {
741 const b = &a.bytes;
742 return b[0] == 0 and b[1] == 0 and
743 b[2] == 0 and
744 b[12] == 0 and b[13] == 0 and
745 b[14] == 0 and b[15] == 1;
746 }
747
748 pub fn isSiteLocal(a: Ip6Address) bool {
749 const b = &a.bytes;
750 return b[0] == 0xfe and (b[1] & 0xc0) == 0xc0;
751 }
752
753 pub fn policy(a: Ip6Address) *const Policy {
754 const b = &a.bytes;
755 for (&defined_policies) |*p| {
756 if (!std.mem.eql(u8, b[0..p.len], p.addr[0..p.len])) continue;
757 if ((b[p.len] & p.mask) != p.addr[p.len]) continue;
758 return p;
759 }
760 unreachable;
761 }
762
763 pub fn scope(a: Ip6Address) u8 {
764 if (isMultiCast(a)) return a.bytes[1] & 15;
765 if (isLinkLocal(a)) return 2;
766 if (isLoopBack(a)) return 2;
767 if (isSiteLocal(a)) return 5;
768 return 14;
769 }
770
771 const defined_policies = [_]Policy{
772 .{
773 .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01".*,
774 .len = 15,
775 .mask = 0xff,
776 .prec = 50,
777 .label = 0,
778 },
779 .{
780 .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00".*,
781 .len = 11,
782 .mask = 0xff,
783 .prec = 35,
784 .label = 4,
785 },
786 .{
787 .addr = "\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*,
788 .len = 1,
789 .mask = 0xff,
790 .prec = 30,
791 .label = 2,
792 },
793 .{
794 .addr = "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*,
795 .len = 3,
796 .mask = 0xff,
797 .prec = 5,
798 .label = 5,
799 },
800 .{
801 .addr = "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*,
802 .len = 0,
803 .mask = 0xfe,
804 .prec = 3,
805 .label = 13,
806 },
807 // These are deprecated and/or returned to the address
808 // pool, so despite the RFC, treating them as special
809 // is probably wrong.
810 // { "", 11, 0xff, 1, 3 },
811 // { "\xfe\xc0", 1, 0xc0, 1, 11 },
812 // { "\x3f\xfe", 1, 0xff, 1, 12 },
813 // Last rule must match all addresses to stop loop.
814 .{
815 .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".*,
816 .len = 0,
817 .mask = 0,
818 .prec = 40,
819 .label = 1,
820 },
821 };
822};
823
824pub const UnixAddress = struct {
825 path: []const u8,
826
827 pub const max_len = 108;
828
829 pub const InitError = error{NameTooLong};
830
831 pub fn init(p: []const u8) InitError!UnixAddress {
832 if (p.len > max_len) return error.NameTooLong;
833 return .{ .path = p };
834 }
835
836 pub const ListenError = error{
837 AddressFamilyUnsupported,
838 AddressInUse,
839 NetworkDown,
840 SystemResources,
841 SymLinkLoop,
842 FileNotFound,
843 NotDir,
844 ReadOnlyFileSystem,
845 ProcessFdQuotaExceeded,
846 SystemFdQuotaExceeded,
847 AccessDenied,
848 PermissionDenied,
849 AddressUnavailable,
850 } || Io.Cancelable || Io.UnexpectedError;
851
852 pub const ListenOptions = struct {
853 /// How many connections the kernel will accept on the application's behalf.
854 /// If more than this many connections pool in the kernel, clients will start
855 /// seeing "Connection refused".
856 kernel_backlog: u31 = default_kernel_backlog,
857 };
858
859 pub fn listen(ua: *const UnixAddress, io: Io, options: ListenOptions) ListenError!Server {
860 assert(ua.path.len <= max_len);
861 return .{ .socket = .{
862 .handle = try io.vtable.netListenUnix(io.userdata, ua, options),
863 .address = .{ .ip4 = .loopback(0) },
864 } };
865 }
866
867 pub const ConnectError = error{
868 SystemResources,
869 ProcessFdQuotaExceeded,
870 SystemFdQuotaExceeded,
871 AddressFamilyUnsupported,
872 ProtocolUnsupportedBySystem,
873 ProtocolUnsupportedByAddressFamily,
874 SocketModeUnsupported,
875 AccessDenied,
876 PermissionDenied,
877 SymLinkLoop,
878 FileNotFound,
879 NotDir,
880 ReadOnlyFileSystem,
881 WouldBlock,
882 NetworkDown,
883 } || Io.Cancelable || Io.UnexpectedError;
884
885 pub fn connect(ua: *const UnixAddress, io: Io) ConnectError!Stream {
886 assert(ua.path.len <= max_len);
887 return .{ .socket = .{
888 .handle = try io.vtable.netConnectUnix(io.userdata, ua),
889 .address = .{ .ip4 = .loopback(0) },
890 } };
891 }
892};
893
894pub const ReceiveFlags = packed struct(u8) {
895 oob: bool = false,
896 peek: bool = false,
897 trunc: bool = false,
898 _: u5 = 0,
899};
900
901pub const IncomingMessage = struct {
902 /// Populated by receive functions.
903 from: IpAddress,
904 /// Populated by receive functions, points into the caller-supplied buffer.
905 data: []u8,
906 /// Supplied by caller before calling receive functions; mutated by receive
907 /// functions.
908 control: []u8,
909 /// Populated by receive functions.
910 flags: Flags,
911
912 /// Useful for initializing before calling `receiveManyTimeout`.
913 pub const init: IncomingMessage = .{
914 .from = undefined,
915 .data = undefined,
916 .control = &.{},
917 .flags = undefined,
918 };
919
920 pub const Flags = packed struct(u8) {
921 /// indicates end-of-record; the data returned completed a record
922 /// (generally used with sockets of type SOCK_SEQPACKET).
923 eor: bool,
924 /// indicates that the trailing portion of a datagram was discarded
925 /// because the datagram was larger than the buffer supplied.
926 trunc: bool,
927 /// indicates that some control data was discarded due to lack of
928 /// space in the buffer for ancil‐ lary data.
929 ctrunc: bool,
930 /// indicates expedited or out-of-band data was received.
931 oob: bool,
932 /// indicates that no data was received but an extended error from the
933 /// socket error queue.
934 errqueue: bool,
935 _: u3 = 0,
936 };
937};
938
939pub const OutgoingMessage = struct {
940 address: *const IpAddress,
941 data_ptr: [*]const u8,
942 /// Initialized with how many bytes of `data_ptr` to send. After sending
943 /// succeeds, replaced with how many bytes were actually sent.
944 data_len: usize,
945 control: []const u8 = &.{},
946};
947
948pub const SendFlags = packed struct(u8) {
949 confirm: bool = false,
950 dont_route: bool = false,
951 eor: bool = false,
952 oob: bool = false,
953 fastopen: bool = false,
954 _: u3 = 0,
955};
956
957pub const Interface = struct {
958 /// Value 0 indicates `none`.
959 index: u32,
960
961 pub const none: Interface = .{ .index = 0 };
962
963 pub const Name = struct {
964 bytes: [max_len:0]u8,
965
966 pub const max_len = if (@TypeOf(std.posix.IFNAMESIZE) == void) 0 else std.posix.IFNAMESIZE - 1;
967
968 pub fn toSlice(n: *const Name) []const u8 {
969 return std.mem.sliceTo(&n.bytes, 0);
970 }
971
972 pub fn fromSlice(bytes: []const u8) error{NameTooLong}!Name {
973 if (bytes.len > max_len) return error.NameTooLong;
974 return .fromSliceUnchecked(bytes);
975 }
976
977 /// Asserts bytes.len fits in `max_len`.
978 pub fn fromSliceUnchecked(bytes: []const u8) Name {
979 assert(bytes.len <= max_len);
980 var result: Name = undefined;
981 @memcpy(result.bytes[0..bytes.len], bytes);
982 result.bytes[bytes.len] = 0;
983 return result;
984 }
985
986 pub const ResolveError = error{
987 InterfaceNotFound,
988 AccessDenied,
989 SystemResources,
990 } || Io.UnexpectedError || Io.Cancelable;
991
992 /// Corresponds to "if_nametoindex" in libc.
993 pub fn resolve(n: *const Name, io: Io) ResolveError!Interface {
994 return io.vtable.netInterfaceNameResolve(io.userdata, n);
995 }
996 };
997
998 pub const NameError = Io.UnexpectedError || Io.Cancelable;
999
1000 /// Asserts not `none`.
1001 ///
1002 /// Corresponds to "if_indextoname" in libc.
1003 pub fn name(i: Interface, io: Io) NameError!Name {
1004 assert(i.index != 0);
1005 return io.vtable.netInterfaceName(io.userdata, i);
1006 }
1007
1008 pub fn isNone(i: Interface) bool {
1009 return i.index == 0;
1010 }
1011};
1012
1013/// An open port with unspecified protocol.
1014pub const Socket = struct {
1015 handle: Handle,
1016 /// Contains the resolved ephemeral port number if requested.
1017 address: IpAddress,
1018
1019 pub const Mode = enum {
1020 /// Provides sequenced, reliable, two-way, connection-based byte
1021 /// streams. An out-of-band data transmission mechanism may be
1022 /// supported.
1023 stream,
1024 /// Supports datagrams (connectionless, unreliable messages of a fixed
1025 /// maximum length).
1026 dgram,
1027 /// Provides a sequenced, reliable, two-way connection-based data
1028 /// transmission path for datagrams of fixed maximum length; a consumer
1029 /// is required to read an entire packet with each input system call.
1030 seqpacket,
1031 /// Provides raw network protocol access.
1032 raw,
1033 /// Provides a reliable datagram layer that does not guarantee ordering.
1034 rdm,
1035 };
1036
1037 /// Underlying platform-defined type which may or may not be
1038 /// interchangeable with a file system file descriptor.
1039 pub const Handle = switch (native_os) {
1040 .windows => std.os.windows.ws2_32.SOCKET,
1041 else => std.posix.fd_t,
1042 };
1043
1044 /// Leaves `address` in a valid state.
1045 pub fn close(s: *const Socket, io: Io) void {
1046 io.vtable.netClose(io.userdata, s.handle);
1047 }
1048
1049 pub const SendError = error{
1050 /// The socket type requires that message be sent atomically, and the
1051 /// size of the message to be sent made this impossible. The message
1052 /// was not transmitted, or was partially transmitted.
1053 MessageOversize,
1054 /// The output queue for a network interface was full. This generally indicates that the
1055 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
1056 /// this does not occur in Linux. Packets are just silently dropped when a device queue
1057 /// overflows.)
1058 ///
1059 /// This is also caused when there is not enough kernel memory available.
1060 SystemResources,
1061 /// No route to network.
1062 NetworkUnreachable,
1063 /// Network reached but no route to host.
1064 HostUnreachable,
1065 /// The local network interface used to reach the destination is offline.
1066 NetworkDown,
1067 /// The destination address is not listening. Can still occur for
1068 /// connectionless messages.
1069 ConnectionRefused,
1070 /// Operating system or protocol does not support the address family.
1071 AddressFamilyUnsupported,
1072 /// Another TCP Fast Open is already in progress.
1073 FastOpenAlreadyInProgress,
1074 /// Network session was unexpectedly closed by recipient.
1075 ConnectionResetByPeer,
1076 /// Local end has been shut down on a connection-oriented socket, or
1077 /// the socket was never connected.
1078 SocketUnconnected,
1079 /// An attempt was made to send to a network/broadcast address as
1080 /// though it was a unicast address.
1081 AccessDenied,
1082 } || Io.UnexpectedError || Io.Cancelable;
1083
1084 /// Transfers `data` to `dest`, connectionless, in one packet.
1085 pub fn send(s: *const Socket, io: Io, dest: *const IpAddress, data: []const u8) SendError!void {
1086 var message: OutgoingMessage = .{ .address = dest, .data_ptr = data.ptr, .data_len = data.len };
1087 const err, const n = io.vtable.netSend(io.userdata, s.handle, (&message)[0..1], .{});
1088 if (n != 1) return err.?;
1089 if (message.data_len != data.len) return error.MessageOversize;
1090 }
1091
1092 pub fn sendMany(s: *const Socket, io: Io, messages: []OutgoingMessage, flags: SendFlags) SendError!void {
1093 return io.vtable.netSend(io.userdata, s.handle, messages, flags);
1094 }
1095
1096 pub const ReceiveError = error{
1097 /// Insufficient memory or other resource internal to the operating system.
1098 SystemResources,
1099 /// Per-process limit on the number of open file descriptors has been reached.
1100 ProcessFdQuotaExceeded,
1101 /// System-wide limit on the total number of open files has been reached.
1102 SystemFdQuotaExceeded,
1103 /// Local end has been shut down on a connection-oriented socket, or
1104 /// the socket was never connected.
1105 SocketUnconnected,
1106 /// The socket type requires that message be sent atomically, and the
1107 /// size of the message to be sent made this impossible. The message
1108 /// was not transmitted, or was partially transmitted.
1109 MessageOversize,
1110 /// Network connection was unexpectedly closed by sender.
1111 ConnectionResetByPeer,
1112 /// The local network interface used to reach the destination is offline.
1113 NetworkDown,
1114 } || Io.UnexpectedError || Io.Cancelable;
1115
1116 /// Waits for data. Connectionless.
1117 ///
1118 /// See also:
1119 /// * `receiveTimeout`
1120 pub fn receive(s: *const Socket, io: Io, buffer: []u8) ReceiveError!IncomingMessage {
1121 var message: IncomingMessage = .init;
1122 const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, .none);
1123 if (maybe_err) |err| switch (err) {
1124 // No timeout is passed to `netReceieve`, so it must not return timeout related errors.
1125 error.Timeout, error.UnsupportedClock => unreachable,
1126 else => |e| return e,
1127 };
1128 assert(1 == count);
1129 return message;
1130 }
1131
1132 pub const ReceiveTimeoutError = ReceiveError || Io.Timeout.Error;
1133
1134 /// Waits for data. Connectionless.
1135 ///
1136 /// Returns `error.Timeout` if no message arrives early enough.
1137 ///
1138 /// See also:
1139 /// * `receive`
1140 /// * `receiveManyTimeout`
1141 pub fn receiveTimeout(
1142 s: *const Socket,
1143 io: Io,
1144 buffer: []u8,
1145 timeout: Io.Timeout,
1146 ) ReceiveTimeoutError!IncomingMessage {
1147 var message: IncomingMessage = .init;
1148 const maybe_err, const count = io.vtable.netReceive(io.userdata, s.handle, (&message)[0..1], buffer, .{}, timeout);
1149 if (maybe_err) |err| return err;
1150 assert(1 == count);
1151 return message;
1152 }
1153
1154 /// Waits until at least one message is delivered, possibly returning more
1155 /// than one message. Connectionless.
1156 ///
1157 /// Returns number of messages received, or `error.Timeout` if no message
1158 /// arrives early enough.
1159 ///
1160 /// See also:
1161 /// * `receive`
1162 /// * `receiveTimeout`
1163 pub fn receiveManyTimeout(
1164 s: *const Socket,
1165 io: Io,
1166 /// Function assumes each element has initialized `control` field.
1167 /// Initializing with `IncomingMessage.init` may be helpful.
1168 message_buffer: []IncomingMessage,
1169 data_buffer: []u8,
1170 flags: ReceiveFlags,
1171 timeout: Io.Timeout,
1172 ) struct { ?ReceiveTimeoutError, usize } {
1173 return io.vtable.netReceive(io.userdata, s.handle, message_buffer, data_buffer, flags, timeout);
1174 }
1175};
1176
1177/// An open socket connection with a network protocol that guarantees
1178/// sequencing, delivery, and prevents repetition. Typically TCP or UNIX domain
1179/// socket.
1180pub const Stream = struct {
1181 socket: Socket,
1182
1183 const max_iovecs_len = 8;
1184
1185 pub fn close(s: *const Stream, io: Io) void {
1186 io.vtable.netClose(io.userdata, s.socket.handle);
1187 }
1188
1189 pub const Reader = struct {
1190 io: Io,
1191 interface: Io.Reader,
1192 stream: Stream,
1193 err: ?Error,
1194
1195 pub const Error = error{
1196 SystemResources,
1197 ConnectionResetByPeer,
1198 Timeout,
1199 SocketUnconnected,
1200 /// The file descriptor does not hold the required rights to read
1201 /// from it.
1202 AccessDenied,
1203 NetworkDown,
1204 } || Io.Cancelable || Io.UnexpectedError;
1205
1206 pub fn init(stream: Stream, io: Io, buffer: []u8) Reader {
1207 return .{
1208 .io = io,
1209 .interface = .{
1210 .vtable = &.{
1211 .stream = streamImpl,
1212 .readVec = readVec,
1213 },
1214 .buffer = buffer,
1215 .seek = 0,
1216 .end = 0,
1217 },
1218 .stream = stream,
1219 .err = null,
1220 };
1221 }
1222
1223 fn streamImpl(io_r: *Io.Reader, io_w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
1224 const dest = limit.slice(try io_w.writableSliceGreedy(1));
1225 var data: [1][]u8 = .{dest};
1226 const n = try readVec(io_r, &data);
1227 io_w.advance(n);
1228 return n;
1229 }
1230
1231 fn readVec(io_r: *Io.Reader, data: [][]u8) Io.Reader.Error!usize {
1232 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r));
1233 const io = r.io;
1234 var iovecs_buffer: [max_iovecs_len][]u8 = undefined;
1235 const dest_n, const data_size = try io_r.writableVector(&iovecs_buffer, data);
1236 const dest = iovecs_buffer[0..dest_n];
1237 assert(dest[0].len > 0);
1238 const n = io.vtable.netRead(io.userdata, r.stream.socket.handle, dest) catch |err| {
1239 r.err = err;
1240 return error.ReadFailed;
1241 };
1242 if (n == 0) {
1243 return error.EndOfStream;
1244 }
1245 if (n > data_size) {
1246 r.interface.end += n - data_size;
1247 return data_size;
1248 }
1249 return n;
1250 }
1251 };
1252
1253 pub const Writer = struct {
1254 io: Io,
1255 interface: Io.Writer,
1256 stream: Stream,
1257 err: ?Error = null,
1258
1259 pub const Error = error{
1260 /// Another TCP Fast Open is already in progress.
1261 FastOpenAlreadyInProgress,
1262 /// Network session was unexpectedly closed by recipient.
1263 ConnectionResetByPeer,
1264 /// The output queue for a network interface was full. This generally indicates that the
1265 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
1266 /// this does not occur in Linux. Packets are just silently dropped when a device queue
1267 /// overflows.)
1268 ///
1269 /// This is also caused when there is not enough kernel memory available.
1270 SystemResources,
1271 /// No route to network.
1272 NetworkUnreachable,
1273 /// Network reached but no route to host.
1274 HostUnreachable,
1275 /// The local network interface used to reach the destination is down.
1276 NetworkDown,
1277 /// The destination address is not listening.
1278 ConnectionRefused,
1279 /// The passed address didn't have the correct address family in its sa_family field.
1280 AddressFamilyUnsupported,
1281 /// Local end has been shut down on a connection-oriented socket, or
1282 /// the socket was never connected.
1283 SocketUnconnected,
1284 SocketNotBound,
1285 } || Io.UnexpectedError || Io.Cancelable;
1286
1287 pub fn init(stream: Stream, io: Io, buffer: []u8) Writer {
1288 return .{
1289 .io = io,
1290 .stream = stream,
1291 .interface = .{
1292 .vtable = &.{ .drain = drain },
1293 .buffer = buffer,
1294 },
1295 };
1296 }
1297
1298 fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
1299 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
1300 const io = w.io;
1301 const buffered = io_w.buffered();
1302 const handle = w.stream.socket.handle;
1303 const n = io.vtable.netWrite(io.userdata, handle, buffered, data, splat) catch |err| {
1304 w.err = err;
1305 return error.WriteFailed;
1306 };
1307 return io_w.consume(n);
1308 }
1309 };
1310
1311 pub fn reader(stream: Stream, io: Io, buffer: []u8) Reader {
1312 return .init(stream, io, buffer);
1313 }
1314
1315 pub fn writer(stream: Stream, io: Io, buffer: []u8) Writer {
1316 return .init(stream, io, buffer);
1317 }
1318};
1319
1320pub const Server = struct {
1321 socket: Socket,
1322
1323 pub fn deinit(s: *Server, io: Io) void {
1324 s.socket.close(io);
1325 s.* = undefined;
1326 }
1327
1328 pub const AcceptError = error{
1329 /// The per-process limit on the number of open file descriptors has been reached.
1330 ProcessFdQuotaExceeded,
1331 /// The system-wide limit on the total number of open files has been reached.
1332 SystemFdQuotaExceeded,
1333 /// Not enough free memory. This often means that the memory allocation is limited
1334 /// by the socket buffer limits, not by the system memory.
1335 SystemResources,
1336 /// Either `listen` was never called, or `shutdown` was called (possibly while
1337 /// this call was blocking). This allows `shutdown` to be used as a concurrent
1338 /// cancellation mechanism.
1339 SocketNotListening,
1340 /// The network subsystem has failed.
1341 NetworkDown,
1342 /// No connection is already queued and ready to be accepted, and
1343 /// the socket is configured as non-blocking.
1344 WouldBlock,
1345 /// An incoming connection was indicated, but was subsequently terminated by the
1346 /// remote peer prior to accepting the call.
1347 ConnectionAborted,
1348 /// Firewall rules forbid connection.
1349 BlockedByFirewall,
1350 ProtocolFailure,
1351 } || Io.UnexpectedError || Io.Cancelable;
1352
1353 /// Blocks until a client connects to the server.
1354 pub fn accept(s: *Server, io: Io) AcceptError!Stream {
1355 return io.vtable.netAccept(io.userdata, s.socket.handle);
1356 }
1357};
1358
1359test "parsing IPv6 addresses" {
1360 try testIp6Parse("fe80::e0e:76ff:fed4:cf22%eno1");
1361 try testIp6Parse("2001:db8::1");
1362 try testIp6ParseTransform("2001:db8::1", "2001:0db8:0000:0000:0000:0000:0000:0001");
1363 try testIp6Parse("::1");
1364 try testIp6Parse("::");
1365 try testIp6Parse("fe80::1");
1366 try testIp6Parse("fe80::abcd:ef12%3");
1367 try testIp6Parse("ff02::");
1368 try testIp6Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
1369}
1370
1371fn testIp6Parse(input: []const u8) !void {
1372 return testIp6ParseTransform(input, input);
1373}
1374
1375fn testIp6ParseTransform(expected: []const u8, input: []const u8) !void {
1376 const ua = switch (Ip6Address.Unresolved.parse(input)) {
1377 .success => |p| p,
1378 else => |x| {
1379 std.debug.print("failed to parse \"{s}\": {any}\n", .{ input, x });
1380 return error.TestFailed;
1381 },
1382 };
1383 var buffer: [100]u8 = undefined;
1384 const result = try std.fmt.bufPrint(&buffer, "{f}", .{ua});
1385 try std.testing.expectEqualStrings(expected, result);
1386}
1387
1388test {
1389 _ = HostName;
1390 _ = @import("net/test.zig");
1391}