From 077656c5b9739939019cbd315d518fb9804d6343 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Thu, 14 Nov 2019 20:09:23 +0100
Subject: [PATCH 1/4] mingw: make sure `errno` is set when socket operations
 fail

Noticed by Jeff Hostetler.

This closes https://github.com/git-for-windows/git/issues/2404

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 compat/mingw.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 119 insertions(+), 6 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index 617ceb7e646089..f262548363ba06 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2186,18 +2186,131 @@ static void ensure_socket_initialization(void)
 	initialized = 1;
 }
 
+static int winsock_error_to_errno(DWORD err)
+{
+	switch (err) {
+	case WSAEINTR: return EINTR;
+	case WSAEBADF: return EBADF;
+	case WSAEACCES: return EACCES;
+	case WSAEFAULT: return EFAULT;
+	case WSAEINVAL: return EINVAL;
+	case WSAEMFILE: return EMFILE;
+	case WSAEWOULDBLOCK: return EWOULDBLOCK;
+	case WSAEINPROGRESS: return EINPROGRESS;
+	case WSAEALREADY: return EALREADY;
+	case WSAENOTSOCK: return ENOTSOCK;
+	case WSAEDESTADDRREQ: return EDESTADDRREQ;
+	case WSAEMSGSIZE: return EMSGSIZE;
+	case WSAEPROTOTYPE: return EPROTOTYPE;
+	case WSAENOPROTOOPT: return ENOPROTOOPT;
+	case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
+	case WSAEOPNOTSUPP: return EOPNOTSUPP;
+	case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
+	case WSAEADDRINUSE: return EADDRINUSE;
+	case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
+	case WSAENETDOWN: return ENETDOWN;
+	case WSAENETUNREACH: return ENETUNREACH;
+	case WSAENETRESET: return ENETRESET;
+	case WSAECONNABORTED: return ECONNABORTED;
+	case WSAECONNRESET: return ECONNRESET;
+	case WSAENOBUFS: return ENOBUFS;
+	case WSAEISCONN: return EISCONN;
+	case WSAENOTCONN: return ENOTCONN;
+	case WSAETIMEDOUT: return ETIMEDOUT;
+	case WSAECONNREFUSED: return ECONNREFUSED;
+	case WSAELOOP: return ELOOP;
+	case WSAENAMETOOLONG: return ENAMETOOLONG;
+	case WSAEHOSTUNREACH: return EHOSTUNREACH;
+	case WSAENOTEMPTY: return ENOTEMPTY;
+	/* No errno equivalent; default to EIO */
+	case WSAESOCKTNOSUPPORT:
+	case WSAEPFNOSUPPORT:
+	case WSAESHUTDOWN:
+	case WSAETOOMANYREFS:
+	case WSAEHOSTDOWN:
+	case WSAEPROCLIM:
+	case WSAEUSERS:
+	case WSAEDQUOT:
+	case WSAESTALE:
+	case WSAEREMOTE:
+	case WSASYSNOTREADY:
+	case WSAVERNOTSUPPORTED:
+	case WSANOTINITIALISED:
+	case WSAEDISCON:
+	case WSAENOMORE:
+	case WSAECANCELLED:
+	case WSAEINVALIDPROCTABLE:
+	case WSAEINVALIDPROVIDER:
+	case WSAEPROVIDERFAILEDINIT:
+	case WSASYSCALLFAILURE:
+	case WSASERVICE_NOT_FOUND:
+	case WSATYPE_NOT_FOUND:
+	case WSA_E_NO_MORE:
+	case WSA_E_CANCELLED:
+	case WSAEREFUSED:
+	case WSAHOST_NOT_FOUND:
+	case WSATRY_AGAIN:
+	case WSANO_RECOVERY:
+	case WSANO_DATA:
+	case WSA_QOS_RECEIVERS:
+	case WSA_QOS_SENDERS:
+	case WSA_QOS_NO_SENDERS:
+	case WSA_QOS_NO_RECEIVERS:
+	case WSA_QOS_REQUEST_CONFIRMED:
+	case WSA_QOS_ADMISSION_FAILURE:
+	case WSA_QOS_POLICY_FAILURE:
+	case WSA_QOS_BAD_STYLE:
+	case WSA_QOS_BAD_OBJECT:
+	case WSA_QOS_TRAFFIC_CTRL_ERROR:
+	case WSA_QOS_GENERIC_ERROR:
+	case WSA_QOS_ESERVICETYPE:
+	case WSA_QOS_EFLOWSPEC:
+	case WSA_QOS_EPROVSPECBUF:
+	case WSA_QOS_EFILTERSTYLE:
+	case WSA_QOS_EFILTERTYPE:
+	case WSA_QOS_EFILTERCOUNT:
+	case WSA_QOS_EOBJLENGTH:
+	case WSA_QOS_EFLOWCOUNT:
+#ifndef _MSC_VER
+	case WSA_QOS_EUNKNOWNPSOBJ:
+#endif
+	case WSA_QOS_EPOLICYOBJ:
+	case WSA_QOS_EFLOWDESC:
+	case WSA_QOS_EPSFLOWSPEC:
+	case WSA_QOS_EPSFILTERSPEC:
+	case WSA_QOS_ESDMODEOBJ:
+	case WSA_QOS_ESHAPERATEOBJ:
+	case WSA_QOS_RESERVED_PETYPE:
+	default: return EIO;
+	}
+}
+
+#define WINSOCK_RETURN(x) { \
+	int ret = (x); \
+	if (ret < 0) \
+		errno = winsock_error_to_errno(WSAGetLastError()); \
+	return ret; \
+}
+
 #undef gethostname
 int mingw_gethostname(char *name, int namelen)
 {
-    ensure_socket_initialization();
-    return gethostname(name, namelen);
+	ensure_socket_initialization();
+	WINSOCK_RETURN(gethostname(name, namelen));
 }
 
 #undef gethostbyname
 struct hostent *mingw_gethostbyname(const char *host)
 {
+	struct hostent *ret;
+
 	ensure_socket_initialization();
-	return gethostbyname(host);
+
+	ret = gethostbyname(host);
+	if (!ret)
+		errno = winsock_error_to_errno(WSAGetLastError());
+
+	return ret;
 }
 
 #undef getaddrinfo
