mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 09:27:59 +00:00
sshproxy: share the caller's LogPolicy.
Now new_connection() takes an optional LogPolicy * argument, and passes it on to the SshProxy setup. This means that SshProxy's implementation of the LogPolicy trait can answer queries like askappend() and logging_error() by passing them on to the same LogPolicy used by the main backend. Not all callers of new_connection have a LogPolicy, so we still have to fall back to the previous conservative default behaviour if SshProxy doesn't have a LogPolicy it can ask. The main backend implementations didn't _quite_ have access to a LogPolicy already, but they do have a LogContext, which has a LogPolicy vtable pointer inside it; so I've added a query function log_get_policy() which allows them to extract that pointer to pass to new_connection. This is the first step of fixing the non-interactivity limitations of SshProxy. But it's also the easiest step: the next ones will be more involved.
This commit is contained in:
parent
a4b8ff911b
commit
a08f953bd6
@ -81,6 +81,11 @@ void logflush(LogContext *ctx)
|
|||||||
fflush(ctx->lgfp);
|
fflush(ctx->lgfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogPolicy *log_get_policy(LogContext *ctx)
|
||||||
|
{
|
||||||
|
return ctx->lp;
|
||||||
|
}
|
||||||
|
|
||||||
static void logfopen_callback(void *vctx, int mode)
|
static void logfopen_callback(void *vctx, int mode)
|
||||||
{
|
{
|
||||||
LogContext *ctx = (LogContext *)vctx;
|
LogContext *ctx = (LogContext *)vctx;
|
||||||
|
20
network.h
20
network.h
@ -110,13 +110,22 @@ struct PlugVtable {
|
|||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
/* proxy indirection layer */
|
/* Proxy indirection layer.
|
||||||
/* NB, control of 'addr' is passed via new_connection, which takes
|
*
|
||||||
* responsibility for freeing it */
|
* Calling new_connection transfers ownership of 'addr': the proxy
|
||||||
|
* layer is now responsible for freeing it, and the caller shouldn't
|
||||||
|
* assume it exists any more.
|
||||||
|
*
|
||||||
|
* You can optionally pass a LogPolicy to this function, which will be
|
||||||
|
* passed on in turn to proxy types that can use one (e.g. SSH jump
|
||||||
|
* host proxy). If you don't have one, all proxy types are required to
|
||||||
|
* be able to manage without (and will just degrade their logging
|
||||||
|
* control).
|
||||||
|
*/
|
||||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf);
|
Plug *plug, Conf *conf, LogPolicy *lp);
|
||||||
Socket *new_listener(const char *srcaddr, int port, Plug *plug,
|
Socket *new_listener(const char *srcaddr, int port, Plug *plug,
|
||||||
bool local_host_only, Conf *conf, int addressfamily);
|
bool local_host_only, Conf *conf, int addressfamily);
|
||||||
SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
||||||
@ -134,7 +143,8 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
|
|||||||
Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf);
|
Plug *plug, Conf *conf,
|
||||||
|
LogPolicy *clientlp);
|
||||||
|
|
||||||
/* socket functions */
|
/* socket functions */
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ SockAddr *name_lookup(const char *host, int port, char **canonicalname,
|
|||||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf)
|
Plug *plug, Conf *conf, LogPolicy *lp)
|
||||||
{
|
{
|
||||||
return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);
|
return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ const bool ssh_proxy_supported = false;
|
|||||||
Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf)
|
Plug *plug, Conf *conf,
|
||||||
|
LogPolicy *clientlp)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,8 @@ static char *raw_init(const BackendVtable *vt, Seat *seat,
|
|||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
raw->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
raw->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
||||||
keepalive, &raw->plug, conf);
|
keepalive, &raw->plug, conf,
|
||||||
|
log_get_policy(logctx));
|
||||||
if ((err = sk_socket_error(raw->s)) != NULL)
|
if ((err = sk_socket_error(raw->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
|
@ -204,7 +204,8 @@ static char *rlogin_init(const BackendVtable *vt, Seat *seat,
|
|||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
rlogin->s = new_connection(addr, *realhost, port, true, false,
|
rlogin->s = new_connection(addr, *realhost, port, true, false,
|
||||||
nodelay, keepalive, &rlogin->plug, conf);
|
nodelay, keepalive, &rlogin->plug, conf,
|
||||||
|
log_get_policy(logctx));
|
||||||
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
if ((err = sk_socket_error(rlogin->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
|
@ -712,7 +712,8 @@ static char *supdup_init(const BackendVtable *x, Seat *seat,
|
|||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
supdup->s = new_connection(addr, *realhost, port, false, true,
|
supdup->s = new_connection(addr, *realhost, port, false, true,
|
||||||
nodelay, keepalive, &supdup->plug, supdup->conf);
|
nodelay, keepalive, &supdup->plug, supdup->conf,
|
||||||
|
log_get_policy(logctx));
|
||||||
if ((err = sk_socket_error(supdup->s)) != NULL)
|
if ((err = sk_socket_error(supdup->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
|
@ -739,7 +739,8 @@ static char *telnet_init(const BackendVtable *vt, Seat *seat,
|
|||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
telnet->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
telnet->s = new_connection(addr, *realhost, port, false, true, nodelay,
|
||||||
keepalive, &telnet->plug, telnet->conf);
|
keepalive, &telnet->plug, telnet->conf,
|
||||||
|
log_get_policy(logctx));
|
||||||
if ((err = sk_socket_error(telnet->s)) != NULL)
|
if ((err = sk_socket_error(telnet->s)) != NULL)
|
||||||
return dupstr(err);
|
return dupstr(err);
|
||||||
|
|
||||||
|
4
proxy.c
4
proxy.c
@ -395,7 +395,7 @@ static const PlugVtable ProxySocket_plugvt = {
|
|||||||
Socket *new_connection(SockAddr *addr, const char *hostname,
|
Socket *new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *conf)
|
Plug *plug, Conf *conf, LogPolicy *lp)
|
||||||
{
|
{
|
||||||
int type = conf_get_int(conf, CONF_proxy_type);
|
int type = conf_get_int(conf, CONF_proxy_type);
|
||||||
|
|
||||||
@ -411,7 +411,7 @@ Socket *new_connection(SockAddr *addr, const char *hostname,
|
|||||||
if (type == PROXY_SSH &&
|
if (type == PROXY_SSH &&
|
||||||
(sret = sshproxy_new_connection(addr, hostname, port, privport,
|
(sret = sshproxy_new_connection(addr, hostname, port, privport,
|
||||||
oobinline, nodelay, keepalive,
|
oobinline, nodelay, keepalive,
|
||||||
plug, conf)) != NULL)
|
plug, conf, lp)) != NULL)
|
||||||
return sret;
|
return sret;
|
||||||
|
|
||||||
if ((sret = platform_new_connection(addr, hostname, port, privport,
|
if ((sret = platform_new_connection(addr, hostname, port, privport,
|
||||||
|
1
putty.h
1
putty.h
@ -1962,6 +1962,7 @@ void logfopen(LogContext *logctx);
|
|||||||
void logfclose(LogContext *logctx);
|
void logfclose(LogContext *logctx);
|
||||||
void logtraffic(LogContext *logctx, unsigned char c, int logmode);
|
void logtraffic(LogContext *logctx, unsigned char c, int logmode);
|
||||||
void logflush(LogContext *logctx);
|
void logflush(LogContext *logctx);
|
||||||
|
LogPolicy *log_get_policy(LogContext *logctx);
|
||||||
void logevent(LogContext *logctx, const char *event);
|
void logevent(LogContext *logctx, const char *event);
|
||||||
void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3);
|
void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3);
|
||||||
void logeventvf(LogContext *logctx, const char *fmt, va_list ap);
|
void logeventvf(LogContext *logctx, const char *fmt, va_list ap);
|
||||||
|
@ -1160,7 +1160,8 @@ char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
|
|||||||
pf->socks_state = SOCKS_NONE;
|
pf->socks_state = SOCKS_NONE;
|
||||||
|
|
||||||
pf->s = new_connection(addr, dummy_realhost, port,
|
pf->s = new_connection(addr, dummy_realhost, port,
|
||||||
false, true, false, false, &pf->plug, mgr->conf);
|
false, true, false, false, &pf->plug, mgr->conf,
|
||||||
|
NULL);
|
||||||
sfree(dummy_realhost);
|
sfree(dummy_realhost);
|
||||||
if ((err = sk_socket_error(pf->s)) != NULL) {
|
if ((err = sk_socket_error(pf->s)) != NULL) {
|
||||||
char *err_ret = dupstr(err);
|
char *err_ret = dupstr(err);
|
||||||
|
@ -789,7 +789,8 @@ static char *connect_to_host(
|
|||||||
|
|
||||||
ssh->s = new_connection(addr, *realhost, port,
|
ssh->s = new_connection(addr, *realhost, port,
|
||||||
false, true, nodelay, keepalive,
|
false, true, nodelay, keepalive,
|
||||||
&ssh->plug, ssh->conf);
|
&ssh->plug, ssh->conf,
|
||||||
|
log_get_policy(ssh->logctx));
|
||||||
if ((err = sk_socket_error(ssh->s)) != NULL) {
|
if ((err = sk_socket_error(ssh->s)) != NULL) {
|
||||||
ssh->s = NULL;
|
ssh->s = NULL;
|
||||||
seat_notify_remote_exit(ssh->seat);
|
seat_notify_remote_exit(ssh->seat);
|
||||||
|
@ -564,7 +564,7 @@ static size_t x11_send(
|
|||||||
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
|
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
|
||||||
xconn->disp->realhost, xconn->disp->port,
|
xconn->disp->realhost, xconn->disp->port,
|
||||||
false, true, false, false, &xconn->plug,
|
false, true, false, false, &xconn->plug,
|
||||||
sshfwd_get_conf(xconn->c));
|
sshfwd_get_conf(xconn->c), NULL);
|
||||||
if ((err = sk_socket_error(xconn->s)) != NULL) {
|
if ((err = sk_socket_error(xconn->s)) != NULL) {
|
||||||
char *err_message = dupprintf("unable to connect to"
|
char *err_message = dupprintf("unable to connect to"
|
||||||
" forwarded X server: %s", err);
|
" forwarded X server: %s", err);
|
||||||
|
81
sshproxy.c
81
sshproxy.c
@ -16,15 +16,15 @@ const bool ssh_proxy_supported = true;
|
|||||||
/*
|
/*
|
||||||
* TODO for future work:
|
* TODO for future work:
|
||||||
*
|
*
|
||||||
* At present, this use of SSH as a proxy is 100% noninteractive. In
|
* At present, this use of SSH as a proxy is mostly noninteractive. In
|
||||||
* our implementations of the Seat and LogPolicy traits, every method
|
* our implementations of the Seat trait, every method that involves
|
||||||
* that involves interactively prompting the user is implemented by
|
* interactively prompting the user is implemented by pretending the
|
||||||
* pretending the user gave a safe default answer. So the effect is
|
* user gave a safe default answer. So the effect is very much as if
|
||||||
* very much as if you'd used 'plink -batch' as a proxy subprocess -
|
* you'd used 'plink -batch' as a proxy subprocess - password prompts
|
||||||
* password prompts are cancelled and any dubious host key or crypto
|
* are cancelled and any dubious host key or crypto primitive is
|
||||||
* primitive is unconditionally rejected - except that it all happens
|
* unconditionally rejected - except that it all happens in-process,
|
||||||
* in-process, making it mildly more convenient to set up, perhaps a
|
* making it mildly more convenient to set up, perhaps a hair faster,
|
||||||
* hair faster, and you get all the Event Log data in one place.
|
* and you get all the Event Log data in one place.
|
||||||
*
|
*
|
||||||
* But the biggest benefit of in-process SSH proxying would be that
|
* But the biggest benefit of in-process SSH proxying would be that
|
||||||
* the interactive prompts from the sub-SSH can be passed through to
|
* the interactive prompts from the sub-SSH can be passed through to
|
||||||
@ -32,24 +32,21 @@ const bool ssh_proxy_supported = true;
|
|||||||
* both require password authentication, you should be able to type
|
* both require password authentication, you should be able to type
|
||||||
* both password in sequence into the PuTTY terminal window; if you're
|
* both password in sequence into the PuTTY terminal window; if you're
|
||||||
* running a session of this kind for the first time, you should be
|
* running a session of this kind for the first time, you should be
|
||||||
* able to confirm both host keys one after another; if you need to
|
* able to confirm both host keys one after another. In the current
|
||||||
* store SSH packet logs from both SSH connections, you should be able
|
* state of the code, none of that is yet implemented: we're borrowing
|
||||||
* to respond in turn to two askappend() prompts if necessary. And in
|
* the client LogPolicy for things like askappend(), but not the Seat,
|
||||||
* the current state of the code, none of that is yet implemented.
|
* which is where all the really important stuff lives.
|
||||||
*
|
*
|
||||||
* To fix that, we'd have to start by arranging for this proxy
|
* To fix that, we'd have to start by arranging for this proxy
|
||||||
* implementation to get hold of the 'real' (outer) Seat and LogPolicy
|
* implementation to get hold of the 'real' (outer) Seat object, which
|
||||||
* objects, which probably means that they'd have to be passed to
|
* means passing it to new_connection as we're already doing with
|
||||||
* new_connection. Then, each method in this file that receives an
|
* LogPolicy. Then, each method in this file that receives an
|
||||||
* interactive prompt request would handle it by passing it on to the
|
* interactive prompt request would handle it by passing it on to the
|
||||||
* outer Seat or LogPolicy, with some kind of tweak that would allow
|
* client Seat if present, with some kind of tweak that would allow
|
||||||
* the end user to see clearly that the prompt had come from the proxy
|
* the end user to see clearly that the prompt had come from the proxy
|
||||||
* SSH connection rather than the primary one.
|
* SSH connection rather than the primary one. If no client Seat was
|
||||||
*
|
* present, we'd have no choice but to fall back to the existing
|
||||||
* One problem here is that not all uses of new_connection _have_ a
|
* behaviour of behaving as if in batch mode.
|
||||||
* Seat or a LogPolicy available. So we'd also have to check if those
|
|
||||||
* pointers are NULL, and if so, fall back to the existing behaviour
|
|
||||||
* of behaving as if in batch mode.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef struct SshProxy {
|
typedef struct SshProxy {
|
||||||
@ -57,6 +54,7 @@ typedef struct SshProxy {
|
|||||||
Conf *conf;
|
Conf *conf;
|
||||||
LogContext *logctx;
|
LogContext *logctx;
|
||||||
Backend *backend;
|
Backend *backend;
|
||||||
|
LogPolicy *clientlp;
|
||||||
|
|
||||||
ProxyStderrBuf psb;
|
ProxyStderrBuf psb;
|
||||||
Plug *plug;
|
Plug *plug;
|
||||||
@ -171,10 +169,17 @@ static int sshproxy_askappend(LogPolicy *lp, Filename *filename,
|
|||||||
void (*callback)(void *ctx, int result),
|
void (*callback)(void *ctx, int result),
|
||||||
void *ctx)
|
void *ctx)
|
||||||
{
|
{
|
||||||
|
SshProxy *sp = container_of(lp, SshProxy, logpolicy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: if we had access to the outer LogPolicy, we could pass on
|
* If we have access to the outer LogPolicy, pass on this request
|
||||||
* this request to the end user. (But we'd still have to have this
|
* to the end user.
|
||||||
* code as a fallback in case there isn't a LogPolicy available.)
|
*/
|
||||||
|
if (sp->clientlp)
|
||||||
|
return lp_askappend(sp->clientlp, filename, callback, ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, fall back to the safe noninteractive assumption.
|
||||||
*/
|
*/
|
||||||
char *msg = dupprintf("Log file \"%s\" already exists; logging cancelled",
|
char *msg = dupprintf("Log file \"%s\" already exists; logging cancelled",
|
||||||
filename_to_str(filename));
|
filename_to_str(filename));
|
||||||
@ -185,12 +190,20 @@ static int sshproxy_askappend(LogPolicy *lp, Filename *filename,
|
|||||||
|
|
||||||
static void sshproxy_logging_error(LogPolicy *lp, const char *event)
|
static void sshproxy_logging_error(LogPolicy *lp, const char *event)
|
||||||
{
|
{
|
||||||
|
SshProxy *sp = container_of(lp, SshProxy, logpolicy);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: if we had access to the outer LogPolicy, we could pass on
|
* If we have access to the outer LogPolicy, pass on this request
|
||||||
* this request to _its_ logging_error method, where it would be
|
* to it.
|
||||||
* more prominent than just dumping it in the outer SSH
|
*/
|
||||||
* connection's Event Log. (But we'd still have to have this code
|
if (sp->clientlp) {
|
||||||
* as a fallback in case there isn't a LogPolicy available.)
|
lp_logging_error(sp->clientlp, event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Otherwise, the best we can do is to put it in the outer SSH
|
||||||
|
* connection's Event Log.
|
||||||
*/
|
*/
|
||||||
char *msg = dupprintf("Logging error: %s", event);
|
char *msg = dupprintf("Logging error: %s", event);
|
||||||
sshproxy_eventlog(lp, msg);
|
sshproxy_eventlog(lp, msg);
|
||||||
@ -459,13 +472,13 @@ static const SeatVtable SshProxy_seat_vt = {
|
|||||||
.verbose = nullseat_verbose_no,
|
.verbose = nullseat_verbose_no,
|
||||||
.interactive = nullseat_interactive_no,
|
.interactive = nullseat_interactive_no,
|
||||||
.get_cursor_position = nullseat_get_cursor_position,
|
.get_cursor_position = nullseat_get_cursor_position,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
||||||
int port, bool privport,
|
int port, bool privport,
|
||||||
bool oobinline, bool nodelay, bool keepalive,
|
bool oobinline, bool nodelay, bool keepalive,
|
||||||
Plug *plug, Conf *clientconf)
|
Plug *plug, Conf *clientconf,
|
||||||
|
LogPolicy *clientlp)
|
||||||
{
|
{
|
||||||
SshProxy *sp = snew(SshProxy);
|
SshProxy *sp = snew(SshProxy);
|
||||||
memset(sp, 0, sizeof(*sp));
|
memset(sp, 0, sizeof(*sp));
|
||||||
@ -575,5 +588,7 @@ Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
|
|||||||
|
|
||||||
sfree(realhost);
|
sfree(realhost);
|
||||||
|
|
||||||
|
sp->clientlp = clientlp;
|
||||||
|
|
||||||
return &sp->sock;
|
return &sp->sock;
|
||||||
}
|
}
|
||||||
|
@ -1189,7 +1189,8 @@ void run_agent(FILE *logfp, const char *symlink_path)
|
|||||||
conn->plug.vt = &X11Connection_plugvt;
|
conn->plug.vt = &X11Connection_plugvt;
|
||||||
s = new_connection(sk_addr_dup(disp->addr),
|
s = new_connection(sk_addr_dup(disp->addr),
|
||||||
disp->realhost, disp->port,
|
disp->realhost, disp->port,
|
||||||
false, true, false, false, &conn->plug, conf);
|
false, true, false, false, &conn->plug, conf,
|
||||||
|
NULL);
|
||||||
if ((err = sk_socket_error(s)) != NULL) {
|
if ((err = sk_socket_error(s)) != NULL) {
|
||||||
fprintf(stderr, "pageant: unable to connect to X server: %s", err);
|
fprintf(stderr, "pageant: unable to connect to X server: %s", err);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -297,7 +297,7 @@ int platform_ssh_share(const char *pi_name, Conf *conf,
|
|||||||
if (can_downstream) {
|
if (can_downstream) {
|
||||||
retsock = new_connection(unix_sock_addr(sockname),
|
retsock = new_connection(unix_sock_addr(sockname),
|
||||||
"", 0, false, true, false, false,
|
"", 0, false, true, false, false,
|
||||||
downplug, conf);
|
downplug, conf, NULL);
|
||||||
if (sk_socket_error(retsock) == NULL) {
|
if (sk_socket_error(retsock) == NULL) {
|
||||||
sfree(*logtext);
|
sfree(*logtext);
|
||||||
*logtext = sockname;
|
*logtext = sockname;
|
||||||
|
Loading…
Reference in New Issue
Block a user