mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 09:58:01 +00:00
Refactor ssh.c's APIs to x11fwd.c and portfwd.c.
The most important change is that, where previously ssh.c held the Socket pointer for each X11 and port forwarding, and the support modules would find their internal state structure by calling sk_get_private_ptr on that Socket, it's now the other way round. ssh.c now directly holds the internal state structure pointer for each forwarding, and when the support module needs the Socket it looks it up in a field of that. This will come in handy when I decouple socket creation from logical forwarding setup, so that X forwardings can delay actually opening a connection to an X server until they look at the authentication data and see which server it has to be. However, while I'm here, I've also taken the opportunity to clean up a few other points, notably error message handling, and also the fact that the same kind of state structure was used for both connection-type and listening-type port forwardings. Now there are separate PortForwarding and PortListener structure types, which seems far more sensible. [originally from svn r10074]
This commit is contained in:
parent
489590cbd4
commit
9cbcd17651
495
portfwd.c
495
portfwd.c
@ -15,10 +15,10 @@
|
|||||||
#define TRUE 1
|
#define TRUE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct PFwdPrivate {
|
struct PortForwarding {
|
||||||
const struct plug_function_table *fn;
|
const struct plug_function_table *fn;
|
||||||
/* the above variable absolutely *must* be the first in this structure */
|
/* the above variable absolutely *must* be the first in this structure */
|
||||||
void *c; /* (channel) data used by ssh.c */
|
struct ssh_channel *c; /* channel structure held by ssh.c */
|
||||||
void *backhandle; /* instance of SSH backend itself */
|
void *backhandle; /* instance of SSH backend itself */
|
||||||
/* Note that backhandle need not be filled in if c is non-NULL */
|
/* Note that backhandle need not be filled in if c is non-NULL */
|
||||||
Socket s;
|
Socket s;
|
||||||
@ -27,8 +27,8 @@ struct PFwdPrivate {
|
|||||||
/*
|
/*
|
||||||
* `dynamic' does double duty. It's set to 0 for an ordinary
|
* `dynamic' does double duty. It's set to 0 for an ordinary
|
||||||
* forwarded port, and nonzero for SOCKS-style dynamic port
|
* forwarded port, and nonzero for SOCKS-style dynamic port
|
||||||
* forwarding; but it also represents the state of the SOCKS
|
* forwarding; but the nonzero values are also a state machine
|
||||||
* exchange.
|
* tracking where the SOCKS exchange has got to.
|
||||||
*/
|
*/
|
||||||
int dynamic;
|
int dynamic;
|
||||||
/*
|
/*
|
||||||
@ -52,24 +52,57 @@ struct PFwdPrivate {
|
|||||||
int buflen;
|
int buflen;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct PFwdPrivate *new_portfwd_private(void)
|
struct PortListener {
|
||||||
|
const struct plug_function_table *fn;
|
||||||
|
/* the above variable absolutely *must* be the first in this structure */
|
||||||
|
void *backhandle; /* instance of SSH backend itself */
|
||||||
|
Socket s;
|
||||||
|
/*
|
||||||
|
* `dynamic' is set to 0 for an ordinary forwarded port, and
|
||||||
|
* nonzero for SOCKS-style dynamic port forwarding.
|
||||||
|
*/
|
||||||
|
int dynamic;
|
||||||
|
/*
|
||||||
|
* `hostname' and `port' are the real hostname and port, for
|
||||||
|
* ordinary forwardings.
|
||||||
|
*/
|
||||||
|
char *hostname;
|
||||||
|
int port;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PortForwarding *new_portfwd_state(void)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr = snew(struct PFwdPrivate);
|
struct PortForwarding *pf = snew(struct PortForwarding);
|
||||||
pr->hostname = NULL;
|
pf->hostname = NULL;
|
||||||
pr->socksbuf = NULL;
|
pf->socksbuf = NULL;
|
||||||
pr->sockslen = pr->sockssize = 0;
|
pf->sockslen = pf->sockssize = 0;
|
||||||
pr->buffer = NULL;
|
pf->buffer = NULL;
|
||||||
return pr;
|
return pf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_portfwd_private(struct PFwdPrivate *pr)
|
static void free_portfwd_state(struct PortForwarding *pf)
|
||||||
{
|
{
|
||||||
if (!pr)
|
if (!pf)
|
||||||
return;
|
return;
|
||||||
sfree(pr->hostname);
|
sfree(pf->hostname);
|
||||||
sfree(pr->socksbuf);
|
sfree(pf->socksbuf);
|
||||||
sfree(pr->buffer);
|
sfree(pf->buffer);
|
||||||
sfree(pr);
|
sfree(pf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct PortListener *new_portlistener_state(void)
|
||||||
|
{
|
||||||
|
struct PortListener *pl = snew(struct PortListener);
|
||||||
|
pl->hostname = NULL;
|
||||||
|
return pl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_portlistener_state(struct PortListener *pl)
|
||||||
|
{
|
||||||
|
if (!pl)
|
||||||
|
return;
|
||||||
|
sfree(pl->hostname);
|
||||||
|
sfree(pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pfd_log(Plug plug, int type, SockAddr addr, int port,
|
static void pfd_log(Plug plug, int type, SockAddr addr, int port,
|
||||||
@ -78,17 +111,23 @@ static void pfd_log(Plug plug, int type, SockAddr addr, int port,
|
|||||||
/* we have to dump these since we have no interface to logging.c */
|
/* we have to dump these since we have no interface to logging.c */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pfl_log(Plug plug, int type, SockAddr addr, int port,
|
||||||
|
const char *error_msg, int error_code)
|
||||||
|
{
|
||||||
|
/* we have to dump these since we have no interface to logging.c */
|
||||||
|
}
|
||||||
|
|
||||||
static int pfd_closing(Plug plug, const char *error_msg, int error_code,
|
static int pfd_closing(Plug plug, const char *error_msg, int error_code,
|
||||||
int calling_back)
|
int calling_back)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
|
struct PortForwarding *pf = (struct PortForwarding *) plug;
|
||||||
|
|
||||||
if (error_msg) {
|
if (error_msg) {
|
||||||
/*
|
/*
|
||||||
* Socket error. Slam the connection instantly shut.
|
* Socket error. Slam the connection instantly shut.
|
||||||
*/
|
*/
|
||||||
if (pr->c) {
|
if (pf->c) {
|
||||||
sshfwd_unclean_close(pr->c, error_msg);
|
sshfwd_unclean_close(pf->c, error_msg);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We might not have an SSH channel, if a socket error
|
* We might not have an SSH channel, if a socket error
|
||||||
@ -96,64 +135,72 @@ static int pfd_closing(Plug plug, const char *error_msg, int error_code,
|
|||||||
* clean ourself up without sshfwd_unclean_close's call
|
* clean ourself up without sshfwd_unclean_close's call
|
||||||
* back to pfd_close.
|
* back to pfd_close.
|
||||||
*/
|
*/
|
||||||
pfd_close(pr->s);
|
pfd_close(pf);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Ordinary EOF received on socket. Send an EOF on the SSH
|
* Ordinary EOF received on socket. Send an EOF on the SSH
|
||||||
* channel.
|
* channel.
|
||||||
*/
|
*/
|
||||||
if (pr->c)
|
if (pf->c)
|
||||||
sshfwd_write_eof(pr->c);
|
sshfwd_write_eof(pf->c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pfl_closing(Plug plug, const char *error_msg, int error_code,
|
||||||
|
int calling_back)
|
||||||
|
{
|
||||||
|
struct PortListener *pl = (struct PortListener *) plug;
|
||||||
|
pfl_terminate(pl);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
|
struct PortForwarding *pf = (struct PortForwarding *) plug;
|
||||||
if (pr->dynamic) {
|
if (pf->dynamic) {
|
||||||
while (len--) {
|
while (len--) {
|
||||||
if (pr->sockslen >= pr->sockssize) {
|
if (pf->sockslen >= pf->sockssize) {
|
||||||
pr->sockssize = pr->sockslen * 5 / 4 + 256;
|
pf->sockssize = pf->sockslen * 5 / 4 + 256;
|
||||||
pr->socksbuf = sresize(pr->socksbuf, pr->sockssize, char);
|
pf->socksbuf = sresize(pf->socksbuf, pf->sockssize, char);
|
||||||
}
|
}
|
||||||
pr->socksbuf[pr->sockslen++] = *data++;
|
pf->socksbuf[pf->sockslen++] = *data++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now check what's in the buffer to see if it's a
|
* Now check what's in the buffer to see if it's a
|
||||||
* valid and complete message in the SOCKS exchange.
|
* valid and complete message in the SOCKS exchange.
|
||||||
*/
|
*/
|
||||||
if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) &&
|
if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 4) &&
|
||||||
pr->socksbuf[0] == 4) {
|
pf->socksbuf[0] == 4) {
|
||||||
/*
|
/*
|
||||||
* SOCKS 4.
|
* SOCKS 4.
|
||||||
*/
|
*/
|
||||||
if (pr->dynamic == 1)
|
if (pf->dynamic == 1)
|
||||||
pr->dynamic = 0x4000;
|
pf->dynamic = 0x4000;
|
||||||
if (pr->sockslen < 2)
|
if (pf->sockslen < 2)
|
||||||
continue; /* don't have command code yet */
|
continue; /* don't have command code yet */
|
||||||
if (pr->socksbuf[1] != 1) {
|
if (pf->socksbuf[1] != 1) {
|
||||||
/* Not CONNECT. */
|
/* Not CONNECT. */
|
||||||
/* Send back a SOCKS 4 error before closing. */
|
/* Send back a SOCKS 4 error before closing. */
|
||||||
char data[8];
|
char data[8];
|
||||||
memset(data, 0, sizeof(data));
|
memset(data, 0, sizeof(data));
|
||||||
data[1] = 91; /* generic `request rejected' */
|
data[1] = 91; /* generic `request rejected' */
|
||||||
sk_write(pr->s, data, 8);
|
sk_write(pf->s, data, 8);
|
||||||
pfd_close(pr->s);
|
pfd_close(pf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (pr->sockslen <= 8)
|
if (pf->sockslen <= 8)
|
||||||
continue; /* haven't started user/hostname */
|
continue; /* haven't started user/hostname */
|
||||||
if (pr->socksbuf[pr->sockslen-1] != 0)
|
if (pf->socksbuf[pf->sockslen-1] != 0)
|
||||||
continue; /* haven't _finished_ user/hostname */
|
continue; /* haven't _finished_ user/hostname */
|
||||||
/*
|
/*
|
||||||
* Now we have a full SOCKS 4 request. Check it to
|
* Now we have a full SOCKS 4 request. Check it to
|
||||||
* see if it's a SOCKS 4A request.
|
* see if it's a SOCKS 4A request.
|
||||||
*/
|
*/
|
||||||
if (pr->socksbuf[4] == 0 && pr->socksbuf[5] == 0 &&
|
if (pf->socksbuf[4] == 0 && pf->socksbuf[5] == 0 &&
|
||||||
pr->socksbuf[6] == 0 && pr->socksbuf[7] != 0) {
|
pf->socksbuf[6] == 0 && pf->socksbuf[7] != 0) {
|
||||||
/*
|
/*
|
||||||
* It's SOCKS 4A. So if we haven't yet
|
* It's SOCKS 4A. So if we haven't yet
|
||||||
* collected the host name, we should continue
|
* collected the host name, we should continue
|
||||||
@ -161,19 +208,19 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
* have, we can go ahead.
|
* have, we can go ahead.
|
||||||
*/
|
*/
|
||||||
int len;
|
int len;
|
||||||
if (pr->dynamic == 0x4000) {
|
if (pf->dynamic == 0x4000) {
|
||||||
pr->dynamic = 0x4001;
|
pf->dynamic = 0x4001;
|
||||||
pr->sockslen = 8; /* reset buffer to overwrite name */
|
pf->sockslen = 8; /* reset buffer to overwrite name */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pr->socksbuf[0] = 0; /* reply version code */
|
pf->socksbuf[0] = 0; /* reply version code */
|
||||||
pr->socksbuf[1] = 90; /* request granted */
|
pf->socksbuf[1] = 90; /* request granted */
|
||||||
sk_write(pr->s, pr->socksbuf, 8);
|
sk_write(pf->s, pf->socksbuf, 8);
|
||||||
len = pr->sockslen - 8;
|
len = pf->sockslen - 8;
|
||||||
pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2);
|
pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);
|
||||||
pr->hostname = snewn(len+1, char);
|
pf->hostname = snewn(len+1, char);
|
||||||
pr->hostname[len] = '\0';
|
pf->hostname[len] = '\0';
|
||||||
memcpy(pr->hostname, pr->socksbuf + 8, len);
|
memcpy(pf->hostname, pf->socksbuf + 8, len);
|
||||||
goto connect;
|
goto connect;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@ -181,52 +228,52 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
* the IP address into the hostname string and
|
* the IP address into the hostname string and
|
||||||
* then just go.
|
* then just go.
|
||||||
*/
|
*/
|
||||||
pr->socksbuf[0] = 0; /* reply version code */
|
pf->socksbuf[0] = 0; /* reply version code */
|
||||||
pr->socksbuf[1] = 90; /* request granted */
|
pf->socksbuf[1] = 90; /* request granted */
|
||||||
sk_write(pr->s, pr->socksbuf, 8);
|
sk_write(pf->s, pf->socksbuf, 8);
|
||||||
pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+2);
|
pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);
|
||||||
pr->hostname = dupprintf("%d.%d.%d.%d",
|
pf->hostname = dupprintf("%d.%d.%d.%d",
|
||||||
(unsigned char)pr->socksbuf[4],
|
(unsigned char)pf->socksbuf[4],
|
||||||
(unsigned char)pr->socksbuf[5],
|
(unsigned char)pf->socksbuf[5],
|
||||||
(unsigned char)pr->socksbuf[6],
|
(unsigned char)pf->socksbuf[6],
|
||||||
(unsigned char)pr->socksbuf[7]);
|
(unsigned char)pf->socksbuf[7]);
|
||||||
goto connect;
|
goto connect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) &&
|
if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 5) &&
|
||||||
pr->socksbuf[0] == 5) {
|
pf->socksbuf[0] == 5) {
|
||||||
/*
|
/*
|
||||||
* SOCKS 5.
|
* SOCKS 5.
|
||||||
*/
|
*/
|
||||||
if (pr->dynamic == 1)
|
if (pf->dynamic == 1)
|
||||||
pr->dynamic = 0x5000;
|
pf->dynamic = 0x5000;
|
||||||
|
|
||||||
if (pr->dynamic == 0x5000) {
|
if (pf->dynamic == 0x5000) {
|
||||||
int i, method;
|
int i, method;
|
||||||
char data[2];
|
char data[2];
|
||||||
/*
|
/*
|
||||||
* We're receiving a set of method identifiers.
|
* We're receiving a set of method identifiers.
|
||||||
*/
|
*/
|
||||||
if (pr->sockslen < 2)
|
if (pf->sockslen < 2)
|
||||||
continue; /* no method count yet */
|
continue; /* no method count yet */
|
||||||
if (pr->sockslen < 2 + (unsigned char)pr->socksbuf[1])
|
if (pf->sockslen < 2 + (unsigned char)pf->socksbuf[1])
|
||||||
continue; /* no methods yet */
|
continue; /* no methods yet */
|
||||||
method = 0xFF; /* invalid */
|
method = 0xFF; /* invalid */
|
||||||
for (i = 0; i < (unsigned char)pr->socksbuf[1]; i++)
|
for (i = 0; i < (unsigned char)pf->socksbuf[1]; i++)
|
||||||
if (pr->socksbuf[2+i] == 0) {
|
if (pf->socksbuf[2+i] == 0) {
|
||||||
method = 0;/* no auth */
|
method = 0;/* no auth */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
data[0] = 5;
|
data[0] = 5;
|
||||||
data[1] = method;
|
data[1] = method;
|
||||||
sk_write(pr->s, data, 2);
|
sk_write(pf->s, data, 2);
|
||||||
pr->dynamic = 0x5001;
|
pf->dynamic = 0x5001;
|
||||||
pr->sockslen = 0; /* re-empty the buffer */
|
pf->sockslen = 0; /* re-empty the buffer */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pr->dynamic == 0x5001) {
|
if (pf->dynamic == 0x5001) {
|
||||||
/*
|
/*
|
||||||
* We're receiving a SOCKS request.
|
* We're receiving a SOCKS request.
|
||||||
*/
|
*/
|
||||||
@ -244,50 +291,50 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
reply[0] = 5; /* VER */
|
reply[0] = 5; /* VER */
|
||||||
reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */
|
reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */
|
||||||
|
|
||||||
if (pr->sockslen < 6) continue;
|
if (pf->sockslen < 6) continue;
|
||||||
atype = (unsigned char)pr->socksbuf[3];
|
atype = (unsigned char)pf->socksbuf[3];
|
||||||
if (atype == 1) /* IPv4 address */
|
if (atype == 1) /* IPv4 address */
|
||||||
alen = 4;
|
alen = 4;
|
||||||
if (atype == 4) /* IPv6 address */
|
if (atype == 4) /* IPv6 address */
|
||||||
alen = 16;
|
alen = 16;
|
||||||
if (atype == 3) /* domain name has leading length */
|
if (atype == 3) /* domain name has leading length */
|
||||||
alen = 1 + (unsigned char)pr->socksbuf[4];
|
alen = 1 + (unsigned char)pf->socksbuf[4];
|
||||||
if (pr->sockslen < 6 + alen) continue;
|
if (pf->sockslen < 6 + alen) continue;
|
||||||
if (pr->socksbuf[1] != 1 || pr->socksbuf[2] != 0) {
|
if (pf->socksbuf[1] != 1 || pf->socksbuf[2] != 0) {
|
||||||
/* Not CONNECT or reserved field nonzero - error */
|
/* Not CONNECT or reserved field nonzero - error */
|
||||||
reply[1] = 1; /* generic failure */
|
reply[1] = 1; /* generic failure */
|
||||||
sk_write(pr->s, (char *) reply, lenof(reply));
|
sk_write(pf->s, (char *) reply, lenof(reply));
|
||||||
pfd_close(pr->s);
|
pfd_close(pf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Now we have a viable connect request. Switch
|
* Now we have a viable connect request. Switch
|
||||||
* on atype.
|
* on atype.
|
||||||
*/
|
*/
|
||||||
pr->port = GET_16BIT_MSB_FIRST(pr->socksbuf+4+alen);
|
pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+4+alen);
|
||||||
if (atype == 1) {
|
if (atype == 1) {
|
||||||
/* REP=0 (success) already */
|
/* REP=0 (success) already */
|
||||||
sk_write(pr->s, (char *) reply, lenof(reply));
|
sk_write(pf->s, (char *) reply, lenof(reply));
|
||||||
pr->hostname = dupprintf("%d.%d.%d.%d",
|
pf->hostname = dupprintf("%d.%d.%d.%d",
|
||||||
(unsigned char)pr->socksbuf[4],
|
(unsigned char)pf->socksbuf[4],
|
||||||
(unsigned char)pr->socksbuf[5],
|
(unsigned char)pf->socksbuf[5],
|
||||||
(unsigned char)pr->socksbuf[6],
|
(unsigned char)pf->socksbuf[6],
|
||||||
(unsigned char)pr->socksbuf[7]);
|
(unsigned char)pf->socksbuf[7]);
|
||||||
goto connect;
|
goto connect;
|
||||||
} else if (atype == 3) {
|
} else if (atype == 3) {
|
||||||
/* REP=0 (success) already */
|
/* REP=0 (success) already */
|
||||||
sk_write(pr->s, (char *) reply, lenof(reply));
|
sk_write(pf->s, (char *) reply, lenof(reply));
|
||||||
pr->hostname = snewn(alen, char);
|
pf->hostname = snewn(alen, char);
|
||||||
pr->hostname[alen-1] = '\0';
|
pf->hostname[alen-1] = '\0';
|
||||||
memcpy(pr->hostname, pr->socksbuf + 5, alen-1);
|
memcpy(pf->hostname, pf->socksbuf + 5, alen-1);
|
||||||
goto connect;
|
goto connect;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Unknown address type. (FIXME: support IPv6!)
|
* Unknown address type. (FIXME: support IPv6!)
|
||||||
*/
|
*/
|
||||||
reply[1] = 8; /* atype not supported */
|
reply[1] = 8; /* atype not supported */
|
||||||
sk_write(pr->s, (char *) reply, lenof(reply));
|
sk_write(pf->s, (char *) reply, lenof(reply));
|
||||||
pfd_close(pr->s);
|
pfd_close(pf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,7 +346,7 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
* sensible interpretation of what's in our buffer. So
|
* sensible interpretation of what's in our buffer. So
|
||||||
* close the connection rudely.
|
* close the connection rudely.
|
||||||
*/
|
*/
|
||||||
pfd_close(pr->s);
|
pfd_close(pf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -309,39 +356,39 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
* connection.
|
* connection.
|
||||||
*/
|
*/
|
||||||
connect:
|
connect:
|
||||||
sfree(pr->socksbuf);
|
sfree(pf->socksbuf);
|
||||||
pr->socksbuf = NULL;
|
pf->socksbuf = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Freeze the socket until the SSH server confirms the
|
* Freeze the socket until the SSH server confirms the
|
||||||
* connection.
|
* connection.
|
||||||
*/
|
*/
|
||||||
sk_set_frozen(pr->s, 1);
|
sk_set_frozen(pf->s, 1);
|
||||||
|
|
||||||
pr->c = new_sock_channel(pr->backhandle, pr->s);
|
pf->c = new_sock_channel(pf->backhandle, pf);
|
||||||
if (pr->c == NULL) {
|
if (pf->c == NULL) {
|
||||||
pfd_close(pr->s);
|
pfd_close(pf);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
/* asks to forward to the specified host/port for this */
|
/* asks to forward to the specified host/port for this */
|
||||||
ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");
|
ssh_send_port_open(pf->c, pf->hostname, pf->port, "forwarding");
|
||||||
}
|
}
|
||||||
pr->dynamic = 0;
|
pf->dynamic = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there's any data remaining in our current buffer,
|
* If there's any data remaining in our current buffer,
|
||||||
* save it to be sent on pfd_confirm().
|
* save it to be sent on pfd_confirm().
|
||||||
*/
|
*/
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
pr->buffer = snewn(len, char);
|
pf->buffer = snewn(len, char);
|
||||||
memcpy(pr->buffer, data, len);
|
memcpy(pf->buffer, data, len);
|
||||||
pr->buflen = len;
|
pf->buflen = len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pr->ready) {
|
if (pf->ready) {
|
||||||
if (sshfwd_write(pr->c, data, len) > 0) {
|
if (sshfwd_write(pf->c, data, len) > 0) {
|
||||||
pr->throttled = 1;
|
pf->throttled = 1;
|
||||||
sk_set_frozen(pr->s, 1);
|
sk_set_frozen(pf->s, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -349,17 +396,21 @@ static int pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
|
|
||||||
static void pfd_sent(Plug plug, int bufsize)
|
static void pfd_sent(Plug plug, int bufsize)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
|
struct PortForwarding *pf = (struct PortForwarding *) plug;
|
||||||
|
|
||||||
if (pr->c)
|
if (pf->c)
|
||||||
sshfwd_unthrottle(pr->c, bufsize);
|
sshfwd_unthrottle(pf->c, bufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called when receiving a PORT OPEN from the server
|
* Called when receiving a PORT OPEN from the server to make a
|
||||||
|
* connection to a destination host.
|
||||||
|
*
|
||||||
|
* On success, returns NULL and fills in *pf_ret. On error, returns a
|
||||||
|
* dynamically allocated error message string.
|
||||||
*/
|
*/
|
||||||
const char *pfd_newconnect(Socket *s, char *hostname, int port,
|
char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
|
||||||
void *c, Conf *conf, int addressfamily)
|
void *c, Conf *conf, int addressfamily)
|
||||||
{
|
{
|
||||||
static const struct plug_function_table fn_table = {
|
static const struct plug_function_table fn_table = {
|
||||||
pfd_log,
|
pfd_log,
|
||||||
@ -372,38 +423,41 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
|
|||||||
SockAddr addr;
|
SockAddr addr;
|
||||||
const char *err;
|
const char *err;
|
||||||
char *dummy_realhost;
|
char *dummy_realhost;
|
||||||
struct PFwdPrivate *pr;
|
struct PortForwarding *pf;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to find host.
|
* Try to find host.
|
||||||
*/
|
*/
|
||||||
addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);
|
addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);
|
||||||
if ((err = sk_addr_error(addr)) != NULL) {
|
if ((err = sk_addr_error(addr)) != NULL) {
|
||||||
|
char *err_ret = dupstr(err);
|
||||||
sk_addr_free(addr);
|
sk_addr_free(addr);
|
||||||
sfree(dummy_realhost);
|
sfree(dummy_realhost);
|
||||||
return err;
|
return err_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
pr = new_portfwd_private();
|
pf = *pf_ret = new_portfwd_state();
|
||||||
pr->fn = &fn_table;
|
pf->fn = &fn_table;
|
||||||
pr->throttled = pr->throttle_override = 0;
|
pf->throttled = pf->throttle_override = 0;
|
||||||
pr->ready = 1;
|
pf->ready = 1;
|
||||||
pr->c = c;
|
pf->c = c;
|
||||||
pr->backhandle = NULL; /* we shouldn't need this */
|
pf->backhandle = NULL; /* we shouldn't need this */
|
||||||
pr->dynamic = 0;
|
pf->dynamic = 0;
|
||||||
|
|
||||||
pr->s = *s = new_connection(addr, dummy_realhost, port,
|
pf->s = new_connection(addr, dummy_realhost, port,
|
||||||
0, 1, 0, 0, (Plug) pr, conf);
|
0, 1, 0, 0, (Plug) pf, conf);
|
||||||
sfree(dummy_realhost);
|
sfree(dummy_realhost);
|
||||||
if ((err = sk_socket_error(*s)) != NULL) {
|
if ((err = sk_socket_error(pf->s)) != NULL) {
|
||||||
free_portfwd_private(pr);
|
char *err_ret = dupstr(err);
|
||||||
return err;
|
sk_close(pf->s);
|
||||||
|
free_portfwd_state(pf);
|
||||||
|
*pf_ret = NULL;
|
||||||
|
return err_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_set_private_ptr(*s, pr);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +465,7 @@ const char *pfd_newconnect(Socket *s, char *hostname, int port,
|
|||||||
called when someone connects to the local port
|
called when someone connects to the local port
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int pfd_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
||||||
{
|
{
|
||||||
static const struct plug_function_table fn_table = {
|
static const struct plug_function_table fn_table = {
|
||||||
pfd_log,
|
pfd_log,
|
||||||
@ -420,44 +474,43 @@ static int pfd_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
|||||||
pfd_sent,
|
pfd_sent,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
struct PFwdPrivate *pr, *org;
|
struct PortForwarding *pf;
|
||||||
|
struct PortListener *pl;
|
||||||
Socket s;
|
Socket s;
|
||||||
const char *err;
|
const char *err;
|
||||||
|
|
||||||
org = (struct PFwdPrivate *)p;
|
pl = (struct PortListener *)p;
|
||||||
pr = new_portfwd_private();
|
pf = new_portfwd_state();
|
||||||
pr->fn = &fn_table;
|
pf->fn = &fn_table;
|
||||||
|
|
||||||
pr->c = NULL;
|
pf->c = NULL;
|
||||||
pr->backhandle = org->backhandle;
|
pf->backhandle = pl->backhandle;
|
||||||
|
|
||||||
pr->s = s = constructor(ctx, (Plug) pr);
|
pf->s = s = constructor(ctx, (Plug) pf);
|
||||||
if ((err = sk_socket_error(s)) != NULL) {
|
if ((err = sk_socket_error(s)) != NULL) {
|
||||||
free_portfwd_private(pr);
|
free_portfwd_state(pf);
|
||||||
return err != NULL;
|
return err != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_set_private_ptr(s, pr);
|
pf->throttled = pf->throttle_override = 0;
|
||||||
|
pf->ready = 0;
|
||||||
|
|
||||||
pr->throttled = pr->throttle_override = 0;
|
if (pl->dynamic) {
|
||||||
pr->ready = 0;
|
pf->dynamic = 1;
|
||||||
|
pf->port = 0; /* "hostname" buffer is so far empty */
|
||||||
if (org->dynamic) {
|
|
||||||
pr->dynamic = 1;
|
|
||||||
pr->port = 0; /* "hostname" buffer is so far empty */
|
|
||||||
sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */
|
sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */
|
||||||
} else {
|
} else {
|
||||||
pr->dynamic = 0;
|
pf->dynamic = 0;
|
||||||
pr->hostname = dupstr(org->hostname);
|
pf->hostname = dupstr(pl->hostname);
|
||||||
pr->port = org->port;
|
pf->port = pl->port;
|
||||||
pr->c = new_sock_channel(org->backhandle, s);
|
pf->c = new_sock_channel(pl->backhandle, pf);
|
||||||
|
|
||||||
if (pr->c == NULL) {
|
if (pf->c == NULL) {
|
||||||
free_portfwd_private(pr);
|
free_portfwd_state(pf);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
/* asks to forward to the specified host/port for this */
|
/* asks to forward to the specified host/port for this */
|
||||||
ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");
|
ssh_send_port_open(pf->c, pf->hostname, pf->port, "forwarding");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,129 +518,119 @@ static int pfd_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Add a new forwarding from port -> desthost:destport
|
/*
|
||||||
sets up a listener on the local machine on (srcaddr:)port
|
* Add a new port-forwarding listener from srcaddr:port -> desthost:destport.
|
||||||
|
*
|
||||||
|
* On success, returns NULL and fills in *pl_ret. On error, returns a
|
||||||
|
* dynamically allocated error message string.
|
||||||
*/
|
*/
|
||||||
const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
|
char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
||||||
int port, void *backhandle, Conf *conf,
|
int port, void *backhandle, Conf *conf,
|
||||||
void **sockdata, int address_family)
|
struct PortListener **pl_ret, int address_family)
|
||||||
{
|
{
|
||||||
static const struct plug_function_table fn_table = {
|
static const struct plug_function_table fn_table = {
|
||||||
pfd_log,
|
pfl_log,
|
||||||
pfd_closing,
|
pfl_closing,
|
||||||
pfd_receive, /* should not happen... */
|
NULL, /* recv */
|
||||||
pfd_sent, /* also should not happen */
|
NULL, /* send */
|
||||||
pfd_accepting
|
pfl_accepting
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *err;
|
const char *err;
|
||||||
struct PFwdPrivate *pr;
|
struct PortListener *pl;
|
||||||
Socket s;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
pr = new_portfwd_private();
|
pl = *pl_ret = new_portlistener_state();
|
||||||
pr->fn = &fn_table;
|
pl->fn = &fn_table;
|
||||||
pr->c = NULL;
|
|
||||||
if (desthost) {
|
if (desthost) {
|
||||||
pr->hostname = dupstr(desthost);
|
pl->hostname = dupstr(desthost);
|
||||||
pr->port = destport;
|
pl->port = destport;
|
||||||
pr->dynamic = 0;
|
pl->dynamic = 0;
|
||||||
} else
|
} else
|
||||||
pr->dynamic = 1;
|
pl->dynamic = 1;
|
||||||
pr->throttled = pr->throttle_override = 0;
|
pl->backhandle = backhandle;
|
||||||
pr->ready = 0;
|
|
||||||
pr->backhandle = backhandle;
|
|
||||||
|
|
||||||
pr->s = s = new_listener(srcaddr, port, (Plug) pr,
|
pl->s = new_listener(srcaddr, port, (Plug) pl,
|
||||||
!conf_get_int(conf, CONF_lport_acceptall),
|
!conf_get_int(conf, CONF_lport_acceptall),
|
||||||
conf, address_family);
|
conf, address_family);
|
||||||
if ((err = sk_socket_error(s)) != NULL) {
|
if ((err = sk_socket_error(pl->s)) != NULL) {
|
||||||
free_portfwd_private(pr);
|
char *err_ret = dupstr(err);
|
||||||
return err;
|
sk_close(pl->s);
|
||||||
|
free_portlistener_state(pl);
|
||||||
|
*pl_ret = NULL;
|
||||||
|
return err_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_set_private_ptr(s, pr);
|
|
||||||
|
|
||||||
*sockdata = (void *)s;
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pfd_close(Socket s)
|
void pfd_close(struct PortForwarding *pf)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr;
|
if (!pf)
|
||||||
|
|
||||||
if (!s)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
|
sk_close(pf->s);
|
||||||
|
free_portfwd_state(pf);
|
||||||
free_portfwd_private(pr);
|
|
||||||
|
|
||||||
sk_close(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Terminate a listener.
|
* Terminate a listener.
|
||||||
*/
|
*/
|
||||||
void pfd_terminate(void *sv)
|
void pfl_terminate(struct PortListener *pl)
|
||||||
{
|
{
|
||||||
pfd_close((Socket)sv);
|
if (!pl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sk_close(pl->s);
|
||||||
|
free_portlistener_state(pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pfd_unthrottle(Socket s)
|
void pfd_unthrottle(struct PortForwarding *pf)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr;
|
if (!pf)
|
||||||
if (!s)
|
|
||||||
return;
|
return;
|
||||||
pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
|
|
||||||
|
|
||||||
pr->throttled = 0;
|
pf->throttled = 0;
|
||||||
sk_set_frozen(s, pr->throttled || pr->throttle_override);
|
sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pfd_override_throttle(Socket s, int enable)
|
void pfd_override_throttle(struct PortForwarding *pf, int enable)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr;
|
if (!pf)
|
||||||
if (!s)
|
|
||||||
return;
|
return;
|
||||||
pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
|
|
||||||
|
|
||||||
pr->throttle_override = enable;
|
pf->throttle_override = enable;
|
||||||
sk_set_frozen(s, pr->throttled || pr->throttle_override);
|
sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called to send data down the raw connection.
|
* Called to send data down the raw connection.
|
||||||
*/
|
*/
|
||||||
int pfd_send(Socket s, char *data, int len)
|
int pfd_send(struct PortForwarding *pf, char *data, int len)
|
||||||
{
|
{
|
||||||
if (s == NULL)
|
if (pf == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
return sk_write(s, data, len);
|
return sk_write(pf->s, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pfd_send_eof(Socket s)
|
void pfd_send_eof(struct PortForwarding *pf)
|
||||||
{
|
{
|
||||||
sk_write_eof(s);
|
sk_write_eof(pf->s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pfd_confirm(Socket s)
|
void pfd_confirm(struct PortForwarding *pf)
|
||||||
{
|
{
|
||||||
struct PFwdPrivate *pr;
|
if (pf == NULL)
|
||||||
|
|
||||||
if (s == NULL)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
|
pf->ready = 1;
|
||||||
pr->ready = 1;
|
sk_set_frozen(pf->s, 0);
|
||||||
sk_set_frozen(s, 0);
|
sk_write(pf->s, NULL, 0);
|
||||||
sk_write(s, NULL, 0);
|
if (pf->buffer) {
|
||||||
if (pr->buffer) {
|
sshfwd_write(pf->c, pf->buffer, pf->buflen);
|
||||||
sshfwd_write(pr->c, pr->buffer, pr->buflen);
|
sfree(pf->buffer);
|
||||||
sfree(pr->buffer);
|
pf->buffer = NULL;
|
||||||
pr->buffer = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
142
ssh.c
142
ssh.c
@ -544,10 +544,10 @@ struct ssh_channel {
|
|||||||
int outstanding_requests;
|
int outstanding_requests;
|
||||||
} a;
|
} a;
|
||||||
struct ssh_x11_channel {
|
struct ssh_x11_channel {
|
||||||
Socket s;
|
struct X11Connection *xconn;
|
||||||
} x11;
|
} x11;
|
||||||
struct ssh_pfd_channel {
|
struct ssh_pfd_channel {
|
||||||
Socket s;
|
struct PortForwarding *pf;
|
||||||
} pfd;
|
} pfd;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
@ -613,7 +613,7 @@ struct ssh_portfwd {
|
|||||||
char *sserv, *dserv;
|
char *sserv, *dserv;
|
||||||
struct ssh_rportfwd *remote;
|
struct ssh_rportfwd *remote;
|
||||||
int addressfamily;
|
int addressfamily;
|
||||||
void *local;
|
struct PortListener *local;
|
||||||
};
|
};
|
||||||
#define free_portfwd(pf) ( \
|
#define free_portfwd(pf) ( \
|
||||||
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
|
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
|
||||||
@ -3005,11 +3005,11 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
|
|||||||
while (NULL != (c = index234(ssh->channels, 0))) {
|
while (NULL != (c = index234(ssh->channels, 0))) {
|
||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
x11_close(c->u.x11.s);
|
x11_close(c->u.x11.xconn);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
case CHAN_SOCKDATA_DORMANT:
|
case CHAN_SOCKDATA_DORMANT:
|
||||||
pfd_close(c->u.pfd.s);
|
pfd_close(c->u.pfd.pf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
del234(ssh->channels, c); /* moving next one to index 0 */
|
del234(ssh->channels, c); /* moving next one to index 0 */
|
||||||
@ -3027,7 +3027,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
|
|||||||
while (NULL != (pf = index234(ssh->portfwds, 0))) {
|
while (NULL != (pf = index234(ssh->portfwds, 0))) {
|
||||||
/* Dispose of any listening socket. */
|
/* Dispose of any listening socket. */
|
||||||
if (pf->local)
|
if (pf->local)
|
||||||
pfd_terminate(pf->local);
|
pfl_terminate(pf->local);
|
||||||
del234(ssh->portfwds, pf); /* moving next one to index 0 */
|
del234(ssh->portfwds, pf); /* moving next one to index 0 */
|
||||||
free_portfwd(pf);
|
free_portfwd(pf);
|
||||||
}
|
}
|
||||||
@ -3238,13 +3238,13 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)
|
|||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
x11_override_throttle(c->u.x11.s, enable);
|
x11_override_throttle(c->u.x11.xconn, enable);
|
||||||
break;
|
break;
|
||||||
case CHAN_AGENT:
|
case CHAN_AGENT:
|
||||||
/* Agent channels require no buffer management. */
|
/* Agent channels require no buffer management. */
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
pfd_override_throttle(c->u.pfd.s, enable);
|
pfd_override_throttle(c->u.pfd.pf, enable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4376,13 +4376,13 @@ void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
|
|||||||
|
|
||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
x11_close(c->u.x11.s);
|
x11_close(c->u.x11.xconn);
|
||||||
logeventf(ssh, "Forwarded X11 connection terminated due to local "
|
logeventf(ssh, "Forwarded X11 connection terminated due to local "
|
||||||
"error: %s", err);
|
"error: %s", err);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
case CHAN_SOCKDATA_DORMANT:
|
case CHAN_SOCKDATA_DORMANT:
|
||||||
pfd_close(c->u.pfd.s);
|
pfd_close(c->u.pfd.pf);
|
||||||
logeventf(ssh, "Forwarded port closed due to local error: %s", err);
|
logeventf(ssh, "Forwarded port closed due to local error: %s", err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4720,7 +4720,7 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
|
|||||||
del234(ssh->rportfwds, rpf);
|
del234(ssh->rportfwds, rpf);
|
||||||
free_rportfwd(rpf);
|
free_rportfwd(rpf);
|
||||||
} else if (epf->local) {
|
} else if (epf->local) {
|
||||||
pfd_terminate(epf->local);
|
pfl_terminate(epf->local);
|
||||||
}
|
}
|
||||||
|
|
||||||
delpos234(ssh->portfwds, i);
|
delpos234(ssh->portfwds, i);
|
||||||
@ -4753,29 +4753,31 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (epf->type == 'L') {
|
if (epf->type == 'L') {
|
||||||
const char *err = pfd_addforward(epf->daddr, epf->dport,
|
char *err = pfl_listen(epf->daddr, epf->dport,
|
||||||
epf->saddr, epf->sport,
|
epf->saddr, epf->sport,
|
||||||
ssh, conf,
|
ssh, conf, &epf->local,
|
||||||
&epf->local,
|
epf->addressfamily);
|
||||||
epf->addressfamily);
|
|
||||||
|
|
||||||
logeventf(ssh, "Local %sport %s forwarding to %s%s%s",
|
logeventf(ssh, "Local %sport %s forwarding to %s%s%s",
|
||||||
epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
||||||
epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
||||||
sportdesc, dportdesc,
|
sportdesc, dportdesc,
|
||||||
err ? " failed: " : "", err ? err : "");
|
err ? " failed: " : "", err ? err : "");
|
||||||
|
if (err)
|
||||||
|
sfree(err);
|
||||||
} else if (epf->type == 'D') {
|
} else if (epf->type == 'D') {
|
||||||
const char *err = pfd_addforward(NULL, -1,
|
char *err = pfl_listen(NULL, -1, epf->saddr, epf->sport,
|
||||||
epf->saddr, epf->sport,
|
ssh, conf, &epf->local,
|
||||||
ssh, conf,
|
epf->addressfamily);
|
||||||
&epf->local,
|
|
||||||
epf->addressfamily);
|
|
||||||
|
|
||||||
logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s",
|
logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s",
|
||||||
epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
||||||
epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
||||||
sportdesc,
|
sportdesc,
|
||||||
err ? " failed: " : "", err ? err : "");
|
err ? " failed: " : "", err ? err : "");
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
sfree(err);
|
||||||
} else {
|
} else {
|
||||||
struct ssh_rportfwd *pf;
|
struct ssh_rportfwd *pf;
|
||||||
|
|
||||||
@ -4875,12 +4877,15 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
|
|||||||
PKT_INT, remoteid, PKT_END);
|
PKT_INT, remoteid, PKT_END);
|
||||||
logevent("Rejected X11 connect request");
|
logevent("Rejected X11 connect request");
|
||||||
} else {
|
} else {
|
||||||
|
char *err;
|
||||||
|
|
||||||
c = snew(struct ssh_channel);
|
c = snew(struct ssh_channel);
|
||||||
c->ssh = ssh;
|
c->ssh = ssh;
|
||||||
|
|
||||||
if (x11_init(&c->u.x11.s, ssh->x11disp, c,
|
if ((err = x11_init(&c->u.x11.xconn, ssh->x11disp, c,
|
||||||
NULL, -1, ssh->conf) != NULL) {
|
NULL, -1, ssh->conf)) != NULL) {
|
||||||
logevent("Opening X11 forward connection failed");
|
logeventf(ssh, "Opening X11 forward connection failed: %s", err);
|
||||||
|
sfree(err);
|
||||||
sfree(c);
|
sfree(c);
|
||||||
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
|
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
|
||||||
PKT_INT, remoteid, PKT_END);
|
PKT_INT, remoteid, PKT_END);
|
||||||
@ -4942,7 +4947,7 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
|
|||||||
int remoteid;
|
int remoteid;
|
||||||
int hostsize, port;
|
int hostsize, port;
|
||||||
char *host;
|
char *host;
|
||||||
const char *e;
|
char *err;
|
||||||
|
|
||||||
remoteid = ssh_pkt_getuint32(pktin);
|
remoteid = ssh_pkt_getuint32(pktin);
|
||||||
ssh_pkt_getstring(pktin, &host, &hostsize);
|
ssh_pkt_getstring(pktin, &host, &hostsize);
|
||||||
@ -4963,10 +4968,11 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
|
|||||||
|
|
||||||
logeventf(ssh, "Received remote port open request for %s:%d",
|
logeventf(ssh, "Received remote port open request for %s:%d",
|
||||||
pf.dhost, port);
|
pf.dhost, port);
|
||||||
e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
|
err = pfd_connect(&c->u.pfd.pf, pf.dhost, port,
|
||||||
c, ssh->conf, pfp->pfrec->addressfamily);
|
c, ssh->conf, pfp->pfrec->addressfamily);
|
||||||
if (e != NULL) {
|
if (err != NULL) {
|
||||||
logeventf(ssh, "Port open failed: %s", e);
|
logeventf(ssh, "Port open failed: %s", err);
|
||||||
|
sfree(err);
|
||||||
sfree(c);
|
sfree(c);
|
||||||
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
|
send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
|
||||||
PKT_INT, remoteid, PKT_END);
|
PKT_INT, remoteid, PKT_END);
|
||||||
@ -5001,7 +5007,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
|
|||||||
c->halfopen = FALSE;
|
c->halfopen = FALSE;
|
||||||
c->type = CHAN_SOCKDATA;
|
c->type = CHAN_SOCKDATA;
|
||||||
c->throttling_conn = 0;
|
c->throttling_conn = 0;
|
||||||
pfd_confirm(c->u.pfd.s);
|
pfd_confirm(c->u.pfd.pf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c && c->pending_eof) {
|
if (c && c->pending_eof) {
|
||||||
@ -5023,7 +5029,7 @@ static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
|
|||||||
c = find234(ssh->channels, &remoteid, ssh_channelfind);
|
c = find234(ssh->channels, &remoteid, ssh_channelfind);
|
||||||
if (c && c->type == CHAN_SOCKDATA_DORMANT) {
|
if (c && c->type == CHAN_SOCKDATA_DORMANT) {
|
||||||
logevent("Forwarded connection refused by server");
|
logevent("Forwarded connection refused by server");
|
||||||
pfd_close(c->u.pfd.s);
|
pfd_close(c->u.pfd.pf);
|
||||||
del234(ssh->channels, c);
|
del234(ssh->channels, c);
|
||||||
sfree(c);
|
sfree(c);
|
||||||
}
|
}
|
||||||
@ -5049,14 +5055,14 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
|
|||||||
|
|
||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
if (c->u.x11.s)
|
if (c->u.x11.xconn)
|
||||||
x11_send_eof(c->u.x11.s);
|
x11_send_eof(c->u.x11.xconn);
|
||||||
else
|
else
|
||||||
send_close = TRUE;
|
send_close = TRUE;
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
if (c->u.pfd.s)
|
if (c->u.pfd.pf)
|
||||||
pfd_send_eof(c->u.pfd.s);
|
pfd_send_eof(c->u.pfd.pf);
|
||||||
else
|
else
|
||||||
send_close = TRUE;
|
send_close = TRUE;
|
||||||
break;
|
break;
|
||||||
@ -5115,10 +5121,10 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
|
|||||||
int bufsize = 0;
|
int bufsize = 0;
|
||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
bufsize = x11_send(c->u.x11.s, p, len);
|
bufsize = x11_send(c->u.x11.xconn, p, len);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
bufsize = pfd_send(c->u.pfd.s, p, len);
|
bufsize = pfd_send(c->u.pfd.pf, p, len);
|
||||||
break;
|
break;
|
||||||
case CHAN_AGENT:
|
case CHAN_AGENT:
|
||||||
/* Data for an agent message. Buffer it. */
|
/* Data for an agent message. Buffer it. */
|
||||||
@ -6660,14 +6666,14 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
|
|||||||
* notification since it will be polled */
|
* notification since it will be polled */
|
||||||
break;
|
break;
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
x11_unthrottle(c->u.x11.s);
|
x11_unthrottle(c->u.x11.xconn);
|
||||||
break;
|
break;
|
||||||
case CHAN_AGENT:
|
case CHAN_AGENT:
|
||||||
/* agent sockets are request/response and need no
|
/* agent sockets are request/response and need no
|
||||||
* buffer management */
|
* buffer management */
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
pfd_unthrottle(c->u.pfd.s);
|
pfd_unthrottle(c->u.pfd.pf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6930,10 +6936,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
|
|||||||
data, length);
|
data, length);
|
||||||
break;
|
break;
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
bufsize = x11_send(c->u.x11.s, data, length);
|
bufsize = x11_send(c->u.x11.xconn, data, length);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
bufsize = pfd_send(c->u.pfd.s, data, length);
|
bufsize = pfd_send(c->u.pfd.pf, data, length);
|
||||||
break;
|
break;
|
||||||
case CHAN_AGENT:
|
case CHAN_AGENT:
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
@ -7021,16 +7027,16 @@ static void ssh_channel_destroy(struct ssh_channel *c)
|
|||||||
update_specials_menu(ssh->frontend);
|
update_specials_menu(ssh->frontend);
|
||||||
break;
|
break;
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
if (c->u.x11.s != NULL)
|
if (c->u.x11.xconn != NULL)
|
||||||
x11_close(c->u.x11.s);
|
x11_close(c->u.x11.xconn);
|
||||||
logevent("Forwarded X11 connection terminated");
|
logevent("Forwarded X11 connection terminated");
|
||||||
break;
|
break;
|
||||||
case CHAN_AGENT:
|
case CHAN_AGENT:
|
||||||
sfree(c->u.a.message);
|
sfree(c->u.a.message);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
if (c->u.pfd.s != NULL)
|
if (c->u.pfd.pf != NULL)
|
||||||
pfd_close(c->u.pfd.s);
|
pfd_close(c->u.pfd.pf);
|
||||||
logevent("Forwarded port closed");
|
logevent("Forwarded port closed");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -7112,14 +7118,14 @@ static void ssh2_channel_got_eof(struct ssh_channel *c)
|
|||||||
c->closes |= CLOSES_RCVD_EOF;
|
c->closes |= CLOSES_RCVD_EOF;
|
||||||
|
|
||||||
if (c->type == CHAN_X11) {
|
if (c->type == CHAN_X11) {
|
||||||
x11_send_eof(c->u.x11.s);
|
x11_send_eof(c->u.x11.xconn);
|
||||||
} else if (c->type == CHAN_AGENT) {
|
} else if (c->type == CHAN_AGENT) {
|
||||||
if (c->u.a.outstanding_requests == 0) {
|
if (c->u.a.outstanding_requests == 0) {
|
||||||
/* Manufacture an outgoing EOF in response to the incoming one. */
|
/* Manufacture an outgoing EOF in response to the incoming one. */
|
||||||
sshfwd_write_eof(c);
|
sshfwd_write_eof(c);
|
||||||
}
|
}
|
||||||
} else if (c->type == CHAN_SOCKDATA) {
|
} else if (c->type == CHAN_SOCKDATA) {
|
||||||
pfd_send_eof(c->u.pfd.s);
|
pfd_send_eof(c->u.pfd.pf);
|
||||||
} else if (c->type == CHAN_MAINSESSION) {
|
} else if (c->type == CHAN_MAINSESSION) {
|
||||||
Ssh ssh = c->ssh;
|
Ssh ssh = c->ssh;
|
||||||
|
|
||||||
@ -7182,10 +7188,10 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
|
|||||||
ssh->send_ok = 0; /* stop trying to read from stdin */
|
ssh->send_ok = 0; /* stop trying to read from stdin */
|
||||||
break;
|
break;
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
x11_override_throttle(c->u.x11.s, 1);
|
x11_override_throttle(c->u.x11.xconn, 1);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
pfd_override_throttle(c->u.pfd.s, 1);
|
pfd_override_throttle(c->u.pfd.pf, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7228,8 +7234,8 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
|
|||||||
|
|
||||||
if (c->type == CHAN_SOCKDATA_DORMANT) {
|
if (c->type == CHAN_SOCKDATA_DORMANT) {
|
||||||
c->type = CHAN_SOCKDATA;
|
c->type = CHAN_SOCKDATA;
|
||||||
if (c->u.pfd.s)
|
if (c->u.pfd.pf)
|
||||||
pfd_confirm(c->u.pfd.s);
|
pfd_confirm(c->u.pfd.pf);
|
||||||
} else if (c->type == CHAN_ZOMBIE) {
|
} else if (c->type == CHAN_ZOMBIE) {
|
||||||
/*
|
/*
|
||||||
* This case can occur if a local socket error occurred
|
* This case can occur if a local socket error occurred
|
||||||
@ -7285,7 +7291,7 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
|
|||||||
logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
|
logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
|
||||||
reasons[reason_code], reason_length, reason_string);
|
reasons[reason_code], reason_length, reason_string);
|
||||||
|
|
||||||
pfd_close(c->u.pfd.s);
|
pfd_close(c->u.pfd.pf);
|
||||||
} else if (c->type == CHAN_ZOMBIE) {
|
} else if (c->type == CHAN_ZOMBIE) {
|
||||||
/*
|
/*
|
||||||
* This case can occur if a local socket error occurred
|
* This case can occur if a local socket error occurred
|
||||||
@ -7536,7 +7542,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
|
|||||||
|
|
||||||
if (typelen == 3 && !memcmp(type, "x11", 3)) {
|
if (typelen == 3 && !memcmp(type, "x11", 3)) {
|
||||||
char *addrstr;
|
char *addrstr;
|
||||||
const char *x11err;
|
char *x11err;
|
||||||
|
|
||||||
ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
|
ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
|
||||||
addrstr = snewn(peeraddrlen+1, char);
|
addrstr = snewn(peeraddrlen+1, char);
|
||||||
@ -7549,9 +7555,10 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
|
|||||||
|
|
||||||
if (!ssh->X11_fwd_enabled)
|
if (!ssh->X11_fwd_enabled)
|
||||||
error = "X11 forwarding is not enabled";
|
error = "X11 forwarding is not enabled";
|
||||||
else if ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,
|
else if ((x11err = x11_init(&c->u.x11.xconn, ssh->x11disp, c,
|
||||||
addrstr, peerport, ssh->conf)) != NULL) {
|
addrstr, peerport, ssh->conf)) != NULL) {
|
||||||
logeventf(ssh, "Local X11 connection failed: %s", x11err);
|
logeventf(ssh, "Local X11 connection failed: %s", x11err);
|
||||||
|
sfree(x11err);
|
||||||
error = "Unable to open an X11 connection";
|
error = "Unable to open an X11 connection";
|
||||||
} else {
|
} else {
|
||||||
logevent("Opening X11 forward connection succeeded");
|
logevent("Opening X11 forward connection succeeded");
|
||||||
@ -7577,15 +7584,16 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
|
|||||||
if (realpf == NULL) {
|
if (realpf == NULL) {
|
||||||
error = "Remote port is not recognised";
|
error = "Remote port is not recognised";
|
||||||
} else {
|
} else {
|
||||||
const char *e = pfd_newconnect(&c->u.pfd.s,
|
char *err = pfd_connect(&c->u.pfd.pf,
|
||||||
realpf->dhost,
|
realpf->dhost,
|
||||||
realpf->dport, c,
|
realpf->dport, c,
|
||||||
ssh->conf,
|
ssh->conf,
|
||||||
realpf->pfrec->addressfamily);
|
realpf->pfrec->addressfamily);
|
||||||
logeventf(ssh, "Attempting to forward remote port to "
|
logeventf(ssh, "Attempting to forward remote port to "
|
||||||
"%s:%d", realpf->dhost, realpf->dport);
|
"%s:%d", realpf->dhost, realpf->dport);
|
||||||
if (e != NULL) {
|
if (err != NULL) {
|
||||||
logeventf(ssh, "Port open failed: %s", e);
|
logeventf(ssh, "Port open failed: %s", err);
|
||||||
|
sfree(err);
|
||||||
error = "Port open failed";
|
error = "Port open failed";
|
||||||
} else {
|
} else {
|
||||||
logevent("Forwarded port opened successfully");
|
logevent("Forwarded port opened successfully");
|
||||||
@ -9909,13 +9917,13 @@ static void ssh_free(void *handle)
|
|||||||
while ((c = delpos234(ssh->channels, 0)) != NULL) {
|
while ((c = delpos234(ssh->channels, 0)) != NULL) {
|
||||||
switch (c->type) {
|
switch (c->type) {
|
||||||
case CHAN_X11:
|
case CHAN_X11:
|
||||||
if (c->u.x11.s != NULL)
|
if (c->u.x11.xconn != NULL)
|
||||||
x11_close(c->u.x11.s);
|
x11_close(c->u.x11.xconn);
|
||||||
break;
|
break;
|
||||||
case CHAN_SOCKDATA:
|
case CHAN_SOCKDATA:
|
||||||
case CHAN_SOCKDATA_DORMANT:
|
case CHAN_SOCKDATA_DORMANT:
|
||||||
if (c->u.pfd.s != NULL)
|
if (c->u.pfd.pf != NULL)
|
||||||
pfd_close(c->u.pfd.s);
|
pfd_close(c->u.pfd.pf);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ssh->version == 2) {
|
if (ssh->version == 2) {
|
||||||
@ -10288,7 +10296,7 @@ static void ssh_special(void *handle, Telnet_Special code)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *new_sock_channel(void *handle, Socket s)
|
void *new_sock_channel(void *handle, struct PortForwarding *pf)
|
||||||
{
|
{
|
||||||
Ssh ssh = (Ssh) handle;
|
Ssh ssh = (Ssh) handle;
|
||||||
struct ssh_channel *c;
|
struct ssh_channel *c;
|
||||||
@ -10298,7 +10306,7 @@ void *new_sock_channel(void *handle, Socket s)
|
|||||||
ssh2_channel_init(c);
|
ssh2_channel_init(c);
|
||||||
c->halfopen = TRUE;
|
c->halfopen = TRUE;
|
||||||
c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
|
c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
|
||||||
c->u.pfd.s = s;
|
c->u.pfd.pf = pf;
|
||||||
add234(ssh->channels, c);
|
add234(ssh->channels, c);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
44
ssh.h
44
ssh.h
@ -330,24 +330,27 @@ void random_add_heavynoise(void *noise, int length);
|
|||||||
|
|
||||||
void logevent(void *, const char *);
|
void logevent(void *, const char *);
|
||||||
|
|
||||||
|
struct PortForwarding;
|
||||||
|
|
||||||
/* Allocate and register a new channel for port forwarding */
|
/* Allocate and register a new channel for port forwarding */
|
||||||
void *new_sock_channel(void *handle, Socket s);
|
void *new_sock_channel(void *handle, struct PortForwarding *pf);
|
||||||
void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
|
void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
|
||||||
|
|
||||||
/* Exports from portfwd.c */
|
/* Exports from portfwd.c */
|
||||||
extern const char *pfd_newconnect(Socket * s, char *hostname, int port,
|
extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port,
|
||||||
void *c, Conf *conf, int addressfamily);
|
void *c, Conf *conf, int addressfamily);
|
||||||
|
extern void pfd_close(struct PortForwarding *);
|
||||||
|
extern int pfd_send(struct PortForwarding *, char *data, int len);
|
||||||
|
extern void pfd_send_eof(struct PortForwarding *);
|
||||||
|
extern void pfd_confirm(struct PortForwarding *);
|
||||||
|
extern void pfd_unthrottle(struct PortForwarding *);
|
||||||
|
extern void pfd_override_throttle(struct PortForwarding *, int enable);
|
||||||
|
struct PortListener;
|
||||||
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
|
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
|
||||||
extern const char *pfd_addforward(char *desthost, int destport, char *srcaddr,
|
extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
||||||
int port, void *backhandle, Conf *conf,
|
int port, void *backhandle, Conf *conf,
|
||||||
void **sockdata, int address_family);
|
struct PortListener **pl, int address_family);
|
||||||
extern void pfd_close(Socket s);
|
extern void pfl_terminate(struct PortListener *);
|
||||||
extern void pfd_terminate(void *sockdata);
|
|
||||||
extern int pfd_send(Socket s, char *data, int len);
|
|
||||||
extern void pfd_send_eof(Socket s);
|
|
||||||
extern void pfd_confirm(Socket s);
|
|
||||||
extern void pfd_unthrottle(Socket s);
|
|
||||||
extern void pfd_override_throttle(Socket s, int enable);
|
|
||||||
|
|
||||||
/* Exports from x11fwd.c */
|
/* Exports from x11fwd.c */
|
||||||
enum {
|
enum {
|
||||||
@ -396,13 +399,14 @@ struct X11Display {
|
|||||||
extern struct X11Display *x11_setup_display(char *display, int authtype,
|
extern struct X11Display *x11_setup_display(char *display, int authtype,
|
||||||
Conf *);
|
Conf *);
|
||||||
void x11_free_display(struct X11Display *disp);
|
void x11_free_display(struct X11Display *disp);
|
||||||
extern const char *x11_init(Socket *, struct X11Display *, void *,
|
struct X11Connection; /* opaque outside x11fwd.c */
|
||||||
const char *, int, Conf *);
|
extern char *x11_init(struct X11Connection **, struct X11Display *,
|
||||||
extern void x11_close(Socket);
|
void *, const char *, int, Conf *);
|
||||||
extern int x11_send(Socket, char *, int);
|
extern void x11_close(struct X11Connection *);
|
||||||
extern void x11_send_eof(Socket s);
|
extern int x11_send(struct X11Connection *, char *, int);
|
||||||
extern void x11_unthrottle(Socket s);
|
extern void x11_send_eof(struct X11Connection *s);
|
||||||
extern void x11_override_throttle(Socket s, int enable);
|
extern void x11_unthrottle(struct X11Connection *s);
|
||||||
|
extern void x11_override_throttle(struct X11Connection *s, int enable);
|
||||||
char *x11_display(const char *display);
|
char *x11_display(const char *display);
|
||||||
/* Platform-dependent X11 functions */
|
/* Platform-dependent X11 functions */
|
||||||
extern void platform_get_x11_auth(struct X11Display *display, Conf *);
|
extern void platform_get_x11_auth(struct X11Display *display, Conf *);
|
||||||
|
213
x11fwd.c
213
x11fwd.c
@ -26,7 +26,7 @@ struct XDMSeen {
|
|||||||
unsigned char clientid[6];
|
unsigned char clientid[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct X11Private {
|
struct X11Connection {
|
||||||
const struct plug_function_table *fn;
|
const struct plug_function_table *fn;
|
||||||
/* the above variable absolutely *must* be the first in this structure */
|
/* the above variable absolutely *must* be the first in this structure */
|
||||||
unsigned char firstpkt[12]; /* first X data packet */
|
unsigned char firstpkt[12]; /* first X data packet */
|
||||||
@ -38,7 +38,7 @@ struct X11Private {
|
|||||||
int throttled, throttle_override;
|
int throttled, throttle_override;
|
||||||
unsigned long peer_ip;
|
unsigned long peer_ip;
|
||||||
int peer_port;
|
int peer_port;
|
||||||
void *c; /* data used by ssh.c */
|
struct ssh_channel *c; /* channel structure held by ssh.c */
|
||||||
Socket s;
|
Socket s;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -504,20 +504,20 @@ static void x11_log(Plug p, int type, SockAddr addr, int port,
|
|||||||
static int x11_closing(Plug plug, const char *error_msg, int error_code,
|
static int x11_closing(Plug plug, const char *error_msg, int error_code,
|
||||||
int calling_back)
|
int calling_back)
|
||||||
{
|
{
|
||||||
struct X11Private *pr = (struct X11Private *) plug;
|
struct X11Connection *xconn = (struct X11Connection *) plug;
|
||||||
|
|
||||||
if (error_msg) {
|
if (error_msg) {
|
||||||
/*
|
/*
|
||||||
* Socket error. Slam the connection instantly shut.
|
* Socket error. Slam the connection instantly shut.
|
||||||
*/
|
*/
|
||||||
sshfwd_unclean_close(pr->c, error_msg);
|
sshfwd_unclean_close(xconn->c, error_msg);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Ordinary EOF received on socket. Send an EOF on the SSH
|
* Ordinary EOF received on socket. Send an EOF on the SSH
|
||||||
* channel.
|
* channel.
|
||||||
*/
|
*/
|
||||||
if (pr->c)
|
if (xconn->c)
|
||||||
sshfwd_write_eof(pr->c);
|
sshfwd_write_eof(xconn->c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -525,11 +525,11 @@ static int x11_closing(Plug plug, const char *error_msg, int error_code,
|
|||||||
|
|
||||||
static int x11_receive(Plug plug, int urgent, char *data, int len)
|
static int x11_receive(Plug plug, int urgent, char *data, int len)
|
||||||
{
|
{
|
||||||
struct X11Private *pr = (struct X11Private *) plug;
|
struct X11Connection *xconn = (struct X11Connection *) plug;
|
||||||
|
|
||||||
if (sshfwd_write(pr->c, data, len) > 0) {
|
if (sshfwd_write(xconn->c, data, len) > 0) {
|
||||||
pr->throttled = 1;
|
xconn->throttled = 1;
|
||||||
sk_set_frozen(pr->s, 1);
|
sk_set_frozen(xconn->s, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -537,9 +537,9 @@ static int x11_receive(Plug plug, int urgent, char *data, int len)
|
|||||||
|
|
||||||
static void x11_sent(Plug plug, int bufsize)
|
static void x11_sent(Plug plug, int bufsize)
|
||||||
{
|
{
|
||||||
struct X11Private *pr = (struct X11Private *) plug;
|
struct X11Connection *xconn = (struct X11Connection *) plug;
|
||||||
|
|
||||||
sshfwd_unthrottle(pr->c, bufsize);
|
sshfwd_unthrottle(xconn->c, bufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -563,11 +563,12 @@ int x11_get_screen_number(char *display)
|
|||||||
/*
|
/*
|
||||||
* Called to set up the raw connection.
|
* Called to set up the raw connection.
|
||||||
*
|
*
|
||||||
* Returns an error message, or NULL on success.
|
* On success, returns NULL and fills in *xconnret. On error, returns
|
||||||
* also, fills the SocketsStructure
|
* a dynamically allocated error message string.
|
||||||
*/
|
*/
|
||||||
extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
|
extern char *x11_init(struct X11Connection **xconnret,
|
||||||
const char *peeraddr, int peerport, Conf *conf)
|
struct X11Display *disp, void *c,
|
||||||
|
const char *peeraddr, int peerport, Conf *conf)
|
||||||
{
|
{
|
||||||
static const struct plug_function_table fn_table = {
|
static const struct plug_function_table fn_table = {
|
||||||
x11_log,
|
x11_log,
|
||||||
@ -578,26 +579,29 @@ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
|
|||||||
};
|
};
|
||||||
|
|
||||||
const char *err;
|
const char *err;
|
||||||
struct X11Private *pr;
|
struct X11Connection *xconn;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open socket.
|
* Open socket.
|
||||||
*/
|
*/
|
||||||
pr = snew(struct X11Private);
|
xconn = *xconnret = snew(struct X11Connection);
|
||||||
pr->fn = &fn_table;
|
xconn->fn = &fn_table;
|
||||||
pr->auth_protocol = NULL;
|
xconn->auth_protocol = NULL;
|
||||||
pr->disp = disp;
|
xconn->disp = disp;
|
||||||
pr->verified = 0;
|
xconn->verified = 0;
|
||||||
pr->data_read = 0;
|
xconn->data_read = 0;
|
||||||
pr->throttled = pr->throttle_override = 0;
|
xconn->throttled = xconn->throttle_override = 0;
|
||||||
pr->c = c;
|
xconn->c = c;
|
||||||
|
|
||||||
pr->s = *s = new_connection(sk_addr_dup(disp->addr),
|
xconn->s = new_connection(sk_addr_dup(disp->addr),
|
||||||
disp->realhost, disp->port,
|
disp->realhost, disp->port,
|
||||||
0, 1, 0, 0, (Plug) pr, conf);
|
0, 1, 0, 0, (Plug) xconn, conf);
|
||||||
if ((err = sk_socket_error(*s)) != NULL) {
|
if ((err = sk_socket_error(xconn->s)) != NULL) {
|
||||||
sfree(pr);
|
char *err_ret = dupstr(err);
|
||||||
return err;
|
sk_close(xconn->s);
|
||||||
|
sfree(xconn);
|
||||||
|
*xconnret = NULL;
|
||||||
|
return err_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -607,109 +611,102 @@ extern const char *x11_init(Socket *s, struct X11Display *disp, void *c,
|
|||||||
int i[4];
|
int i[4];
|
||||||
if (peeraddr &&
|
if (peeraddr &&
|
||||||
4 == sscanf(peeraddr, "%d.%d.%d.%d", i+0, i+1, i+2, i+3)) {
|
4 == sscanf(peeraddr, "%d.%d.%d.%d", i+0, i+1, i+2, i+3)) {
|
||||||
pr->peer_ip = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];
|
xconn->peer_ip = (i[0] << 24) | (i[1] << 16) | (i[2] << 8) | i[3];
|
||||||
pr->peer_port = peerport;
|
xconn->peer_port = peerport;
|
||||||
} else {
|
} else {
|
||||||
pr->peer_ip = 0;
|
xconn->peer_ip = 0;
|
||||||
pr->peer_port = -1;
|
xconn->peer_port = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_set_private_ptr(*s, pr);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void x11_close(Socket s)
|
void x11_close(struct X11Connection *xconn)
|
||||||
{
|
{
|
||||||
struct X11Private *pr;
|
if (!xconn)
|
||||||
if (!s)
|
|
||||||
return;
|
return;
|
||||||
pr = (struct X11Private *) sk_get_private_ptr(s);
|
|
||||||
if (pr->auth_protocol) {
|
if (xconn->auth_protocol) {
|
||||||
sfree(pr->auth_protocol);
|
sfree(xconn->auth_protocol);
|
||||||
sfree(pr->auth_data);
|
sfree(xconn->auth_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
sfree(pr);
|
sk_close(xconn->s);
|
||||||
|
sfree(xconn);
|
||||||
sk_close(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void x11_unthrottle(Socket s)
|
void x11_unthrottle(struct X11Connection *xconn)
|
||||||
{
|
{
|
||||||
struct X11Private *pr;
|
if (!xconn)
|
||||||
if (!s)
|
|
||||||
return;
|
return;
|
||||||
pr = (struct X11Private *) sk_get_private_ptr(s);
|
|
||||||
|
|
||||||
pr->throttled = 0;
|
xconn->throttled = 0;
|
||||||
sk_set_frozen(s, pr->throttled || pr->throttle_override);
|
sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
void x11_override_throttle(Socket s, int enable)
|
void x11_override_throttle(struct X11Connection *xconn, int enable)
|
||||||
{
|
{
|
||||||
struct X11Private *pr;
|
if (!xconn)
|
||||||
if (!s)
|
|
||||||
return;
|
return;
|
||||||
pr = (struct X11Private *) sk_get_private_ptr(s);
|
|
||||||
|
|
||||||
pr->throttle_override = enable;
|
xconn->throttle_override = enable;
|
||||||
sk_set_frozen(s, pr->throttled || pr->throttle_override);
|
sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called to send data down the raw connection.
|
* Called to send data down the raw connection.
|
||||||
*/
|
*/
|
||||||
int x11_send(Socket s, char *data, int len)
|
int x11_send(struct X11Connection *xconn, char *data, int len)
|
||||||
{
|
{
|
||||||
struct X11Private *pr;
|
if (!xconn)
|
||||||
if (!s)
|
|
||||||
return 0;
|
return 0;
|
||||||
pr = (struct X11Private *) sk_get_private_ptr(s);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the first packet.
|
* Read the first packet.
|
||||||
*/
|
*/
|
||||||
while (len > 0 && pr->data_read < 12)
|
while (len > 0 && xconn->data_read < 12)
|
||||||
pr->firstpkt[pr->data_read++] = (unsigned char) (len--, *data++);
|
xconn->firstpkt[xconn->data_read++] = (unsigned char) (len--, *data++);
|
||||||
if (pr->data_read < 12)
|
if (xconn->data_read < 12)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have not allocated the auth_protocol and auth_data
|
* If we have not allocated the auth_protocol and auth_data
|
||||||
* strings, do so now.
|
* strings, do so now.
|
||||||
*/
|
*/
|
||||||
if (!pr->auth_protocol) {
|
if (!xconn->auth_protocol) {
|
||||||
pr->auth_plen = GET_16BIT(pr->firstpkt[0], pr->firstpkt + 6);
|
xconn->auth_plen = GET_16BIT(xconn->firstpkt[0], xconn->firstpkt + 6);
|
||||||
pr->auth_dlen = GET_16BIT(pr->firstpkt[0], pr->firstpkt + 8);
|
xconn->auth_dlen = GET_16BIT(xconn->firstpkt[0], xconn->firstpkt + 8);
|
||||||
pr->auth_psize = (pr->auth_plen + 3) & ~3;
|
xconn->auth_psize = (xconn->auth_plen + 3) & ~3;
|
||||||
pr->auth_dsize = (pr->auth_dlen + 3) & ~3;
|
xconn->auth_dsize = (xconn->auth_dlen + 3) & ~3;
|
||||||
/* Leave room for a terminating zero, to make our lives easier. */
|
/* Leave room for a terminating zero, to make our lives easier. */
|
||||||
pr->auth_protocol = snewn(pr->auth_psize + 1, char);
|
xconn->auth_protocol = snewn(xconn->auth_psize + 1, char);
|
||||||
pr->auth_data = snewn(pr->auth_dsize, unsigned char);
|
xconn->auth_data = snewn(xconn->auth_dsize, unsigned char);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read the auth_protocol and auth_data strings.
|
* Read the auth_protocol and auth_data strings.
|
||||||
*/
|
*/
|
||||||
while (len > 0 && pr->data_read < 12 + pr->auth_psize)
|
while (len > 0 &&
|
||||||
pr->auth_protocol[pr->data_read++ - 12] = (len--, *data++);
|
xconn->data_read < 12 + xconn->auth_psize)
|
||||||
while (len > 0 && pr->data_read < 12 + pr->auth_psize + pr->auth_dsize)
|
xconn->auth_protocol[xconn->data_read++ - 12] = (len--, *data++);
|
||||||
pr->auth_data[pr->data_read++ - 12 -
|
while (len > 0 &&
|
||||||
pr->auth_psize] = (unsigned char) (len--, *data++);
|
xconn->data_read < 12 + xconn->auth_psize + xconn->auth_dsize)
|
||||||
if (pr->data_read < 12 + pr->auth_psize + pr->auth_dsize)
|
xconn->auth_data[xconn->data_read++ - 12 -
|
||||||
|
xconn->auth_psize] = (unsigned char) (len--, *data++);
|
||||||
|
if (xconn->data_read < 12 + xconn->auth_psize + xconn->auth_dsize)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we haven't verified the authorisation, do so now.
|
* If we haven't verified the authorisation, do so now.
|
||||||
*/
|
*/
|
||||||
if (!pr->verified) {
|
if (!xconn->verified) {
|
||||||
char *err;
|
char *err;
|
||||||
|
|
||||||
pr->auth_protocol[pr->auth_plen] = '\0'; /* ASCIZ */
|
xconn->auth_protocol[xconn->auth_plen] = '\0'; /* ASCIZ */
|
||||||
err = x11_verify(pr->peer_ip, pr->peer_port,
|
err = x11_verify(xconn->peer_ip, xconn->peer_port,
|
||||||
pr->disp, pr->auth_protocol,
|
xconn->disp, xconn->auth_protocol,
|
||||||
pr->auth_data, pr->auth_dlen);
|
xconn->auth_data, xconn->auth_dlen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If authorisation failed, construct and send an error
|
* If authorisation failed, construct and send an error
|
||||||
@ -726,12 +723,12 @@ int x11_send(Socket s, char *data, int len)
|
|||||||
msgsize = (msglen + 3) & ~3;
|
msgsize = (msglen + 3) & ~3;
|
||||||
reply[0] = 0; /* failure */
|
reply[0] = 0; /* failure */
|
||||||
reply[1] = msglen; /* length of reason string */
|
reply[1] = msglen; /* length of reason string */
|
||||||
memcpy(reply + 2, pr->firstpkt + 2, 4); /* major/minor proto vsn */
|
memcpy(reply + 2, xconn->firstpkt + 2, 4); /* major/minor proto vsn */
|
||||||
PUT_16BIT(pr->firstpkt[0], reply + 6, msgsize >> 2);/* data len */
|
PUT_16BIT(xconn->firstpkt[0], reply + 6, msgsize >> 2);/* data len */
|
||||||
memset(reply + 8, 0, msgsize);
|
memset(reply + 8, 0, msgsize);
|
||||||
memcpy(reply + 8, message, msglen);
|
memcpy(reply + 8, message, msglen);
|
||||||
sshfwd_write(pr->c, (char *)reply, 8 + msgsize);
|
sshfwd_write(xconn->c, (char *)reply, 8 + msgsize);
|
||||||
sshfwd_write_eof(pr->c);
|
sshfwd_write_eof(xconn->c);
|
||||||
sfree(reply);
|
sfree(reply);
|
||||||
sfree(message);
|
sfree(message);
|
||||||
return 0;
|
return 0;
|
||||||
@ -745,59 +742,59 @@ int x11_send(Socket s, char *data, int len)
|
|||||||
{
|
{
|
||||||
char realauthdata[64];
|
char realauthdata[64];
|
||||||
int realauthlen = 0;
|
int realauthlen = 0;
|
||||||
int authstrlen = strlen(x11_authnames[pr->disp->localauthproto]);
|
int authstrlen = strlen(x11_authnames[xconn->disp->localauthproto]);
|
||||||
int buflen = 0; /* initialise to placate optimiser */
|
int buflen = 0; /* initialise to placate optimiser */
|
||||||
static const char zeroes[4] = { 0,0,0,0 };
|
static const char zeroes[4] = { 0,0,0,0 };
|
||||||
void *buf;
|
void *buf;
|
||||||
|
|
||||||
if (pr->disp->localauthproto == X11_MIT) {
|
if (xconn->disp->localauthproto == X11_MIT) {
|
||||||
assert(pr->disp->localauthdatalen <= lenof(realauthdata));
|
assert(xconn->disp->localauthdatalen <= lenof(realauthdata));
|
||||||
realauthlen = pr->disp->localauthdatalen;
|
realauthlen = xconn->disp->localauthdatalen;
|
||||||
memcpy(realauthdata, pr->disp->localauthdata, realauthlen);
|
memcpy(realauthdata, xconn->disp->localauthdata, realauthlen);
|
||||||
} else if (pr->disp->localauthproto == X11_XDM &&
|
} else if (xconn->disp->localauthproto == X11_XDM &&
|
||||||
pr->disp->localauthdatalen == 16 &&
|
xconn->disp->localauthdatalen == 16 &&
|
||||||
((buf = sk_getxdmdata(s, &buflen))!=0)) {
|
((buf = sk_getxdmdata(xconn->s, &buflen))!=0)) {
|
||||||
time_t t;
|
time_t t;
|
||||||
realauthlen = (buflen+12+7) & ~7;
|
realauthlen = (buflen+12+7) & ~7;
|
||||||
assert(realauthlen <= lenof(realauthdata));
|
assert(realauthlen <= lenof(realauthdata));
|
||||||
memset(realauthdata, 0, realauthlen);
|
memset(realauthdata, 0, realauthlen);
|
||||||
memcpy(realauthdata, pr->disp->localauthdata, 8);
|
memcpy(realauthdata, xconn->disp->localauthdata, 8);
|
||||||
memcpy(realauthdata+8, buf, buflen);
|
memcpy(realauthdata+8, buf, buflen);
|
||||||
t = time(NULL);
|
t = time(NULL);
|
||||||
PUT_32BIT_MSB_FIRST(realauthdata+8+buflen, t);
|
PUT_32BIT_MSB_FIRST(realauthdata+8+buflen, t);
|
||||||
des_encrypt_xdmauth(pr->disp->localauthdata+9,
|
des_encrypt_xdmauth(xconn->disp->localauthdata+9,
|
||||||
(unsigned char *)realauthdata,
|
(unsigned char *)realauthdata,
|
||||||
realauthlen);
|
realauthlen);
|
||||||
sfree(buf);
|
sfree(buf);
|
||||||
}
|
}
|
||||||
/* implement other auth methods here if required */
|
/* implement other auth methods here if required */
|
||||||
|
|
||||||
PUT_16BIT(pr->firstpkt[0], pr->firstpkt + 6, authstrlen);
|
PUT_16BIT(xconn->firstpkt[0], xconn->firstpkt + 6, authstrlen);
|
||||||
PUT_16BIT(pr->firstpkt[0], pr->firstpkt + 8, realauthlen);
|
PUT_16BIT(xconn->firstpkt[0], xconn->firstpkt + 8, realauthlen);
|
||||||
|
|
||||||
sk_write(s, (char *)pr->firstpkt, 12);
|
sk_write(xconn->s, (char *)xconn->firstpkt, 12);
|
||||||
|
|
||||||
if (authstrlen) {
|
if (authstrlen) {
|
||||||
sk_write(s, x11_authnames[pr->disp->localauthproto],
|
sk_write(xconn->s, x11_authnames[xconn->disp->localauthproto],
|
||||||
authstrlen);
|
authstrlen);
|
||||||
sk_write(s, zeroes, 3 & (-authstrlen));
|
sk_write(xconn->s, zeroes, 3 & (-authstrlen));
|
||||||
}
|
}
|
||||||
if (realauthlen) {
|
if (realauthlen) {
|
||||||
sk_write(s, realauthdata, realauthlen);
|
sk_write(xconn->s, realauthdata, realauthlen);
|
||||||
sk_write(s, zeroes, 3 & (-realauthlen));
|
sk_write(xconn->s, zeroes, 3 & (-realauthlen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pr->verified = 1;
|
xconn->verified = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After initialisation, just copy data simply.
|
* After initialisation, just copy data simply.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return sk_write(s, data, len);
|
return sk_write(xconn->s, data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void x11_send_eof(Socket s)
|
void x11_send_eof(struct X11Connection *xconn)
|
||||||
{
|
{
|
||||||
sk_write_eof(s);
|
sk_write_eof(xconn->s);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user