mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-socket: translate Winsock errors to errno on Windows
Winsock APIs (socket, bind, connect, accept, recv, send, ...) report
errors via WSAGetLastError() and do not set errno, so mrb_sys_fail
on Windows was reading a stale or zero errno. Result: every socket
failure raised SystemCallError with errno 0 ("Success") instead of
the appropriate Errno::* class.
Add mrb_hal_socket_set_errno_from_last_error() to the HAL:
- POSIX: no-op (failed calls already set errno)
- Windows: maps WSAGetLastError() to a POSIX errno via wsa_to_errno()
with 32 cases covering the common Winsock error codes; unmapped
codes fall back to EIO. Each case is #ifdef-guarded against older
MSVC CRTs that lack a particular Exxx.
In src/socket.c, route the 22 socket-API failure sites through a new
sock_sys_fail() helper that calls the HAL translator before
mrb_sys_fail. Also fix mrb_hal_socket_set_nonblock() on Windows,
which was returning -1 without setting errno after ioctlsocket
failure.
POSIX behavior unchanged (verified: TCPSocket connect refused ->
Errno::ECONNREFUSED, bind to privileged port -> Errno::EACCES, bad
sockopt -> Errno::EOPNOTSUPP).
Closes #6819, reported by Asmod4n.
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,16 @@ void mrb_hal_socket_init(mrb_state *mrb);
|
||||
/* Finalize socket subsystem (e.g., WSACleanup on Windows) */
|
||||
void mrb_hal_socket_final(mrb_state *mrb);
|
||||
|
||||
/*
|
||||
* Error Handling
|
||||
*/
|
||||
|
||||
/* Translate the most recent socket-API error into a POSIX errno value and
|
||||
* store it in errno. On Windows, this reads WSAGetLastError() and maps it;
|
||||
* on POSIX, this is a no-op (errno is already set by the failed call).
|
||||
* Call this immediately after a socket-API failure, before mrb_sys_fail. */
|
||||
void mrb_hal_socket_set_errno_from_last_error(void);
|
||||
|
||||
/*
|
||||
* Socket Control Operations
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,16 @@ mrb_hal_socket_final(mrb_state *mrb)
|
||||
/* No cleanup needed for POSIX sockets */
|
||||
}
|
||||
|
||||
/*
|
||||
* Error Handling
|
||||
*/
|
||||
|
||||
void
|
||||
mrb_hal_socket_set_errno_from_last_error(void)
|
||||
{
|
||||
/* POSIX socket calls already set errno on failure; nothing to do. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Socket Control Operations
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,124 @@ mrb_hal_socket_final(mrb_state *mrb)
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
/*
|
||||
* Error Handling
|
||||
*/
|
||||
|
||||
/* Map a Winsock error code to a POSIX errno value. Each case is guarded
|
||||
* with #ifdef so older MSVC CRTs that lack a particular Exxx still build;
|
||||
* unknown codes fall back to EIO so mrb_sys_fail still produces a non-zero
|
||||
* SystemCallError rather than reporting "success." */
|
||||
static int
|
||||
wsa_to_errno(int wsa_err)
|
||||
{
|
||||
switch (wsa_err) {
|
||||
case 0: return 0;
|
||||
#ifdef EINTR
|
||||
case WSAEINTR: return EINTR;
|
||||
#endif
|
||||
#ifdef EBADF
|
||||
case WSAEBADF: return EBADF;
|
||||
#endif
|
||||
#ifdef EACCES
|
||||
case WSAEACCES: return EACCES;
|
||||
#endif
|
||||
#ifdef EFAULT
|
||||
case WSAEFAULT: return EFAULT;
|
||||
#endif
|
||||
#ifdef EINVAL
|
||||
case WSAEINVAL: return EINVAL;
|
||||
#endif
|
||||
#ifdef EMFILE
|
||||
case WSAEMFILE: return EMFILE;
|
||||
#endif
|
||||
#ifdef EWOULDBLOCK
|
||||
case WSAEWOULDBLOCK: return EWOULDBLOCK;
|
||||
#endif
|
||||
#ifdef EINPROGRESS
|
||||
case WSAEINPROGRESS: return EINPROGRESS;
|
||||
#endif
|
||||
#ifdef EALREADY
|
||||
case WSAEALREADY: return EALREADY;
|
||||
#endif
|
||||
#ifdef ENOTSOCK
|
||||
case WSAENOTSOCK: return ENOTSOCK;
|
||||
#endif
|
||||
#ifdef EDESTADDRREQ
|
||||
case WSAEDESTADDRREQ: return EDESTADDRREQ;
|
||||
#endif
|
||||
#ifdef EMSGSIZE
|
||||
case WSAEMSGSIZE: return EMSGSIZE;
|
||||
#endif
|
||||
#ifdef EPROTOTYPE
|
||||
case WSAEPROTOTYPE: return EPROTOTYPE;
|
||||
#endif
|
||||
#ifdef ENOPROTOOPT
|
||||
case WSAENOPROTOOPT: return ENOPROTOOPT;
|
||||
#endif
|
||||
#ifdef EPROTONOSUPPORT
|
||||
case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
|
||||
#endif
|
||||
#ifdef EOPNOTSUPP
|
||||
case WSAEOPNOTSUPP: return EOPNOTSUPP;
|
||||
#endif
|
||||
#ifdef EAFNOSUPPORT
|
||||
case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
|
||||
case WSAEPFNOSUPPORT: return EAFNOSUPPORT;
|
||||
case WSAESOCKTNOSUPPORT: return EAFNOSUPPORT;
|
||||
#endif
|
||||
#ifdef EADDRINUSE
|
||||
case WSAEADDRINUSE: return EADDRINUSE;
|
||||
#endif
|
||||
#ifdef EADDRNOTAVAIL
|
||||
case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
|
||||
#endif
|
||||
#ifdef ENETDOWN
|
||||
case WSAENETDOWN: return ENETDOWN;
|
||||
#endif
|
||||
#ifdef ENETUNREACH
|
||||
case WSAENETUNREACH: return ENETUNREACH;
|
||||
#endif
|
||||
#ifdef ENETRESET
|
||||
case WSAENETRESET: return ENETRESET;
|
||||
#endif
|
||||
#ifdef ECONNABORTED
|
||||
case WSAECONNABORTED: return ECONNABORTED;
|
||||
#endif
|
||||
#ifdef ECONNRESET
|
||||
case WSAECONNRESET: return ECONNRESET;
|
||||
#endif
|
||||
#ifdef ENOBUFS
|
||||
case WSAENOBUFS: return ENOBUFS;
|
||||
#endif
|
||||
#ifdef EISCONN
|
||||
case WSAEISCONN: return EISCONN;
|
||||
#endif
|
||||
#ifdef ENOTCONN
|
||||
case WSAENOTCONN: return ENOTCONN;
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
case WSAETIMEDOUT: return ETIMEDOUT;
|
||||
#endif
|
||||
#ifdef ECONNREFUSED
|
||||
case WSAECONNREFUSED: return ECONNREFUSED;
|
||||
#endif
|
||||
#ifdef EHOSTUNREACH
|
||||
case WSAEHOSTUNREACH: return EHOSTUNREACH;
|
||||
#endif
|
||||
#ifdef ENAMETOOLONG
|
||||
case WSAENAMETOOLONG: return ENAMETOOLONG;
|
||||
#endif
|
||||
default: return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_hal_socket_set_errno_from_last_error(void)
|
||||
{
|
||||
errno = wsa_to_errno(WSAGetLastError());
|
||||
}
|
||||
|
||||
/*
|
||||
* Socket Control Operations
|
||||
*/
|
||||
@@ -54,6 +172,7 @@ mrb_hal_socket_set_nonblock(mrb_state *mrb, int fd, int nonblock)
|
||||
u_long mode = nonblock ? 1 : 0;
|
||||
int result = ioctlsocket(fd, FIONBIO, &mode);
|
||||
if (result != NO_ERROR) {
|
||||
mrb_hal_socket_set_errno_from_last_error();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -134,6 +134,16 @@ static inline const char *get_pf_name(int family) {
|
||||
|
||||
#define E_SOCKET_ERROR mrb_class_get_id(mrb, MRB_SYM(SocketError))
|
||||
|
||||
/* Raise a SystemCallError for the most recent socket-API failure.
|
||||
* On Windows the HAL translates WSAGetLastError() into errno first;
|
||||
* on POSIX errno is already set, so this is just mrb_sys_fail. */
|
||||
static mrb_noreturn void
|
||||
sock_sys_fail(mrb_state *mrb, const char *mesg)
|
||||
{
|
||||
mrb_hal_socket_set_errno_from_last_error();
|
||||
mrb_sys_fail(mrb, mesg);
|
||||
}
|
||||
|
||||
struct gen_addrinfo_args {
|
||||
struct RClass *klass;
|
||||
struct addrinfo *addrinfo;
|
||||
@@ -295,7 +305,7 @@ sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen)
|
||||
port = ntohs(port);
|
||||
mrb_value host = mrb_str_new_capa(mrb, NI_MAXHOST);
|
||||
if (getnameinfo(sa, salen, RSTRING_PTR(host), NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == -1)
|
||||
mrb_sys_fail(mrb, "getnameinfo");
|
||||
sock_sys_fail(mrb, "getnameinfo");
|
||||
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
|
||||
|
||||
mrb_value ary = mrb_ary_new_capa(mrb, 4);
|
||||
@@ -344,7 +354,7 @@ mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self)
|
||||
uid_t euid;
|
||||
int s = socket_fd(mrb, self);
|
||||
if (getpeereid(s, &euid, &egid) != 0)
|
||||
mrb_sys_fail(mrb, "getpeereid");
|
||||
sock_sys_fail(mrb, "getpeereid");
|
||||
|
||||
mrb_value ary = mrb_ary_new_capa(mrb, 2);
|
||||
mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)euid));
|
||||
@@ -371,7 +381,7 @@ mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self)
|
||||
socklen_t salen = sizeof(ss);
|
||||
|
||||
if (getpeername(socket_fd(mrb, self), (struct sockaddr*)&ss, &salen) != 0)
|
||||
mrb_sys_fail(mrb, "getpeername");
|
||||
sock_sys_fail(mrb, "getpeername");
|
||||
|
||||
return mrb_str_new(mrb, (char*)&ss, salen);
|
||||
}
|
||||
@@ -391,7 +401,7 @@ mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self)
|
||||
socklen_t salen = sizeof(ss);
|
||||
|
||||
if (getsockname(socket_fd(mrb, self), (struct sockaddr*)&ss, &salen) != 0)
|
||||
mrb_sys_fail(mrb, "getsockname");
|
||||
sock_sys_fail(mrb, "getsockname");
|
||||
|
||||
return mrb_str_new(mrb, (char*)&ss, salen);
|
||||
}
|
||||
@@ -636,7 +646,7 @@ mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self)
|
||||
socklen_t optlen = sizeof(opt);
|
||||
|
||||
if (getsockopt(s, (int)level, (int)optname, opt, &optlen) == -1)
|
||||
mrb_sys_fail(mrb, "getsockopt");
|
||||
sock_sys_fail(mrb, "getsockopt");
|
||||
mrb_int family = socket_family(s);
|
||||
mrb_value data = mrb_str_new(mrb, opt, optlen);
|
||||
mrb_value args[4] = {mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data};
|
||||
@@ -662,7 +672,7 @@ mrb_basicsocket_recv(mrb_state *mrb, mrb_value self)
|
||||
mrb_value buf = mrb_str_new_capa(mrb, maxlen);
|
||||
ssize_t n = recv(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags);
|
||||
if (n == -1)
|
||||
mrb_sys_fail(mrb, "recv");
|
||||
sock_sys_fail(mrb, "recv");
|
||||
mrb_str_resize(mrb, buf, (mrb_int)n);
|
||||
return buf;
|
||||
}
|
||||
@@ -686,7 +696,7 @@ mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self)
|
||||
mrb_value sa = mrb_str_new_capa(mrb, socklen);
|
||||
ssize_t n = recvfrom(socket_fd(mrb, self), RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags, (struct sockaddr*)RSTRING_PTR(sa), &socklen);
|
||||
if (n == -1)
|
||||
mrb_sys_fail(mrb, "recvfrom");
|
||||
sock_sys_fail(mrb, "recvfrom");
|
||||
mrb_str_resize(mrb, buf, (mrb_int)n);
|
||||
mrb_str_resize(mrb, sa, (mrb_int)socklen);
|
||||
|
||||
@@ -721,7 +731,7 @@ mrb_basicsocket_send(mrb_state *mrb, mrb_value self)
|
||||
n = sendto(socket_fd(mrb, self), RSTRING_PTR(mesg), (fsize_t)RSTRING_LEN(mesg), (int)flags, (const struct sockaddr*)RSTRING_PTR(dest), (fsize_t)RSTRING_LEN(dest));
|
||||
}
|
||||
if (n == -1)
|
||||
mrb_sys_fail(mrb, "send");
|
||||
sock_sys_fail(mrb, "send");
|
||||
return mrb_fixnum_value((mrb_int)n);
|
||||
}
|
||||
|
||||
@@ -743,7 +753,7 @@ mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self)
|
||||
int fd = socket_fd(mrb, self);
|
||||
|
||||
if (mrb_hal_socket_set_nonblock(mrb, fd, nonblocking) == -1)
|
||||
mrb_sys_fail(mrb, "set_nonblock");
|
||||
sock_sys_fail(mrb, "set_nonblock");
|
||||
|
||||
return mrb_nil_value();
|
||||
}
|
||||
@@ -801,7 +811,7 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
|
||||
|
||||
int s = socket_fd(mrb, self);
|
||||
if (setsockopt(s, (int)level, (int)optname, RSTRING_PTR(optval), (socklen_t)RSTRING_LEN(optval)) == -1)
|
||||
mrb_sys_fail(mrb, "setsockopt");
|
||||
sock_sys_fail(mrb, "setsockopt");
|
||||
return mrb_fixnum_value(0);
|
||||
}
|
||||
|
||||
@@ -822,7 +832,7 @@ mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self)
|
||||
|
||||
mrb_get_args(mrb, "|i", &how);
|
||||
if (shutdown(socket_fd(mrb, self), (int)how) != 0)
|
||||
mrb_sys_fail(mrb, "shutdown");
|
||||
sock_sys_fail(mrb, "shutdown");
|
||||
return mrb_fixnum_value(0);
|
||||
}
|
||||
|
||||
@@ -935,7 +945,7 @@ mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self)
|
||||
ssize_t n = recvfrom(fd, RSTRING_PTR(buf), (fsize_t)maxlen, (int)flags,
|
||||
(struct sockaddr*)&ss, &socklen);
|
||||
if (n == -1) {
|
||||
mrb_sys_fail(mrb, "recvfrom");
|
||||
sock_sys_fail(mrb, "recvfrom");
|
||||
}
|
||||
mrb_str_resize(mrb, buf, (mrb_int)n);
|
||||
|
||||
@@ -965,7 +975,7 @@ mrb_socket_gethostname(mrb_state *mrb, mrb_value cls)
|
||||
mrb_value buf = mrb_str_new_capa(mrb, (mrb_int)bufsize);
|
||||
|
||||
if (gethostname(RSTRING_PTR(buf), (fsize_t)bufsize) != 0)
|
||||
mrb_sys_fail(mrb, "gethostname");
|
||||
sock_sys_fail(mrb, "gethostname");
|
||||
mrb_str_resize(mrb, buf, (mrb_int)strlen(RSTRING_PTR(buf)));
|
||||
return buf;
|
||||
}
|
||||
@@ -985,7 +995,7 @@ mrb_socket_accept(mrb_state *mrb, mrb_value klass)
|
||||
mrb_get_args(mrb, "i", &s0);
|
||||
int s1 = (int)accept(s0, NULL, NULL);
|
||||
if (s1 == -1) {
|
||||
mrb_sys_fail(mrb, "accept");
|
||||
sock_sys_fail(mrb, "accept");
|
||||
}
|
||||
return mrb_fixnum_value(s1);
|
||||
}
|
||||
@@ -1003,7 +1013,7 @@ mrb_socket_accept2(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
int s1 = (int)accept(s0, (struct sockaddr*)RSTRING_PTR(sastr), &socklen);
|
||||
if (s1 == -1) {
|
||||
mrb_sys_fail(mrb, "accept");
|
||||
sock_sys_fail(mrb, "accept");
|
||||
}
|
||||
|
||||
mrb_str_resize(mrb, sastr, socklen);
|
||||
@@ -1026,7 +1036,7 @@ mrb_socket_bind(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
mrb_get_args(mrb, "iS", &s, &sastr);
|
||||
if (bind((int)s, (struct sockaddr*)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) {
|
||||
mrb_sys_fail(mrb, "bind");
|
||||
sock_sys_fail(mrb, "bind");
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
@@ -1045,7 +1055,7 @@ mrb_socket_connect(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
mrb_get_args(mrb, "iS", &s, &sastr);
|
||||
if (connect((int)s, (struct sockaddr*)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr)) == -1) {
|
||||
mrb_sys_fail(mrb, "connect");
|
||||
sock_sys_fail(mrb, "connect");
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
@@ -1063,7 +1073,7 @@ mrb_socket_listen(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
mrb_get_args(mrb, "ii", &s, &backlog);
|
||||
if (listen((int)s, (int)backlog) == -1) {
|
||||
mrb_sys_fail(mrb, "listen");
|
||||
sock_sys_fail(mrb, "listen");
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
@@ -1119,7 +1129,7 @@ mrb_socket_socketpair(mrb_state *mrb, mrb_value klass)
|
||||
mrb_get_args(mrb, "iii", &domain, &type, &protocol);
|
||||
|
||||
if (mrb_hal_socket_socketpair(mrb, (int)domain, (int)type, (int)protocol, sv) == -1) {
|
||||
mrb_sys_fail(mrb, "socketpair");
|
||||
sock_sys_fail(mrb, "socketpair");
|
||||
}
|
||||
|
||||
mrb_value ary = mrb_ary_new_capa(mrb, 2);
|
||||
@@ -1145,7 +1155,7 @@ mrb_socket_socket(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
int s = (int)socket((int)domain, (int)type, (int)protocol);
|
||||
if (s == -1)
|
||||
mrb_sys_fail(mrb, "socket");
|
||||
sock_sys_fail(mrb, "socket");
|
||||
return mrb_fixnum_value(s);
|
||||
}
|
||||
|
||||
@@ -1220,7 +1230,7 @@ mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self)
|
||||
}
|
||||
break;
|
||||
case SOCKET_ERROR: /* Error */
|
||||
mrb_sys_fail(mrb, "recv");
|
||||
sock_sys_fail(mrb, "recv");
|
||||
break;
|
||||
default:
|
||||
if (RSTRING_LEN(buf) != ret) {
|
||||
@@ -1263,7 +1273,7 @@ mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self)
|
||||
|
||||
int n = send(sd, RSTRING_PTR(str), (int)RSTRING_LEN(str), 0);
|
||||
if (n == SOCKET_ERROR)
|
||||
mrb_sys_fail(mrb, "send");
|
||||
sock_sys_fail(mrb, "send");
|
||||
return mrb_int_value(mrb, n);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user