mirror of
https://github.com/curl/curl.git
synced 2025-09-17 17:42:49 +03:00
parent
77a6bf8489
commit
80eb71a3f5
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -59,3 +59,4 @@ scripts/curl.fish
|
||||||
curl_fuzzer
|
curl_fuzzer
|
||||||
curl_fuzzer_seed_corpus.zip
|
curl_fuzzer_seed_corpus.zip
|
||||||
libstandaloneengine.a
|
libstandaloneengine.a
|
||||||
|
tests/string
|
||||||
|
|
|
@ -143,10 +143,7 @@ const char *serverlogfile = DEFAULT_LOGFILE;
|
||||||
const char *reqlogfile = DEFAULT_REQFILE;
|
const char *reqlogfile = DEFAULT_REQFILE;
|
||||||
static const char *configfile = DEFAULT_CONFIG;
|
static const char *configfile = DEFAULT_CONFIG;
|
||||||
|
|
||||||
#ifdef ENABLE_IPV6
|
static const char *socket_type = "IPv4";
|
||||||
static bool use_ipv6 = FALSE;
|
|
||||||
#endif
|
|
||||||
static const char *ipv_inuse = "IPv4";
|
|
||||||
static unsigned short port = DEFAULT_PORT;
|
static unsigned short port = DEFAULT_PORT;
|
||||||
|
|
||||||
static void resetdefaults(void)
|
static void resetdefaults(void)
|
||||||
|
@ -177,6 +174,16 @@ static unsigned short shortval(char *value)
|
||||||
return num & 0xffff;
|
return num & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum {
|
||||||
|
socket_domain_inet = AF_INET
|
||||||
|
#ifdef ENABLE_IPV6
|
||||||
|
, socket_domain_inet6 = AF_INET6
|
||||||
|
#endif
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
, socket_domain_unix = AF_UNIX
|
||||||
|
#endif
|
||||||
|
} socket_domain = AF_INET;
|
||||||
|
|
||||||
static void getconfig(void)
|
static void getconfig(void)
|
||||||
{
|
{
|
||||||
FILE *fp = fopen(configfile, FOPEN_READTEXT);
|
FILE *fp = fopen(configfile, FOPEN_READTEXT);
|
||||||
|
@ -777,7 +784,11 @@ static bool incoming(curl_socket_t listenfd)
|
||||||
}
|
}
|
||||||
|
|
||||||
static curl_socket_t sockdaemon(curl_socket_t sock,
|
static curl_socket_t sockdaemon(curl_socket_t sock,
|
||||||
unsigned short *listenport)
|
unsigned short *listenport
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
, const char *unix_socket
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
/* passive daemon style */
|
/* passive daemon style */
|
||||||
srvr_sockaddr_union_t listener;
|
srvr_sockaddr_union_t listener;
|
||||||
|
@ -828,24 +839,29 @@ static curl_socket_t sockdaemon(curl_socket_t sock,
|
||||||
/* When the specified listener port is zero, it is actually a
|
/* When the specified listener port is zero, it is actually a
|
||||||
request to let the system choose a non-zero available port. */
|
request to let the system choose a non-zero available port. */
|
||||||
|
|
||||||
#ifdef ENABLE_IPV6
|
switch(socket_domain) {
|
||||||
if(!use_ipv6) {
|
case AF_INET:
|
||||||
#endif
|
|
||||||
memset(&listener.sa4, 0, sizeof(listener.sa4));
|
memset(&listener.sa4, 0, sizeof(listener.sa4));
|
||||||
listener.sa4.sin_family = AF_INET;
|
listener.sa4.sin_family = AF_INET;
|
||||||
listener.sa4.sin_addr.s_addr = INADDR_ANY;
|
listener.sa4.sin_addr.s_addr = INADDR_ANY;
|
||||||
listener.sa4.sin_port = htons(*listenport);
|
listener.sa4.sin_port = htons(*listenport);
|
||||||
rc = bind(sock, &listener.sa, sizeof(listener.sa4));
|
rc = bind(sock, &listener.sa, sizeof(listener.sa4));
|
||||||
|
break;
|
||||||
#ifdef ENABLE_IPV6
|
#ifdef ENABLE_IPV6
|
||||||
}
|
case AF_INET6:
|
||||||
else {
|
|
||||||
memset(&listener.sa6, 0, sizeof(listener.sa6));
|
memset(&listener.sa6, 0, sizeof(listener.sa6));
|
||||||
listener.sa6.sin6_family = AF_INET6;
|
listener.sa6.sin6_family = AF_INET6;
|
||||||
listener.sa6.sin6_addr = in6addr_any;
|
listener.sa6.sin6_addr = in6addr_any;
|
||||||
listener.sa6.sin6_port = htons(*listenport);
|
listener.sa6.sin6_port = htons(*listenport);
|
||||||
rc = bind(sock, &listener.sa, sizeof(listener.sa6));
|
rc = bind(sock, &listener.sa, sizeof(listener.sa6));
|
||||||
}
|
break;
|
||||||
#endif /* ENABLE_IPV6 */
|
#endif /* ENABLE_IPV6 */
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
case AF_UNIX:
|
||||||
|
rc = bind_unix_socket(sock, unix_socket, &listener.sau);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if(rc) {
|
if(rc) {
|
||||||
error = SOCKERRNO;
|
error = SOCKERRNO;
|
||||||
logmsg("Error binding socket on port %hu: (%d) %s",
|
logmsg("Error binding socket on port %hu: (%d) %s",
|
||||||
|
@ -854,19 +870,21 @@ static curl_socket_t sockdaemon(curl_socket_t sock,
|
||||||
return CURL_SOCKET_BAD;
|
return CURL_SOCKET_BAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!*listenport) {
|
if(!*listenport
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
&& !unix_socket
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
/* The system was supposed to choose a port number, figure out which
|
/* The system was supposed to choose a port number, figure out which
|
||||||
port we actually got and update the listener port value with it. */
|
port we actually got and update the listener port value with it. */
|
||||||
curl_socklen_t la_size;
|
curl_socklen_t la_size;
|
||||||
srvr_sockaddr_union_t localaddr;
|
srvr_sockaddr_union_t localaddr;
|
||||||
#ifdef ENABLE_IPV6
|
#ifdef ENABLE_IPV6
|
||||||
if(!use_ipv6)
|
if(socket_domain == AF_INET6)
|
||||||
|
la_size = sizeof(localaddr.sa6);
|
||||||
|
else
|
||||||
#endif
|
#endif
|
||||||
la_size = sizeof(localaddr.sa4);
|
la_size = sizeof(localaddr.sa4);
|
||||||
#ifdef ENABLE_IPV6
|
|
||||||
else
|
|
||||||
la_size = sizeof(localaddr.sa6);
|
|
||||||
#endif
|
|
||||||
memset(&localaddr.sa, 0, (size_t)la_size);
|
memset(&localaddr.sa, 0, (size_t)la_size);
|
||||||
if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
|
if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
|
||||||
error = SOCKERRNO;
|
error = SOCKERRNO;
|
||||||
|
@ -924,6 +942,11 @@ int main(int argc, char *argv[])
|
||||||
int error;
|
int error;
|
||||||
int arg = 1;
|
int arg = 1;
|
||||||
|
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
const char *unix_socket = NULL;
|
||||||
|
bool unlink_socket = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
while(argc>arg) {
|
while(argc>arg) {
|
||||||
if(!strcmp("--version", argv[arg])) {
|
if(!strcmp("--version", argv[arg])) {
|
||||||
printf("socksd IPv4%s\n",
|
printf("socksd IPv4%s\n",
|
||||||
|
@ -972,19 +995,36 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
else if(!strcmp("--ipv6", argv[arg])) {
|
else if(!strcmp("--ipv6", argv[arg])) {
|
||||||
#ifdef ENABLE_IPV6
|
#ifdef ENABLE_IPV6
|
||||||
ipv_inuse = "IPv6";
|
socket_domain = AF_INET6;
|
||||||
use_ipv6 = TRUE;
|
socket_type = "IPv6";
|
||||||
#endif
|
#endif
|
||||||
arg++;
|
arg++;
|
||||||
}
|
}
|
||||||
else if(!strcmp("--ipv4", argv[arg])) {
|
else if(!strcmp("--ipv4", argv[arg])) {
|
||||||
/* for completeness, we support this option as well */
|
/* for completeness, we support this option as well */
|
||||||
#ifdef ENABLE_IPV6
|
#ifdef ENABLE_IPV6
|
||||||
ipv_inuse = "IPv4";
|
socket_type = "IPv4";
|
||||||
use_ipv6 = FALSE;
|
|
||||||
#endif
|
#endif
|
||||||
arg++;
|
arg++;
|
||||||
}
|
}
|
||||||
|
else if(!strcmp("--unix-socket", argv[arg])) {
|
||||||
|
arg++;
|
||||||
|
if(argc>arg) {
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
struct sockaddr_un sau;
|
||||||
|
unix_socket = argv[arg];
|
||||||
|
if(strlen(unix_socket) >= sizeof(sau.sun_path)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"socksd: socket path must be shorter than %zu chars\n",
|
||||||
|
sizeof(sau.sun_path));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
socket_domain = AF_UNIX;
|
||||||
|
socket_type = "unix";
|
||||||
|
#endif
|
||||||
|
arg++;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if(!strcmp("--port", argv[arg])) {
|
else if(!strcmp("--port", argv[arg])) {
|
||||||
arg++;
|
arg++;
|
||||||
if(argc>arg) {
|
if(argc>arg) {
|
||||||
|
@ -1006,6 +1046,7 @@ int main(int argc, char *argv[])
|
||||||
" --reqfile [file]\n"
|
" --reqfile [file]\n"
|
||||||
" --ipv4\n"
|
" --ipv4\n"
|
||||||
" --ipv6\n"
|
" --ipv6\n"
|
||||||
|
" --unix-socket [file]\n"
|
||||||
" --bindonly\n"
|
" --bindonly\n"
|
||||||
" --port [port]\n");
|
" --port [port]\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1023,14 +1064,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
install_signal_handlers(false);
|
install_signal_handlers(false);
|
||||||
|
|
||||||
#ifdef ENABLE_IPV6
|
sock = socket(socket_domain, SOCK_STREAM, 0);
|
||||||
if(!use_ipv6)
|
|
||||||
#endif
|
|
||||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
#ifdef ENABLE_IPV6
|
|
||||||
else
|
|
||||||
sock = socket(AF_INET6, SOCK_STREAM, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(CURL_SOCKET_BAD == sock) {
|
if(CURL_SOCKET_BAD == sock) {
|
||||||
error = SOCKERRNO;
|
error = SOCKERRNO;
|
||||||
|
@ -1041,14 +1075,27 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
{
|
{
|
||||||
/* passive daemon style */
|
/* passive daemon style */
|
||||||
sock = sockdaemon(sock, &port);
|
sock = sockdaemon(sock, &port
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
, unix_socket
|
||||||
|
#endif
|
||||||
|
);
|
||||||
if(CURL_SOCKET_BAD == sock) {
|
if(CURL_SOCKET_BAD == sock) {
|
||||||
goto socks5_cleanup;
|
goto socks5_cleanup;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
unlink_socket = true;
|
||||||
|
#endif
|
||||||
msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
|
msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
|
||||||
}
|
}
|
||||||
|
|
||||||
logmsg("Running %s version", ipv_inuse);
|
logmsg("Running %s version", socket_type);
|
||||||
|
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
if(socket_domain == AF_UNIX)
|
||||||
|
logmsg("Listening on unix socket %s", unix_socket);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
logmsg("Listening on port %hu", port);
|
logmsg("Listening on port %hu", port);
|
||||||
|
|
||||||
wrotepidfile = write_pidfile(pidname);
|
wrotepidfile = write_pidfile(pidname);
|
||||||
|
@ -1075,6 +1122,13 @@ socks5_cleanup:
|
||||||
if(sock != CURL_SOCKET_BAD)
|
if(sock != CURL_SOCKET_BAD)
|
||||||
sclose(sock);
|
sclose(sock);
|
||||||
|
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
if(unlink_socket && socket_domain == AF_UNIX) {
|
||||||
|
error = unlink(unix_socket);
|
||||||
|
logmsg("unlink(%s) = %d (%s)", unix_socket, error, strerror(error));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if(wrotepidfile)
|
if(wrotepidfile)
|
||||||
unlink(pidname);
|
unlink(pidname);
|
||||||
if(wroteportfile)
|
if(wroteportfile)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* | (__| |_| | _ <| |___
|
* | (__| |_| | _ <| |___
|
||||||
* \___|\___/|_| \_\_____|
|
* \___|\___/|_| \_\_____|
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
*
|
*
|
||||||
* This software is licensed as described in the file COPYING, which
|
* This software is licensed as described in the file COPYING, which
|
||||||
* you should have received as part of this distribution. The terms
|
* you should have received as part of this distribution. The terms
|
||||||
|
@ -2066,59 +2066,7 @@ int main(int argc, char *argv[])
|
||||||
#endif /* ENABLE_IPV6 */
|
#endif /* ENABLE_IPV6 */
|
||||||
#ifdef USE_UNIX_SOCKETS
|
#ifdef USE_UNIX_SOCKETS
|
||||||
case AF_UNIX:
|
case AF_UNIX:
|
||||||
memset(&me.sau, 0, sizeof(me.sau));
|
rc = bind_unix_socket(sock, unix_socket, &me.sau);
|
||||||
me.sau.sun_family = AF_UNIX;
|
|
||||||
strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path) - 1);
|
|
||||||
rc = bind(sock, &me.sa, sizeof(me.sau));
|
|
||||||
if(0 != rc && errno == EADDRINUSE) {
|
|
||||||
struct_stat statbuf;
|
|
||||||
/* socket already exists. Perhaps it is stale? */
|
|
||||||
curl_socket_t unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
||||||
if(CURL_SOCKET_BAD == unixfd) {
|
|
||||||
error = SOCKERRNO;
|
|
||||||
logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
|
|
||||||
unix_socket, error, strerror(error));
|
|
||||||
goto sws_cleanup;
|
|
||||||
}
|
|
||||||
/* check whether the server is alive */
|
|
||||||
rc = connect(unixfd, &me.sa, sizeof(me.sau));
|
|
||||||
error = errno;
|
|
||||||
sclose(unixfd);
|
|
||||||
if(ECONNREFUSED != error) {
|
|
||||||
logmsg("Error binding socket, failed to connect to %s: (%d) %s",
|
|
||||||
unix_socket, error, strerror(error));
|
|
||||||
goto sws_cleanup;
|
|
||||||
}
|
|
||||||
/* socket server is not alive, now check if it was actually a socket. */
|
|
||||||
#ifdef WIN32
|
|
||||||
/* Windows does not have lstat function. */
|
|
||||||
rc = curlx_win32_stat(unix_socket, &statbuf);
|
|
||||||
#else
|
|
||||||
rc = lstat(unix_socket, &statbuf);
|
|
||||||
#endif
|
|
||||||
if(0 != rc) {
|
|
||||||
logmsg("Error binding socket, failed to stat %s: (%d) %s",
|
|
||||||
unix_socket, errno, strerror(errno));
|
|
||||||
goto sws_cleanup;
|
|
||||||
}
|
|
||||||
#ifdef S_IFSOCK
|
|
||||||
if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
|
|
||||||
logmsg("Error binding socket, failed to stat %s: (%d) %s",
|
|
||||||
unix_socket, error, strerror(error));
|
|
||||||
goto sws_cleanup;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* dead socket, cleanup and retry bind */
|
|
||||||
rc = unlink(unix_socket);
|
|
||||||
if(0 != rc) {
|
|
||||||
logmsg("Error binding socket, failed to unlink %s: (%d) %s",
|
|
||||||
unix_socket, errno, strerror(errno));
|
|
||||||
goto sws_cleanup;
|
|
||||||
}
|
|
||||||
/* stale socket is gone, retry bind */
|
|
||||||
rc = bind(sock, &me.sa, sizeof(me.sau));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif /* USE_UNIX_SOCKETS */
|
#endif /* USE_UNIX_SOCKETS */
|
||||||
}
|
}
|
||||||
if(0 != rc) {
|
if(0 != rc) {
|
||||||
|
|
|
@ -809,3 +809,66 @@ void restore_signal_handlers(bool keep_sigalrm)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
|
||||||
|
int bind_unix_socket(curl_socket_t sock, const char *unix_socket,
|
||||||
|
struct sockaddr_un *sau) {
|
||||||
|
int error;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
memset(sau, 0, sizeof(struct sockaddr_un));
|
||||||
|
sau->sun_family = AF_UNIX;
|
||||||
|
strncpy(sau->sun_path, unix_socket, sizeof(sau->sun_path) - 1);
|
||||||
|
rc = bind(sock, (struct sockaddr*)sau, sizeof(struct sockaddr_un));
|
||||||
|
if(0 != rc && errno == EADDRINUSE) {
|
||||||
|
struct_stat statbuf;
|
||||||
|
/* socket already exists. Perhaps it is stale? */
|
||||||
|
curl_socket_t unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if(CURL_SOCKET_BAD == unixfd) {
|
||||||
|
error = SOCKERRNO;
|
||||||
|
logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
|
||||||
|
unix_socket, error, strerror(error));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* check whether the server is alive */
|
||||||
|
rc = connect(unixfd, (struct sockaddr*)sau, sizeof(struct sockaddr_un));
|
||||||
|
error = errno;
|
||||||
|
sclose(unixfd);
|
||||||
|
if(ECONNREFUSED != error) {
|
||||||
|
logmsg("Error binding socket, failed to connect to %s: (%d) %s",
|
||||||
|
unix_socket, error, strerror(error));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* socket server is not alive, now check if it was actually a socket. */
|
||||||
|
#ifdef WIN32
|
||||||
|
/* Windows does not have lstat function. */
|
||||||
|
rc = curlx_win32_stat(unix_socket, &statbuf);
|
||||||
|
#else
|
||||||
|
rc = lstat(unix_socket, &statbuf);
|
||||||
|
#endif
|
||||||
|
if(0 != rc) {
|
||||||
|
logmsg("Error binding socket, failed to stat %s: (%d) %s",
|
||||||
|
unix_socket, errno, strerror(errno));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#ifdef S_IFSOCK
|
||||||
|
if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
|
||||||
|
logmsg("Error binding socket, failed to stat %s: (%d) %s",
|
||||||
|
unix_socket, error, strerror(error));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* dead socket, cleanup and retry bind */
|
||||||
|
rc = unlink(unix_socket);
|
||||||
|
if(0 != rc) {
|
||||||
|
logmsg("Error binding socket, failed to unlink %s: (%d) %s",
|
||||||
|
unix_socket, errno, strerror(errno));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* stale socket is gone, retry bind */
|
||||||
|
rc = bind(sock, (struct sockaddr*)sau, sizeof(struct sockaddr_un));
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* | (__| |_| | _ <| |___
|
* | (__| |_| | _ <| |___
|
||||||
* \___|\___/|_| \_\_____|
|
* \___|\___/|_| \_\_____|
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||||
*
|
*
|
||||||
* This software is licensed as described in the file COPYING, which
|
* This software is licensed as described in the file COPYING, which
|
||||||
* you should have received as part of this distribution. The terms
|
* you should have received as part of this distribution. The terms
|
||||||
|
@ -79,4 +79,14 @@ extern HANDLE exit_event;
|
||||||
void install_signal_handlers(bool keep_sigalrm);
|
void install_signal_handlers(bool keep_sigalrm);
|
||||||
void restore_signal_handlers(bool keep_sigalrm);
|
void restore_signal_handlers(bool keep_sigalrm);
|
||||||
|
|
||||||
|
#ifdef USE_UNIX_SOCKETS
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_UN_H
|
||||||
|
#include <sys/un.h> /* for sockaddr_un */
|
||||||
|
#endif /* HAVE_SYS_UN_H */
|
||||||
|
|
||||||
|
int bind_unix_socket(curl_socket_t sock, const char *unix_socket,
|
||||||
|
struct sockaddr_un *sau);
|
||||||
|
#endif /* USE_UNIX_SOCKETS */
|
||||||
|
|
||||||
#endif /* HEADER_CURL_SERVER_UTIL_H */
|
#endif /* HEADER_CURL_SERVER_UTIL_H */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user