@@ -2205,7 +2318,7 @@ int mingw_getaddrinfo(const char *node, const char *service,
 		      const struct addrinfo *hints, struct addrinfo **res)
 {
 	ensure_socket_initialization();
-	return getaddrinfo(node, service, hints, res);
+	WINSOCK_RETURN(getaddrinfo(node, service, hints, res));
 }
 
 int mingw_socket(int domain, int type, int protocol)
@@ -2225,7 +2338,7 @@ int mingw_socket(int domain, int type, int protocol)
 		 * in errno so that _if_ someone looks up the code somewhere,
 		 * then it is at least the number that are usually listed.
 		 */
-		errno = WSAGetLastError();
+		errno = winsock_error_to_errno(WSAGetLastError());
 		return -1;
 	}
 	/* convert into a file descriptor */
@@ -2255,7 +2368,7 @@ int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
 int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
 {
 	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
-	return setsockopt(s, lvl, optname, (const char*)optval, optlen);
+	WINSOCK_RETURN(setsockopt(s, lvl, optname, (const char*)optval, optlen));
 }
 
 #undef shutdown

From 30598a39880b21a63c21b10e761ef3a7e71827e7 Mon Sep 17 00:00:00 2001
From: Jeff Hostetler <jeffhost@microsoft.com>
Date: Wed, 20 Nov 2019 14:11:18 -0500
Subject: [PATCH 2/4] fixup: make WINSOCK_RETURN easier to debug in gdb

TODO squash this and omit the fprintfs

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 compat/mingw.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index f262548363ba06..623ea844290390 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2285,13 +2285,20 @@ static int winsock_error_to_errno(DWORD err)
 	}
 }
 
-#define WINSOCK_RETURN(x) { \
-	int ret = (x); \
-	if (ret < 0) \
-		errno = winsock_error_to_errno(WSAGetLastError()); \
-	return ret; \
+static int winsock_return(int ret)
+{
+	if (ret < 0) {
+		DWORD wsa = WSAGetLastError();
+		int e = winsock_error_to_errno(wsa);
+		fprintf(stderr, "winsock error: %d -> %d\n", wsa, e);
+		fflush(stderr);
+		errno = e;
+	}
+	return ret;
 }
 
