Zig Version
0.16.0-dev.2624+1eeec5be2
Steps to Reproduce and Observed Behavior
Currently, AFAICS, the only way to send outbound control messages over a socket is via Io.net.OutgoingMessage and Io.net.Socket.sendMany(). If the socket happens to be a connected (stream-mode) AF_UNIX socket, OutgoingMessage requires setting a destination address: IpAddress, and setting any arbitrary address there (even unspecified) will then cause the send to fail with EISCONN.
As a local hack, I've gotten this working correctly for my code by patching in support for setting .address = null, but I'm not really sure if that's even an acceptable temporary hack, given that the address field is IP-only and there are other issues with AF_UNIX in general right now, but FWIW:
commit c0ced9011019f7d342dd695729aae2ecedb02ec9
Author: Brandon L Black <blblack@gmail.com>
Date: Tue Feb 17 13:25:29 2026 -0600
Allow null address in Io.net.OutgoingMessage
OutgoingMessage is the only interface for sending control
messages, and in order for that to work on a connected unix
socket, one must be able to avoid setting a specific destination
address (setting an arbitrary one like loopback/unspecified
results in EISCONN).
diff --git lib/std/Io/Threaded.zig lib/std/Io/Threaded.zig
index a7e9668309..ca79e1d021 100644
--- lib/std/Io/Threaded.zig
+++ lib/std/Io/Threaded.zig
@@ -13073,8 +13073,8 @@ fn netSendOne(
var addr: PosixAddress = undefined;
var iovec: posix.iovec_const = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
const msg: posix.msghdr_const = .{
- .name = &addr.any,
- .namelen = addressToPosix(message.address, &addr),
+ .name = if (message.address != null) &addr.any else null,
+ .namelen = if (message.address) |ma| addressToPosix(ma, &addr) else 0,
.iov = (&iovec)[0..1],
.iovlen = 1,
// OS returns EINVAL if this pointer is invalid even if controllen is zero.
@@ -13183,8 +13183,8 @@ fn netSendMany(
iovec.* = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
msg.* = .{
.hdr = .{
- .name = &addr.any,
- .namelen = addressToPosix(message.address, addr),
+ .name = if (message.address != null) &addr.any else null,
+ .namelen = if (message.address) |ma| addressToPosix(ma, addr) else 0,
.iov = iovec[0..1],
.iovlen = 1,
.control = @constCast(message.control.ptr),
diff --git lib/std/Io/net.zig lib/std/Io/net.zig
index 51a8d6647b..b02c30c0ce 100644
--- lib/std/Io/net.zig
+++ lib/std/Io/net.zig
@@ -938,7 +938,8 @@ pub const IncomingMessage = struct {
};
pub const OutgoingMessage = struct {
- address: *const IpAddress,
+ /// null address allows for control messages on connected sockets
+ address: ?*const IpAddress,
data_ptr: [*]const u8,
/// Initialized with how many bytes of `data_ptr` to send. After sending
/// succeeds, replaced with how many bytes were actually sent.
Expected Behavior
To be able to send control messages over connected AF_UNIX sockets somehow via std.Io.