summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Hay <steve.m.hay@googlemail.com>2013-09-05 15:18:20 +0100
committerSteve Hay <steve.m.hay@googlemail.com>2013-09-16 15:37:32 +0100
commitcd6a3131ad2bbd2f1f502de34f7a253780ee0bbd (patch)
tree0e216112ab7c43da621259d80f4ebdcf84a9af7b
parentc9beaf974d06db3d9c7faecba5bb3e9e2a91767d (diff)
downloadperl-cd6a3131ad2bbd2f1f502de34f7a253780ee0bbd.tar.gz
Fix stringification of $! in VC10+ builds where errno > sys_nerr
VC++ 2010 and above define a "POSIX supplement" of errno values ranging from EADDRINUSE (100) to EWOULDBLOCK (140), but sys_nerr is still 43 and strerror() returns "Unknown error" for them. We already avoid using strerror() if errno > sys_nerr, but we treat such values as Windows error codes (i.e. <winerror.h> values) and look up the corresponding system messages for them. There is no better plan for these POSIX supplement errno values, but they must be converted from <errno.h> values to <winerror.h> values first otherwise we will look up the wrong system message. In practice, we only expect to find errno > sys_nerr in the case of Windows sockets errors (we used to assign WSAGetLastError() to errno), so we simply convert Exxx values to WSAExxx values where possible, and use a default <winerror.h> value otherwise (namely, ERROR_INVALID_FUNCTION). This change fixes code such as this: perl -le "$!=107; print $!" which now outputs the expected "No connection could be made because the target machine actively refused it." rather than "The program stopped because an alternate diskette was not inserted." (in VC10+ builds). Also: Fix the spelling of ECANCELED.
-rw-r--r--win32/include/sys/errno2.h4
-rw-r--r--win32/win32.c20
-rw-r--r--win32/win32sck.c112
3 files changed, 126 insertions, 10 deletions
diff --git a/win32/include/sys/errno2.h b/win32/include/sys/errno2.h
index 076ed01b30..084e9e4f22 100644
--- a/win32/include/sys/errno2.h
+++ b/win32/include/sys/errno2.h
@@ -150,8 +150,8 @@
#ifndef ENOMORE
# define ENOMORE WSAENOMORE
#endif
-#ifndef ECANCELLED
-# define ECANCELLED WSAECANCELLED
+#ifndef ECANCELED
+# define ECANCELED WSAECANCELLED
#endif
#ifndef EINVALIDPROCTABLE
# define EINVALIDPROCTABLE WSAEINVALIDPROCTABLE
diff --git a/win32/win32.c b/win32/win32.c
index 0e8a89e6d6..3802a54b1e 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -2522,6 +2522,8 @@ win32_flock(int fd, int oper)
#undef LK_LEN
+extern int convert_wsa_error_to_errno(int wsaerr); /* in win32sck.c */
+
/* Get the errno value corresponding to the given err. This function is not
* intended to handle conversion of general GetLastError() codes. It only exists
* to translate Windows sockets error codes from WSAGetLastError(). Such codes
@@ -2584,10 +2586,14 @@ win32_feof(FILE *fp)
return (feof(fp));
}
+extern int convert_errno_to_wsa_error(int err); /* in win32sck.c */
+
/*
* Since the errors returned by the socket error function
* WSAGetLastError() are not known by the library routine strerror
- * we have to roll our own.
+ * we have to roll our own to cover the case of socket errors
+ * that could not be converted to regular errno values by
+ * get_last_socket_error() in win32/win32sck.c.
*/
DllExport char *
@@ -2601,6 +2607,18 @@ win32_strerror(int e)
dTHXa(NULL);
if (e < 0)
e = GetLastError();
+#if EADDRINUSE != WSAEADDRINUSE
+ /* VC10+ define a "POSIX supplement" of errno values ranging from
+ * EADDRINUSE (100) to EWOULDBLOCK (140), but sys_nerr is still 43 and
+ * strerror() returns "Unknown error" for them. We must therefore still
+ * roll our own messages for these codes, and additionally map them to
+ * corresponding Windows (sockets) error codes first to avoid getting
+ * the wrong system message.
+ */
+ else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
+ e = convert_errno_to_wsa_error(e);
+ }
+#endif
aTHXa(PERL_GET_THX);
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
diff --git a/win32/win32sck.c b/win32/win32sck.c
index 283b0f911a..be51a2b243 100644
--- a/win32/win32sck.c
+++ b/win32/win32sck.c
@@ -61,12 +61,6 @@ EndSockets(void)
WSACleanup();
}
-static int
-get_last_socket_error(void)
-{
- return convert_wsa_error_to_errno(WSAGetLastError());
-}
-
/* Translate WSAExxx values to corresponding Exxx values. Not all WSAExxx
* constants have corresponding Exxx constants in <errno.h> (even in VC++
* 2010 and above, which have expanded <errno.h> with more values), but any
@@ -174,7 +168,7 @@ convert_wsa_error_to_errno(int wsaerr)
case WSAENOMORE:
return ENOMORE;
case WSAECANCELLED:
- return ECANCELLED;
+ return ECANCELED;
case WSAEINVALIDPROCTABLE:
return EINVALIDPROCTABLE;
case WSAEINVALIDPROVIDER:
@@ -188,6 +182,110 @@ convert_wsa_error_to_errno(int wsaerr)
return wsaerr;
}
+/* Translate Exxx values in the POSIX supplement range defined in VC++ 2010 and
+ * above (EADDRINUSE <= err <= EWOULDBLOCK) to corresponding WSAExxx values. Not
+ * all such Exxx constants have corresponding WSAExxx constants in <winsock2.h>;
+ * we just use ERROR_INVALID_FUNCTION for those that are missing but do not
+ * really expect to encounter them anyway.
+ * Other Exxx values (err < sys_nerr) are returned unchanged.
+ */
+int
+convert_errno_to_wsa_error(int err)
+{
+ switch (err) {
+ case EADDRINUSE:
+ return WSAEADDRINUSE;
+ case EADDRNOTAVAIL:
+ return WSAEADDRNOTAVAIL;
+ case EAFNOSUPPORT:
+ return WSAEAFNOSUPPORT;
+ case EALREADY:
+ return WSAEALREADY;
+ case EBADMSG:
+ return ERROR_INVALID_FUNCTION;
+ case ECANCELED:
+ return WSAECANCELLED;
+ case ECONNABORTED:
+ return WSAECONNABORTED;
+ case ECONNREFUSED:
+ return WSAECONNREFUSED;
+ case ECONNRESET:
+ return WSAECONNRESET;
+ case EDESTADDRREQ:
+ return WSAEDESTADDRREQ;
+ case EHOSTUNREACH:
+ return WSAEHOSTUNREACH;
+ case EIDRM:
+ return ERROR_INVALID_FUNCTION;
+ case EINPROGRESS:
+ return WSAEINPROGRESS;
+ case EISCONN:
+ return WSAEISCONN;
+ case ELOOP:
+ return WSAELOOP;
+ case EMSGSIZE:
+ return WSAEMSGSIZE;
+ case ENETDOWN:
+ return WSAENETDOWN;
+ case ENETRESET:
+ return WSAENETRESET;
+ case ENETUNREACH:
+ return WSAENETUNREACH;
+ case ENOBUFS:
+ return WSAENOBUFS;
+ case ENODATA:
+ return ERROR_INVALID_FUNCTION;
+ case ENOLINK:
+ return ERROR_INVALID_FUNCTION;
+ case ENOMSG:
+ return ERROR_INVALID_FUNCTION;
+ case ENOPROTOOPT:
+ return WSAENOPROTOOPT;
+ case ENOSR:
+ return ERROR_INVALID_FUNCTION;
+ case ENOSTR:
+ return ERROR_INVALID_FUNCTION;
+ case ENOTCONN:
+ return WSAENOTCONN;
+ case ENOTRECOVERABLE:
+ return ERROR_INVALID_FUNCTION;
+ case ENOTSOCK:
+ return WSAENOTSOCK;
+ case ENOTSUP:
+ return ERROR_INVALID_FUNCTION;
+ case EOPNOTSUPP:
+ return WSAEOPNOTSUPP;
+ case EOTHER:
+ return ERROR_INVALID_FUNCTION;
+ case EOVERFLOW:
+ return ERROR_INVALID_FUNCTION;
+ case EOWNERDEAD:
+ return ERROR_INVALID_FUNCTION;
+ case EPROTO:
+ return ERROR_INVALID_FUNCTION;
+ case EPROTONOSUPPORT:
+ return WSAEPROTONOSUPPORT;
+ case EPROTOTYPE:
+ return WSAEPROTOTYPE;
+ case ETIME:
+ return ERROR_INVALID_FUNCTION;
+ case ETIMEDOUT:
+ return WSAETIMEDOUT;
+ case ETXTBSY:
+ return ERROR_INVALID_FUNCTION;
+ case EWOULDBLOCK:
+ return WSAEWOULDBLOCK;
+ }
+
+ return err;
+}
+
+static int
+get_last_socket_error(void)
+{
+ return convert_wsa_error_to_errno(WSAGetLastError());
+}
+
void
start_sockets(void)
{