+#define WINSOCK_RETURN(x) do { return winsock_return(x); } while (0)
+
 #undef gethostname
 int mingw_gethostname(char *name, int namelen)
 {

From 4ddc9fc778bb87c92139a0563e4e6b2885c0db47 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Thu, 14 Nov 2019 20:09:23 +0100
Subject: [PATCH 3/4] mingw: make sure `errno` is set correctly when socket
 operations fail

The winsock2 library provides functions that work on different data
types than file descriptors, therefore we wrap them.

But that is not the only difference: they also do not set `errno` but
expect the callers to enquire about errors via `WSAGetLastError()`.

Let's translate that into appropriate `errno` values whenever the socket
operations fail so that Git's code base does not have to change its
expectations.

This closes https://github.com/git-for-windows/git/issues/2404

Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 compat/mingw.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index 623ea844290390..e3048e95f49a0d 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2285,7 +2285,7 @@ static int winsock_error_to_errno(DWORD err)
 	}
 }
 
-static int winsock_return(int ret)
+static inline int winsock_return(int ret)
 {
 	if (ret < 0) {
 		DWORD wsa = WSAGetLastError();
@@ -2361,14 +2361,14 @@ int mingw_socket(int domain, int type, int protocol)
 int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
 {
 	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
-	return connect(s, sa, sz);
+	WINSOCK_RETURN(connect(s, sa, sz));
 }
 
 #undef bind
 int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
 {
 	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
-	return bind(s, sa, sz);
+	WINSOCK_RETURN(bind(s, sa, sz));
 }
 
 #undef setsockopt
@@ -2382,14 +2382,14 @@ int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
 int mingw_shutdown(int sockfd, int how)
 {
 	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
-	return shutdown(s, how);
+	WINSOCK_RETURN(shutdown(s, how));
 }
 
 #undef listen
 int mingw_listen(int sockfd, int backlog)
 {
 	SOCKET s = (SOCKET)_get_osfhandle(sockfd);
-	return listen(s, backlog);
+	WINSOCK_RETURN(listen(s, backlog));
 }
 
 #undef accept
@@ -2400,6 +2400,11 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
 	SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
 	SOCKET s2 = accept(s1, sa, sz);
 
+	if (s2 == INVALID_SOCKET) {
+		errno = winsock_error_to_errno(WSAGetLastError());
+		return -1;
+	}
+
 	/* convert into a file descriptor */
 	if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
 		int err = errno;

From bf4aa26453b8f75f305791f9fcc99a2f15b9dda0 Mon Sep 17 00:00:00 2001
From: Jeff Hostetler <jeffhost@microsoft.com>
Date: Mon, 25 Nov 2019 10:16:37 -0500
Subject: [PATCH 4/4] fixup: simplify calls to WSAGetLastError

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
 compat/mingw.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index e3048e95f49a0d..b6e822bbf5d18d 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2285,15 +2285,25 @@ static int winsock_error_to_errno(DWORD err)
 	}
 }
 
+/*
+ * On Windows, `errno` is a global macro to a function call.
+ * This makes it difficult to debug and single-step our mappings.
+ */
+static inline void set_wsa_errno(void)
+{
+	DWORD wsa = WSAGetLastError();
+	int e = winsock_error_to_errno(wsa);
+	errno = e;
+
+	fprintf(stderr, "winsock error: %d -> %d\n", wsa, e);
+	fflush(stderr);
+}
+
 static inline int winsock_return(int ret)
 {
-	if (ret < 0) {
-		DWORD wsa = WSAGetLastError();
-		int e = winsock_error_to_errno(wsa);
-		fprintf(stderr, "winsock error: %d -> %d\n", wsa, e);
-		fflush(stderr);
-		errno = e;
-	}
+	if (ret < 0)
+		set_wsa_errno();
+
 	return ret;
 }
 
@@ -2315,7 +2325,7 @@ struct hostent *mingw_gethostbyname(const char *host)
 
 	ret = gethostbyname(host);
 	if (!ret)
-		errno = winsock_error_to_errno(WSAGetLastError());
+		set_wsa_errno();
 
 	return ret;
 }
@@ -2345,7 +2355,7 @@ int mingw_socket(int domain, int type, int protocol)
 		 * in errno so that _if_ someone looks up the code somewhere,
 		 * then it is at least the number that are usually listed.
 		 */
-		errno = winsock_error_to_errno(WSAGetLastError());
+		set_wsa_errno();
 		return -1;
 	}
 	/* convert into a file descriptor */
@@ -2401,7 +2411,7 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
 	SOCKET s2 = accept(s1, sa, sz);
 
 	if (s2 == INVALID_SOCKET) {
-		errno = winsock_error_to_errno(WSAGetLastError());
+		set_wsa_errno();
 		return -1;
 	}
 
