Skip to content

Commit 7a9458c

Browse files
authored
Don't try to set SO_REUSEADDR and SO_REUSEPORT on Unix sockets (libevent#1625)
1 parent 66ee086 commit 7a9458c

File tree

3 files changed

+158
-1
lines changed

3 files changed

+158
-1
lines changed

listener.c

+20
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,31 @@ evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
244244
}
245245

246246
if (flags & LEV_OPT_REUSEABLE) {
247+
if (family == AF_UNIX) {
248+
/* Despite the fact that SO_REUSEADDR can be set on a Unix domain socket
249+
* via setsockopt() without reporting an error, SO_REUSEADDR is actually
250+
* not supported for sockets of AF_UNIX.
251+
* Instead of confusing the callers by allowing this option to be set and
252+
* failing the subsequent bind() on the same socket, it's better to fail here.
253+
*/
254+
evutil_closesocket(fd);
255+
return NULL;
256+
}
247257
if (evutil_make_listen_socket_reuseable(fd) < 0)
248258
goto err;
249259
}
250260

251261
if (flags & LEV_OPT_REUSEABLE_PORT) {
262+
if (family == AF_UNIX) {
263+
/* Despite the fact that SO_REUSEPORT can be set on a Unix domain socket
264+
* via setsockopt() without reporting an error, SO_REUSEPORT is actually
265+
* not supported for sockets of AF_UNIX.
266+
* Instead of confusing the callers by allowing this option to be set and
267+
* failing the subsequent bind() on the same socket, it's better to fail here.
268+
*/
269+
evutil_closesocket(fd);
270+
return NULL;
271+
}
252272
if (evutil_make_listen_socket_reuseable_port(fd) < 0)
253273
goto err;
254274
}

test/regress_listener.c

+134-1
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@
2626
#include "util-internal.h"
2727

2828
#ifdef _WIN32
29+
#ifdef EVENT__HAVE_AFUNIX_H
30+
#include <afunix.h>
31+
#endif
32+
#include <io.h>
2933
#include <winsock2.h>
3034
#include <windows.h>
35+
#include <winerror.h>
3136
#endif
3237

3338
#include <sys/types.h>
@@ -38,13 +43,18 @@
3843
# ifdef _XOPEN_SOURCE_EXTENDED
3944
# include <arpa/inet.h>
4045
# endif
46+
#ifdef EVENT__HAVE_SYS_UN_H
47+
#include <sys/un.h>
48+
#endif
4149
#include <unistd.h>
4250
#endif
4351
#ifdef EVENT__HAVE_SYS_RESOURCE_H
4452
#include <sys/resource.h>
4553
#endif
4654

4755
#include <string.h>
56+
#include <stdio.h>
57+
#include <stdlib.h>
4858

4959
#include "event2/listener.h"
5060
#include "event2/event.h"
@@ -54,11 +64,16 @@
5464
#include "regress_thread.h"
5565
#endif
5666

57-
67+
#include "strlcpy-internal.h"
5868
#include "regress.h"
5969
#include "tinytest.h"
6070
#include "tinytest_macros.h"
6171

72+
#ifdef _WIN32
73+
#define unlink _unlink
74+
#define close _close
75+
#endif
76+
6277
static void
6378
acceptcb(struct evconnlistener *listener, evutil_socket_t fd,
6479
struct sockaddr *addr, int socklen, void *arg)
@@ -430,6 +445,118 @@ regress_listener_disable_in_thread_error(void *arg)
430445
}
431446
#endif
432447

