Skip to content

Instantly share code, notes, and snippets.

@marler8997
Created April 22, 2026 16:34
Show Gist options
  • Select an option

  • Save marler8997/5ab75f9e90f68edef1cecaa1d1979060 to your computer and use it in GitHub Desktop.

Select an option

Save marler8997/5ab75f9e90f68edef1cecaa1d1979060 to your computer and use it in GitHub Desktop.
Zig 0.16.0 Missing Windows Network Errors
//! Minimal repro for three NTSTATUS codes that stdlib's AFD-based socket
//! functions should map but currently fall through to `windows.unexpectedStatus`:
//!
//! netConnectIpWindows / AFD.CONNECT: CONNECTION_REFUSED (0xc0000236)
//! netReadWindows / AFD.RECEIVE: CONNECTION_RESET (0xc000020d)
//! netWriteWindows / AFD.SEND: CONNECTION_RESET (0xc000020d)
//!
//! On an unpatched stdlib each scenario prints the raw NTSTATUS via
//! std.options.unexpected_error_tracing and surfaces `error.Unexpected`.
//! On a patched stdlib each scenario surfaces a specific Zig error
//! (ConnectionRefused / ConnectionResetByPeer).
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const io = init.io;
try scenario1_connectRefused(io);
try scenario2_readAfterReset(io);
try scenario3_writeAfterReset(io);
}
fn banner(comptime name: []const u8) void {
std.log.info("", .{});
std.log.info("=== {s} ===", .{name});
}
// AFD.CONNECT to an unused localhost port -> CONNECTION_REFUSED.
fn scenario1_connectRefused(io: std.Io) !void {
banner("connect to unused localhost port (expect CONNECTION_REFUSED)");
// Bind and immediately close a socket to discover a port nothing listens on.
const port = blk: {
var addr: std.Io.net.IpAddress = .{ .ip4 = .loopback(0) };
var probe = try std.Io.net.IpAddress.listen(&addr, io, .{});
defer probe.deinit(io);
break :blk probe.socket.address.ip4.port;
};
var target: std.Io.net.IpAddress = .{ .ip4 = .loopback(port) };
if (std.Io.net.IpAddress.connect(&target, io, .{ .mode = .stream })) |stream| {
stream.close(io);
std.log.err(" connect unexpectedly succeeded", .{});
} else |err| {
std.log.info(" connect -> error.{s}", .{@errorName(err)});
}
}
// AFD.RECEIVE on a socket whose peer sent RST -> CONNECTION_RESET.
// Trick: server writes more than the client reads; when client closes with
// unread data in its recv buffer, Windows' TCP stack sends RST. (SO_LINGER=0
// would be simpler but AFD doesn't expose it.)
fn scenario2_readAfterReset(io: std.Io) !void {
banner("read after peer RST (expect CONNECTION_RESET)");
var listen_addr: std.Io.net.IpAddress = .{ .ip4 = .loopback(0) };
var server = try std.Io.net.IpAddress.listen(&listen_addr, io, .{ .reuse_address = true });
defer server.deinit(io);
const thread = try std.Thread.spawn(.{}, rstClient, .{ io, server.socket.address.ip4.port });
var stream = try server.accept(io);
defer stream.close(io);
var write_buf: [64]u8 = undefined;
var writer = stream.writer(io, &write_buf);
try writer.interface.writeAll(&[_]u8{0xaa} ** 1024);
try writer.interface.flush();
thread.join();
var buf: [100]u8 = undefined;
var bufs: [1][]u8 = .{&buf};
if (io.vtable.netRead(io.userdata, stream.socket.handle, &bufs)) |n| {
std.log.info(" read -> n={}", .{n});
} else |err| {
std.log.info(" read -> error.{s}", .{@errorName(err)});
}
}
// AFD.SEND on a socket whose peer sent RST -> CONNECTION_RESET.
fn scenario3_writeAfterReset(io: std.Io) !void {
banner("write after peer RST (expect CONNECTION_RESET)");
var listen_addr: std.Io.net.IpAddress = .{ .ip4 = .loopback(0) };
var server = try std.Io.net.IpAddress.listen(&listen_addr, io, .{ .reuse_address = true });
defer server.deinit(io);
const thread = try std.Thread.spawn(.{}, rstClient, .{ io, server.socket.address.ip4.port });
var stream = try server.accept(io);
defer stream.close(io);
var write_buf: [64]u8 = undefined;
var writer = stream.writer(io, &write_buf);
try writer.interface.writeAll(&[_]u8{0xaa} ** 1024);
try writer.interface.flush();
thread.join();
// Drain any pending RST so the NEXT write clearly sees the reset connection.
var drain: [4]u8 = undefined;
var drain_bufs: [1][]u8 = .{&drain};
_ = io.vtable.netRead(io.userdata, stream.socket.handle, &drain_bufs) catch {};
const payload = &[_]u8{0xbb} ** 64;
const data: []const []const u8 = &.{payload};
if (io.vtable.netWrite(io.userdata, stream.socket.handle, &.{}, data, 1)) |n| {
std.log.info(" netWrite -> n={}", .{n});
} else |err| {
std.log.info(" netWrite -> error.{s}", .{@errorName(err)});
}
}
// Peer that connects, reads a single byte (proving the server's data arrived
// in its kernel recv buffer), then closes — leaving the remaining bytes
// unread. Windows sends RST from this side on close.
fn rstClient(io: std.Io, port: u16) void {
rstClientInner(io, port) catch |e| std.log.err("rstClient: {t}", .{e});
}
fn rstClientInner(io: std.Io, port: u16) !void {
var addr: std.Io.net.IpAddress = .{ .ip4 = .loopback(port) };
var stream = try std.Io.net.IpAddress.connect(&addr, io, .{ .mode = .stream });
defer stream.close(io);
var read_buf: [1]u8 = undefined;
var reader = stream.reader(io, &read_buf);
_ = reader.interface.takeByte() catch return reader.err.?;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment