Merge pull request #41 from ppelleti/winsock-errs

FormatMessage for winsock errors
This commit is contained in:
Nick Mathewson 2013-02-26 15:22:03 -08:00
commit 87c56727b6
5 changed files with 261 additions and 68 deletions

View File

@ -3468,6 +3468,8 @@ event_global_setup_locks_(const int enable_locks)
#endif #endif
if (evsig_global_setup_locks_(enable_locks) < 0) if (evsig_global_setup_locks_(enable_locks) < 0)
return -1; return -1;
if (evutil_global_setup_locks_(enable_locks) < 0)
return -1;
if (evutil_secure_rng_global_setup_locks_(enable_locks) < 0) if (evutil_secure_rng_global_setup_locks_(enable_locks) < 0)
return -1; return -1;
return 0; return 0;

View File

@ -373,6 +373,7 @@ void *evthread_setup_global_lock_(void *lock_, unsigned locktype,
int event_global_setup_locks_(const int enable_locks); int event_global_setup_locks_(const int enable_locks);
int evsig_global_setup_locks_(const int enable_locks); int evsig_global_setup_locks_(const int enable_locks);
int evutil_global_setup_locks_(const int enable_locks);
int evutil_secure_rng_global_setup_locks_(const int enable_locks); int evutil_secure_rng_global_setup_locks_(const int enable_locks);
#endif #endif

231
evutil.c
View File

@ -81,11 +81,14 @@
#include "util-internal.h" #include "util-internal.h"
#include "log-internal.h" #include "log-internal.h"
#include "mm-internal.h" #include "mm-internal.h"
#include "evthread-internal.h"
#include "strlcpy-internal.h" #include "strlcpy-internal.h"
#include "ipv6-internal.h" #include "ipv6-internal.h"
#ifdef _WIN32 #ifdef _WIN32
#define HT_NO_CACHE_HASH_VALUES
#include "ht-internal.h"
#define open _open #define open _open
#define read _read #define read _read
#define close _close #define close _close
@ -1597,80 +1600,177 @@ evutil_gai_strerror(int err)
} }
#ifdef _WIN32 #ifdef _WIN32
#define E(code, s) { code, (s " [" #code " ]") } /* destructively remove a trailing line terminator from s */
static struct { int code; const char *msg; } windows_socket_errors[] = { static void
E(WSAEINTR, "Interrupted function call"), chomp (char *s)
E(WSAEACCES, "Permission denied"), {
E(WSAEFAULT, "Bad address"), size_t len;
E(WSAEINVAL, "Invalid argument"), if (s && (len = strlen (s)) > 0 && s[len - 1] == '\n') {
E(WSAEMFILE, "Too many open files"), s[--len] = 0;
E(WSAEWOULDBLOCK, "Resource temporarily unavailable"), if (len > 0 && s[len - 1] == '\r')
E(WSAEINPROGRESS, "Operation now in progress"), s[--len] = 0;
E(WSAEALREADY, "Operation already in progress"), }
E(WSAENOTSOCK, "Socket operation on nonsocket"), }
E(WSAEDESTADDRREQ, "Destination address required"),
E(WSAEMSGSIZE, "Message too long"),
E(WSAEPROTOTYPE, "Protocol wrong for socket"),
E(WSAENOPROTOOPT, "Bad protocol option"),
E(WSAEPROTONOSUPPORT, "Protocol not supported"),
E(WSAESOCKTNOSUPPORT, "Socket type not supported"),
/* What's the difference between NOTSUPP and NOSUPPORT? :) */
E(WSAEOPNOTSUPP, "Operation not supported"),
E(WSAEPFNOSUPPORT, "Protocol family not supported"),
E(WSAEAFNOSUPPORT, "Address family not supported by protocol family"),
E(WSAEADDRINUSE, "Address already in use"),
E(WSAEADDRNOTAVAIL, "Cannot assign requested address"),
E(WSAENETDOWN, "Network is down"),
E(WSAENETUNREACH, "Network is unreachable"),
E(WSAENETRESET, "Network dropped connection on reset"),
E(WSAECONNABORTED, "Software caused connection abort"),
E(WSAECONNRESET, "Connection reset by peer"),
E(WSAENOBUFS, "No buffer space available"),
E(WSAEISCONN, "Socket is already connected"),
E(WSAENOTCONN, "Socket is not connected"),
E(WSAESHUTDOWN, "Cannot send after socket shutdown"),
E(WSAETIMEDOUT, "Connection timed out"),
E(WSAECONNREFUSED, "Connection refused"),
E(WSAEHOSTDOWN, "Host is down"),
E(WSAEHOSTUNREACH, "No route to host"),
E(WSAEPROCLIM, "Too many processes"),
/* Yes, some of these start with WSA, not WSAE. No, I don't know why. */ /* FormatMessage returns allocated strings, but evutil_socket_error_to_string
E(WSASYSNOTREADY, "Network subsystem is unavailable"), * is supposed to return a string which is good indefinitely without having
E(WSAVERNOTSUPPORTED, "Winsock.dll out of range"), * to be freed. To make this work without leaking memory, we cache the
E(WSANOTINITIALISED, "Successful WSAStartup not yet performed"), * string the first time FormatMessage is called on a particular error
E(WSAEDISCON, "Graceful shutdown now in progress"), * code, and then return the cached string on subsequent calls with the
#ifdef WSATYPE_NOT_FOUND * same code. The strings aren't freed until libevent_global_shutdown
E(WSATYPE_NOT_FOUND, "Class type not found"), * (or never). We use a linked list to cache the errors, because we
#endif * only expect there to be a few dozen, and that should be fast enough.
E(WSAHOST_NOT_FOUND, "Host not found"),
E(WSATRY_AGAIN, "Nonauthoritative host not found"),
E(WSANO_RECOVERY, "This is a nonrecoverable error"),
E(WSANO_DATA, "Valid name, no data record of requested type)"),
/* There are some more error codes whose numeric values are marked
* <b>OS dependent</b>. They start with WSA_, apparently for the same
* reason that practitioners of some craft traditions deliberately
* introduce imperfections into their baskets and rugs "to allow the
* evil spirits to escape." If we catch them, then our binaries
* might not report consistent results across versions of Windows.
* Thus, I'm going to let them all fall through.
*/ */
{ -1, NULL },
struct cached_sock_errs_entry {
HT_ENTRY(cached_sock_errs_entry) node;
DWORD code;
char *msg; /* allocated with LocalAlloc; free with LocalFree */
}; };
#undef E
static inline unsigned
hash_cached_sock_errs(const struct cached_sock_errs_entry *e)
{
/* Use Murmur3's 32-bit finalizer as an integer hash function */
DWORD h = e->code;
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
static inline int
eq_cached_sock_errs(const struct cached_sock_errs_entry *a,
const struct cached_sock_errs_entry *b)
{
return a->code == b->code;
}
#ifndef EVENT__DISABLE_THREAD_SUPPORT
static void *windows_socket_errors_lock_ = NULL;
#endif
static HT_HEAD(cached_sock_errs_map, cached_sock_errs_entry)
windows_socket_errors = HT_INITIALIZER();
HT_PROTOTYPE(cached_sock_errs_map,
cached_sock_errs_entry,
node,
hash_cached_sock_errs,
eq_cached_sock_errs);
HT_GENERATE(cached_sock_errs_map,
cached_sock_errs_entry,
node,
hash_cached_sock_errs,
eq_cached_sock_errs,
0.5,
mm_malloc,
mm_realloc,
mm_free);
/** Equivalent to strerror, but for windows socket errors. */ /** Equivalent to strerror, but for windows socket errors. */
const char * const char *
evutil_socket_error_to_string(int errcode) evutil_socket_error_to_string(int errcode)
{ {
/* XXXX Is there really no built-in function to do this? */ struct cached_sock_errs_entry *errs, *newerr, find;
int i; char *msg = NULL;
for (i=0; windows_socket_errors[i].code >= 0; ++i) {
if (errcode == windows_socket_errors[i].code) EVLOCK_LOCK(windows_socket_errors_lock_, 0);
return windows_socket_errors[i].msg;
find.code = errcode;
errs = HT_FIND(cached_sock_errs_map, &windows_socket_errors, &find);
if (errs) {
msg = errs->msg;
goto done;
} }
return strerror(errcode);
if (0 != FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, errcode, 0, (LPTSTR)&msg, 0, NULL))
chomp (msg); /* because message has trailing newline */
else {
size_t len = 50;
/* use LocalAlloc because FormatMessage does */
msg = LocalAlloc(LMEM_FIXED, len);
if (!msg) {
msg = "winsock error";
goto done;
} }
evutil_snprintf(msg, len, "winsock error 0x%08x", errcode);
}
newerr = (struct cached_sock_errs_entry *)
mm_malloc(sizeof (struct cached_sock_errs_entry));
if (!newerr) {
LocalFree(msg);
msg = "winsock error";
goto done;
}
newerr->code = errcode;
newerr->msg = msg;
HT_INSERT(cached_sock_errs_map, &windows_socket_errors, newerr);
done:
EVLOCK_UNLOCK(windows_socket_errors_lock_, 0);
return msg;
}
#ifndef EVENT__DISABLE_THREAD_SUPPORT
int
evutil_global_setup_locks_(const int enable_locks)
{
EVTHREAD_SETUP_GLOBAL_LOCK(windows_socket_errors_lock_, 0);
return 0;
}
#endif
static void
evutil_free_sock_err_globals(void)
{
struct cached_sock_errs_entry **errs, *tofree;
for (errs = HT_START(cached_sock_errs_map, &windows_socket_errors)
; errs; ) {
tofree = *errs;
errs = HT_NEXT_RMV(cached_sock_errs_map,
&windows_socket_errors,
errs);
LocalFree(tofree->msg);
mm_free(tofree);
}
HT_CLEAR(cached_sock_errs_map, &windows_socket_errors);
#ifndef EVENT__DISABLE_THREAD_SUPPORT
if (windows_socket_errors_lock_ != NULL) {
EVTHREAD_FREE_LOCK(windows_socket_errors_lock_, 0);
windows_socket_errors_lock_ = NULL;
}
#endif
}
#else
#ifndef EVENT__DISABLE_THREAD_SUPPORT
int
evutil_global_setup_locks_(const int enable_locks)
{
return 0;
}
#endif
static void
evutil_free_sock_err_globals(void)
{
}
#endif #endif
int int
@ -2529,4 +2629,5 @@ void
evutil_free_globals_(void) evutil_free_globals_(void)
{ {
evutil_free_secure_rng_globals_(); evutil_free_secure_rng_globals_();
evutil_free_sock_err_globals();
} }