448+
#ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
449+
450+
#ifdef _WIN32
451+
static int
452+
is_unicode_temp_path()
453+
{
454+
DWORD n;
455+
WCHAR short_temp_path[MAX_PATH];
456+
WCHAR long_temp_path[MAX_PATH];
457+
n = GetTempPathW(MAX_PATH, short_temp_path);
458+
if (n == 0 || n > MAX_PATH)
459+
return 1;
460+
n = GetLongPathNameW(short_temp_path, long_temp_path, MAX_PATH);
461+
if (n == 0 || n > MAX_PATH)
462+
return 1;
463+
464+
// If it contains valid wide characters, it is considered Unicode.
465+
for (const wchar_t* p = long_temp_path; *p != L'\0'; ++p) {
466+
if (*p > 127) { // Non-ASCII character
467+
return 1; // It's a Unicode path
468+
}
469+
}
470+
return 0; // No non-ASCII characters found, may not be Unicode
471+
}
472+
#endif
473+
474+
static void
475+
empty_listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
476+
struct sockaddr *sa, int socklen, void *user_data)
477+
{
478+
printf("Empty listener, do nothing about it!\n");
479+
}
480+
481+
static void
482+
regress_listener_reuseport_on_unix_socket(void *arg)
483+
{
484+
struct basic_test_data *data = arg;
485+
struct event_base *base = data->base;
486+
struct evconnlistener *listener = NULL;
487+
char *socket_path = NULL;
488+
int truncated_temp_file = 0;
489+
struct sockaddr_un addr;
490+
int tempfd;
491+
492+
tempfd = regress_make_tmpfile("", 0, &socket_path);
493+
/* On Windows, if TMP environment variable is corrupted, we may not be
494+
* able create temporary file, just skip it */
495+
if (tempfd < 0)
496+
tt_skip();
497+
TT_BLATHER(("Temporary socket path: %s, fd: %i", socket_path, tempfd));
498+
499+
#if defined(_WIN32) && defined(EVENT__HAVE_AFUNIX_H)
500+
if (evutil_check_working_afunix_() == 0)
501+
/* AF_UNIX is not available on the current Windows platform,
502+
* just skip this test. */
503+
tt_skip();
504+
#endif
505+
506+
/* close fd to avoid leaks, since we only need a temporary file path
507+
* for bind() to create a socket file. */
508+
close(tempfd);
509+
/* For security reason, we must delete any existing sockets in the filesystem.
510+
* Besides, we will get EADDRINUSE error if the socket file already exists. */
511+
unlink(socket_path);
512+
513+
memset(&addr, 0, sizeof(addr));
514+
addr.sun_family = AF_UNIX;
515+
if (strlcpy(addr.sun_path, socket_path, sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
516+
/* The temporary path exceeds the system limit, we may need to adjust some subsequent
517+
* assertions accordingly to ensure the success of this test. */
518+
truncated_temp_file = 1;
519+
520+
listener = evconnlistener_new_bind(base, empty_listener_cb, (void *)base,
521+
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
522+
(struct sockaddr*)&addr, sizeof(addr));
523+
tt_assert_msg(listener == NULL, "AF_UNIX listener shouldn't use SO_REUSEADDR!");
524+
525+
listener = evconnlistener_new_bind(base, empty_listener_cb, (void *)base,
526+
LEV_OPT_REUSEABLE_PORT|LEV_OPT_CLOSE_ON_FREE, -1,
527+
(struct sockaddr*)&addr, sizeof(addr));
528+
tt_assert_msg(listener == NULL, "AF_UNIX listener shouldn't use SO_REUSEPORT!");
529+
530+
/* Create a AF_UNIX listener without reusing address or port. */
531+
listener = evconnlistener_new_bind(base, empty_listener_cb, (void *)base,
532+
LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE, -1,
533+
(struct sockaddr*)&addr, sizeof(addr));
534+
if (truncated_temp_file) {
535+
tt_assert_msg(listener == NULL, "Should not create a AF_UNIX listener with truncated socket path!");
536+
} else {
537+
#ifdef _WIN32
538+
if (listener == NULL && WSAENETDOWN == EVUTIL_SOCKET_ERROR() && is_unicode_temp_path())
539+
/* TODO(panjf2000): this error kept happening on the GitHub Runner of
540+
* (windows-latest, UNOCODE_TEMPORARY_DIRECTORY) disturbingly and the
541+
* root cause still remains unknown.
542+
* Thus, we skip this for now, but try to dive deep and figure out the
543+
* root cause later. */
544+
tt_skip();
545+
#endif
546+
tt_assert_msg(listener, "Could not create a AF_UNIX listener normally!");
547+
}
548+
549+
end:
550+
if (socket_path) {
551+
unlink(socket_path);
552+
free(socket_path);
553+
}
554+
if (listener)
555+
evconnlistener_free(listener);
556+
}
557+
558+
#endif
559+
433560
struct testcase_t listener_testcases[] = {
434561

435562
{ "randport", regress_pick_a_port, TT_FORK|TT_NEED_BASE,
@@ -468,6 +595,12 @@ struct testcase_t listener_testcases[] = {
468595
&basic_setup, NULL, },
469596
#endif
470597

598+
#ifdef EVENT__HAVE_STRUCT_SOCKADDR_UN
599+
{ "reuseport_on_unix_socket", regress_listener_reuseport_on_unix_socket,
600+
TT_FORK|TT_NEED_BASE,
601+
&basic_setup, NULL, },
602+
#endif
603+
471604
END_OF_TESTCASES,
472605
};
473606

test/regress_main.c

+4
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,10 @@ main(int argc, const char **argv)
534534

535535
libevent_global_shutdown();
536536

537+
#ifdef _WIN32
538+
WSACleanup();
539+
#endif
540+
537541
return 0;
538542
}
539543

0 commit comments

Comments
 (0)