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