View File

@ -24,11 +24,13 @@ REGRESS_OBJS=regress.obj regress_buffer.obj regress_http.obj regress_dns.obj \
OTHER_OBJS=test-init.obj test-eof.obj test-weof.obj test-time.obj \ OTHER_OBJS=test-init.obj test-eof.obj test-weof.obj test-time.obj \
bench.obj bench_cascade.obj bench_http.obj bench_httpclient.obj \ bench.obj bench_cascade.obj bench_http.obj bench_httpclient.obj \
test-changelist.obj test-changelist.obj \
print-winsock-errors.obj
PROGRAMS=regress.exe \ PROGRAMS=regress.exe \
test-init.exe test-eof.exe test-weof.exe test-time.exe \ test-init.exe test-eof.exe test-weof.exe test-time.exe \
test-changelist.exe test-changelist.exe \
print-winsock-errors.exe
# Disabled for now: # Disabled for now:
# bench.exe bench_cascade.exe bench_http.exe bench_httpclient.exe # bench.exe bench_cascade.exe bench_http.exe bench_httpclient.exe
@ -52,6 +54,9 @@ test-weof.exe: test-weof.obj
test-time.exe: test-time.obj test-time.exe: test-time.obj
$(CC) $(CFLAGS) $(LIBS) test-time.obj $(CC) $(CFLAGS) $(LIBS) test-time.obj
print-winsock-errors.exe: print-winsock-errors.obj
$(CC) $(CFLAGS) $(LIBS) print-winsock-errors.obj
bench.exe: bench.obj bench.exe: bench.obj
$(CC) $(CFLAGS) $(LIBS) bench.obj $(CC) $(CFLAGS) $(LIBS) bench.obj
bench_cascade.exe: bench_cascade.obj bench_cascade.exe: bench_cascade.obj

