std.posix.accept: improve EINVAL's error name from SocketNotListening to SocketOrAddrLenIncorrect.
Zig Version
0.12.0
Steps to Reproduce and Observed Output
Error Message
error: SocketNotListening
/home/randomuser/zig/0.12.0/files/lib/std/posix.zig:3836:27: 0x1035876 in accept (zig-posix_accept_err)
.INVAL => return error.SocketNotListening,
^
/home/randomuser/Projects/Zig/zig-posix_accept_err/src/main.zig:20:22: 0x1034e55 in main (zig-posix_accept_err)
const conn = try posix.accept(
Steps to Reproduce
Using the main.zig file shown below, run the compiled binary (I used zig build run but any method works).
Now using a tcp client of your choice, connect to the specified port: nc 127.0.0.1 9090.
const std = @import("std");
const net = std.net;
const posix = std.posix;
pub fn main() !void {
const laddr = net.Address.initIp4(.{ 127, 0, 0, 1 }, 9090);
const socket = try posix.socket(
posix.AF.INET,
posix.SOCK.STREAM,
0,
);
defer posix.close(socket);
try posix.bind(socket, &laddr.any, laddr.getOsSockLen());
try posix.listen(socket, 9);
var client: net.Address = undefined;
var client_len: posix.socklen_t = undefined;
{
const conn = try posix.accept(
socket,
&client.any,
&client_len,
0,
);
errdefer posix.close(socket);
errdefer std.process.exit(1);
defer posix.close(conn);
}
}
Expected Output
Looking at the man pages for the accept function, it states: EINVAL Socket is not listening for connections, or addrlen is invalid (e.g., is negative).
I see that this opengroup spec seems to not have the addrlen stipulation.
While I understand that using undefined as the value for addrlen is bad practice and inherently incorrect, I do think that there might be value in having the error name better encapsulate the potential root causes. In my case, the socket was listening but I had passed the wrong addrlen.
The caveat here being that: (a) I am a beginner to the sockets API on linux, so I spent a considerable amount of time going down the rabbit hole of trying to find out why my socket wasn't listening even though tools like ss stated it was.
It wasn't until I ran it with strace that I saw it returned EINVAL and had a better idea of where to look for fixing it.
I'm not sure that renaming it is a best case scenario, and quite frankly, I'm sure those that are experienced with linux syscalls and the sockets api might just know that this is a potential source for errors, but if renaming the error can help save some debug time and be more transparent, then I'd hate to not at least suggest the idea.
More resources:
- ziggit.dev original issue: https://ziggit.dev/t/net-zig-passing-in-an-undefined-net-address-for-posix-accepts-client-addr/4003?u=dalba-sudo.
I can see how Zig's error mapping has created confusion in your case. "EINVAL" here can mean the socket is an invalid state ("not listening for connections"), or some of the syscall parameters were invalid. Or according to this man page if the "flags" in the accept4 version of the call are invalid, EINVAL can also be returned. And that's just on Linux. Other systems with Posix-ish APIs that might back this Zig API might have slightly different reasons to return EINVAL.
I'm of the opinion that Zig's Posix wrappers should return minimally processed errnos, as the sematics of the error are outside of Zig's control. So Zig's accept wrapper code should probably return error.InvalidArgument (or just error.EINVAL to make it easier to understand with existing documentation.) Alternatively, Zig could do some simple checks (e.g., to make sure the provided addr_size parameter is positive) so it can return a specific error.InvalidSocketAddressSize in this case. Huh ... Zig currently seems to be assert'ing that the flags parameter is valid, so perhaps asserting that addr_size is positive would be one way to address this.
See #6389 for more general discussion about Zig's errno mapping strategy. This seems like another case to factor into that discussion.