mirror of
https://github.com/git-for-windows/git.git
synced 2026-02-04 03:33:01 -06:00
credential-cache: handle ECONNREFUSED gracefully (#5329)
I should probably add some tests for this.
This commit is contained in:
commit
ed886133e6
1
Makefile
1
Makefile
@ -1367,6 +1367,7 @@ CLAR_TEST_SUITES += u-example-decorate
|
||||
CLAR_TEST_SUITES += u-hash
|
||||
CLAR_TEST_SUITES += u-hashmap
|
||||
CLAR_TEST_SUITES += u-mem-pool
|
||||
CLAR_TEST_SUITES += u-mingw
|
||||
CLAR_TEST_SUITES += u-oid-array
|
||||
CLAR_TEST_SUITES += u-oidmap
|
||||
CLAR_TEST_SUITES += u-oidtree
|
||||
|
||||
@ -23,7 +23,7 @@ static int connection_closed(int error)
|
||||
|
||||
static int connection_fatally_broken(int error)
|
||||
{
|
||||
return (error != ENOENT) && (error != ENETDOWN);
|
||||
return (error != ENOENT) && (error != ENETDOWN) && (error != ECONNREFUSED);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@ -292,6 +292,11 @@ int mingw_socket(int domain, int type, int protocol);
|
||||
int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
|
||||
#define connect mingw_connect
|
||||
|
||||
char *mingw_strerror(int errnum);
|
||||
#ifndef _UCRT
|
||||
#define strerror mingw_strerror
|
||||
#endif
|
||||
|
||||
int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
|
||||
#define bind mingw_bind
|
||||
|
||||
|
||||
251
compat/mingw.c
251
compat/mingw.c
@ -2254,18 +2254,235 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
#ifdef DEBUG_WSA_ERRNO
|
||||
fprintf(stderr, "winsock error: %d -> %d\n", wsa, e);
|
||||
fflush(stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int winsock_return(int ret)
|
||||
{
|
||||
if (ret < 0)
|
||||
set_wsa_errno();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WINSOCK_RETURN(x) do { return winsock_return(x); } while (0)
|
||||
|
||||
#undef strerror
|
||||
char *mingw_strerror(int errnum)
|
||||
{
|
||||
static char buf[41] ="";
|
||||
switch (errnum) {
|
||||
case EWOULDBLOCK:
|
||||
xsnprintf(buf, 41, "%s", "Operation would block");
|
||||
break;
|
||||
case EINPROGRESS:
|
||||
xsnprintf(buf, 41, "%s", "Operation now in progress");
|
||||
break;
|
||||
case EALREADY:
|
||||
xsnprintf(buf, 41, "%s", "Operation already in progress");
|
||||
break;
|
||||
case ENOTSOCK:
|
||||
xsnprintf(buf, 41, "%s", "Socket operation on non-socket");
|
||||
break;
|
||||
case EDESTADDRREQ:
|
||||
xsnprintf(buf, 41, "%s", "Destination address required");
|
||||
break;
|
||||
case EMSGSIZE:
|
||||
xsnprintf(buf, 41, "%s", "Message too long");
|
||||
break;
|
||||
case EPROTOTYPE:
|
||||
xsnprintf(buf, 41, "%s", "Protocol wrong type for socket");
|
||||
break;
|
||||
case ENOPROTOOPT:
|
||||
xsnprintf(buf, 41, "%s", "Protocol not available");
|
||||
break;
|
||||
case EPROTONOSUPPORT:
|
||||
xsnprintf(buf, 41, "%s", "Protocol not supported");
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
xsnprintf(buf, 41, "%s", "Operation not supported");
|
||||
break;
|
||||
case EAFNOSUPPORT:
|
||||
xsnprintf(buf, 41, "%s", "Address family not supported by protocol");
|
||||
break;
|
||||
case EADDRINUSE:
|
||||
xsnprintf(buf, 41, "%s", "Address already in use");
|
||||
break;
|
||||
case EADDRNOTAVAIL:
|
||||
xsnprintf(buf, 41, "%s", "Cannot assign requested address");
|
||||
break;
|
||||
case ENETDOWN:
|
||||
xsnprintf(buf, 41, "%s", "Network is down");
|
||||
break;
|
||||
case ENETUNREACH:
|
||||
xsnprintf(buf, 41, "%s", "Network is unreachable");
|
||||
break;
|
||||
case ENETRESET:
|
||||
xsnprintf(buf, 41, "%s", "Network dropped connection on reset");
|
||||
break;
|
||||
case ECONNABORTED:
|
||||
xsnprintf(buf, 41, "%s", "Software caused connection abort");
|
||||
break;
|
||||
case ECONNRESET:
|
||||
xsnprintf(buf, 41, "%s", "Connection reset by peer");
|
||||
break;
|
||||
case ENOBUFS:
|
||||
xsnprintf(buf, 41, "%s", "No buffer space available");
|
||||
break;
|
||||
case EISCONN:
|
||||
xsnprintf(buf, 41, "%s", "Transport endpoint is already connected");
|
||||
break;
|
||||
case ENOTCONN:
|
||||
xsnprintf(buf, 41, "%s", "Transport endpoint is not connected");
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
xsnprintf(buf, 41, "%s", "Connection timed out");
|
||||
break;
|
||||
case ECONNREFUSED:
|
||||
xsnprintf(buf, 41, "%s", "Connection refused");
|
||||
break;
|
||||
case ELOOP:
|
||||
xsnprintf(buf, 41, "%s", "Too many levels of symbolic links");
|
||||
break;
|
||||
case EHOSTUNREACH:
|
||||
xsnprintf(buf, 41, "%s", "No route to host");
|
||||
break;
|
||||
default: return strerror(errnum);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#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)
|
||||
set_wsa_errno();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef getaddrinfo
|
||||
@ -2273,7 +2490,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)
|
||||
@ -2284,16 +2501,7 @@ int mingw_socket(int domain, int type, int protocol)
|
||||
ensure_socket_initialization();
|
||||
s = WSASocket(domain, type, protocol, NULL, 0, 0);
|
||||
if (s == INVALID_SOCKET) {
|
||||
/*
|
||||
* WSAGetLastError() values are regular BSD error codes
|
||||
* biased by WSABASEERR.
|
||||
* However, strerror() does not know about networking
|
||||
* specific errors, which are values beginning at 38 or so.
|
||||
* Therefore, we choose to leave the biased error code
|
||||
* in errno so that _if_ someone looks up the code somewhere,
|
||||
* then it is at least the number that are usually listed.
|
||||
*/
|
||||
errno = WSAGetLastError();
|
||||
set_wsa_errno();
|
||||
return -1;
|
||||
}
|
||||
/* convert into a file descriptor */
|
||||
@ -2309,35 +2517,35 @@ 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
|
||||
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
|
||||
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
|
||||
@ -2348,6 +2556,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) {
|
||||
set_wsa_errno();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* convert into a file descriptor */
|
||||
if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
|
||||
int err = errno;
|
||||
|
||||
@ -4,6 +4,7 @@ clar_test_suites = [
|
||||
'unit-tests/u-hash.c',
|
||||
'unit-tests/u-hashmap.c',
|
||||
'unit-tests/u-mem-pool.c',
|
||||
'unit-tests/u-mingw.c',
|
||||
'unit-tests/u-oid-array.c',
|
||||
'unit-tests/u-oidmap.c',
|
||||
'unit-tests/u-oidtree.c',
|
||||
|
||||
@ -12,7 +12,7 @@ test -z "$NO_UNIX_SOCKETS" || {
|
||||
if test_have_prereq MINGW
|
||||
then
|
||||
service_running=$(sc query afunix | grep "4 RUNNING")
|
||||
test -z "$service_running" || {
|
||||
test -n "$service_running" || {
|
||||
skip_all='skipping credential-cache tests, unix sockets not available'
|
||||
test_done
|
||||
}
|
||||
|
||||
72
t/unit-tests/u-mingw.c
Normal file
72
t/unit-tests/u-mingw.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include "unit-test.h"
|
||||
|
||||
#if defined(GIT_WINDOWS_NATIVE) && !defined(_UCRT)
|
||||
#undef strerror
|
||||
int errnos_contains(int);
|
||||
static int errnos [53]={
|
||||
/* errnos in err_win_to_posix */
|
||||
EACCES, EBUSY, EEXIST, ERANGE, EIO, ENODEV, ENXIO, ENOEXEC, EINVAL, ENOENT,
|
||||
EPIPE, ENAMETOOLONG, ENOSYS, ENOTEMPTY, ENOSPC, EFAULT, EBADF, EPERM, EINTR,
|
||||
E2BIG, ESPIPE, ENOMEM, EXDEV, EAGAIN, ENFILE, EMFILE, ECHILD, EROFS,
|
||||
/* errnos only in winsock_error_to_errno */
|
||||
EWOULDBLOCK, EINPROGRESS, EALREADY, ENOTSOCK, EDESTADDRREQ, EMSGSIZE,
|
||||
EPROTOTYPE, ENOPROTOOPT, EPROTONOSUPPORT, EOPNOTSUPP, EAFNOSUPPORT,
|
||||
EADDRINUSE, EADDRNOTAVAIL, ENETDOWN, ENETUNREACH, ENETRESET, ECONNABORTED,
|
||||
ECONNRESET, ENOBUFS, EISCONN, ENOTCONN, ETIMEDOUT, ECONNREFUSED, ELOOP,
|
||||
EHOSTUNREACH
|
||||
};
|
||||
|
||||
int errnos_contains(int errnum)
|
||||
{
|
||||
for(int i=0;i<53;i++)
|
||||
if(errnos[i]==errnum)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_mingw__no_strerror_shim_on_ucrt(void)
|
||||
{
|
||||
#if defined(GIT_WINDOWS_NATIVE) && defined(_UCRT)
|
||||
cl_assert_(strerror != mingw_strerror,
|
||||
"mingw_strerror is unnescessary when building against UCRT");
|
||||
#else
|
||||
cl_skip();
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_mingw__strerror(void)
|
||||
{
|
||||
#if defined(GIT_WINDOWS_NATIVE) && !defined(_UCRT)
|
||||
for(int i=0;i<53;i++)
|
||||
{
|
||||
char *crt;
|
||||
char *mingw;
|
||||
mingw = mingw_strerror(errnos[i]);
|
||||
crt = strerror(errnos[i]);
|
||||
cl_assert_(!strcasestr(mingw, "unknown error"),
|
||||
"mingw_strerror should know all errno values we care about");
|
||||
if(!strcasestr(crt, "unknown error"))
|
||||
cl_assert_equal_s(crt,mingw);
|
||||
}
|
||||
#else
|
||||
cl_skip();
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_mingw__errno_translation(void)
|
||||
{
|
||||
#if defined(GIT_WINDOWS_NATIVE) && !defined(_UCRT)
|
||||
/* GetLastError() return values are currently defined from 0 to 15841,
|
||||
testing up to 20000 covers some room for future expansion */
|
||||
for (int i=0;i<20000;i++)
|
||||
{
|
||||
if(i!=ERROR_SUCCESS)
|
||||
cl_assert_(errnos_contains(err_win_to_posix(i)),
|
||||
"all err_win_to_posix return values should be tested against mingw_strerror");
|
||||
/* ideally we'd test the same for winsock_error_to_errno, but it's static */
|
||||
}
|
||||
#else
|
||||
cl_skip();
|
||||
#endif
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user