View File

@ -0,0 +1,84 @@
#include <winsock2.h>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "event2/event.h"
#include "event2/util.h"
#include "event2/thread.h"
#define E(x) printf (#x " -> \"%s\"\n", evutil_socket_error_to_string (x));
int main (int argc, char **argv)
{
int i, j;
const char *s1, *s2;
evthread_use_windows_threads ();
s1 = evutil_socket_error_to_string (WSAEINTR);
for (i = 0; i < 3; i++) {
printf ("\niteration %d:\n\n", i);
E(WSAEINTR);
E(WSAEACCES);
E(WSAEFAULT);
E(WSAEINVAL);
E(WSAEMFILE);
E(WSAEWOULDBLOCK);
E(WSAEINPROGRESS);
E(WSAEALREADY);
E(WSAENOTSOCK);
E(WSAEDESTADDRREQ);
E(WSAEMSGSIZE);
E(WSAEPROTOTYPE);
E(WSAENOPROTOOPT);
E(WSAEPROTONOSUPPORT);
E(WSAESOCKTNOSUPPORT);
E(WSAEOPNOTSUPP);
E(WSAEPFNOSUPPORT);
E(WSAEAFNOSUPPORT);
E(WSAEADDRINUSE);
E(WSAEADDRNOTAVAIL);
E(WSAENETDOWN);
E(WSAENETUNREACH);
E(WSAENETRESET);
E(WSAECONNABORTED);
E(WSAECONNRESET);
E(WSAENOBUFS);
E(WSAEISCONN);
E(WSAENOTCONN);
E(WSAESHUTDOWN);
E(WSAETIMEDOUT);
E(WSAECONNREFUSED);
E(WSAEHOSTDOWN);
E(WSAEHOSTUNREACH);
E(WSAEPROCLIM);
E(WSASYSNOTREADY);
E(WSAVERNOTSUPPORTED);
E(WSANOTINITIALISED);
E(WSAEDISCON);
E(WSATYPE_NOT_FOUND);
E(WSAHOST_NOT_FOUND);
E(WSATRY_AGAIN);
E(WSANO_RECOVERY);
E(WSANO_DATA);
E(0xdeadbeef); /* test the case where no message is available */
/* fill up the hash table a bit to make sure it grows properly */
for (j = 0; j < 50; j++) {
int err;
evutil_secure_rng_get_bytes(&err, sizeof(err));
evutil_socket_error_to_string(err);
}
}
s2 = evutil_socket_error_to_string (WSAEINTR);
if (s1 != s2)
printf ("caching failed!\n");
libevent_global_shutdown ();
return EXIT_SUCCESS;
}