зеркало из https://github.com/github/putty.git
Move port-forwarding setup out of ssh.c.
The tree234 storing currently active port forwardings - both local and remote - now lives in portfwd.c, as does the complicated function that updates it based on a Conf listing the new set of desired forwardings. Local port forwardings are passed to ssh.c via the same route as before - once the listening port receives a connection and portfwd.c knows where it should be directed to (in particular, after the SOCKS exchange, if any), it calls ssh_send_port_open. Remote forwardings are now initiated by calling ssh_rportfwd_alloc, which adds an entry to the rportfwds tree (which _is_ still in ssh.c, and still confusingly sorted by a different criterion depending on SSH protocol version) and sends out the appropriate protocol request. ssh_rportfwd_remove cancels one again, sending a protocol request too. Those functions look enough like ssh_{alloc,remove}_sharing_rportfwd that I've merged those into the new pair as well - now allocating an rportfwd allows you to specify either a destination host/port or a sharing context, and returns a handy pointer you can use to cancel the forwarding later.
This commit is contained in:
Родитель
aa08e6ca91
Коммит
895b09a4c6
3
defs.h
3
defs.h
|
@ -60,6 +60,9 @@ typedef struct ssh_sharing_state ssh_sharing_state;
|
|||
typedef struct ssh_sharing_connstate ssh_sharing_connstate;
|
||||
typedef struct share_channel share_channel;
|
||||
|
||||
typedef struct PortFwdManager PortFwdManager;
|
||||
typedef struct PortFwdRecord PortFwdRecord;
|
||||
|
||||
typedef struct dlgparam dlgparam;
|
||||
|
||||
typedef struct settings_w settings_w;
|
||||
|
|
11
misc.c
11
misc.c
|
@ -1181,6 +1181,17 @@ int smemeq(const void *av, const void *bv, size_t len)
|
|||
return (0x100 - val) >> 8;
|
||||
}
|
||||
|
||||
int nullstrcmp(const char *a, const char *b)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
return 0;
|
||||
if (a == NULL)
|
||||
return -1;
|
||||
if (b == NULL)
|
||||
return +1;
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
ptrlen make_ptrlen(const void *ptr, size_t len)
|
||||
{
|
||||
ptrlen pl;
|
||||
|
|
6
misc.h
6
misc.h
|
@ -87,6 +87,12 @@ int validate_manual_hostkey(char *key);
|
|||
|
||||
struct tm ltime(void);
|
||||
|
||||
/*
|
||||
* Special form of strcmp which can cope with NULL inputs. NULL is
|
||||
* defined to sort before even the empty string.
|
||||
*/
|
||||
int nullstrcmp(const char *a, const char *b);
|
||||
|
||||
ptrlen make_ptrlen(const void *ptr, size_t len);
|
||||
int ptrlen_eq_string(ptrlen pl, const char *str);
|
||||
char *mkstr(ptrlen pl);
|
||||
|
|
511
portfwd.c
511
portfwd.c
|
@ -10,6 +10,18 @@
|
|||
#include "ssh.h"
|
||||
#include "sshchan.h"
|
||||
|
||||
static void logeventf(Frontend *frontend, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *buf;
|
||||
|
||||
va_start(ap, fmt);
|
||||
buf = dupvprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
logevent(frontend, buf);
|
||||
sfree(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumeration of values that live in the 'socks_state' field of
|
||||
* struct PortForwarding.
|
||||
|
@ -139,6 +151,8 @@ static void pfd_closing(Plug plug, const char *error_msg, int error_code,
|
|||
}
|
||||
}
|
||||
|
||||
static void pfl_terminate(struct PortListener *pl);
|
||||
|
||||
static void pfl_closing(Plug plug, const char *error_msg, int error_code,
|
||||
int calling_back)
|
||||
{
|
||||
|
@ -447,61 +461,6 @@ static const struct ChannelVtable PortForwarding_channelvt = {
|
|||
chan_no_eager_close,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
char *pfd_connect(Channel **chan_ret, char *hostname,int port,
|
||||
SshChannel *c, Conf *conf, int addressfamily)
|
||||
{
|
||||
SockAddr addr;
|
||||
const char *err;
|
||||
char *dummy_realhost = NULL;
|
||||
struct PortForwarding *pf;
|
||||
|
||||
/*
|
||||
* Try to find host.
|
||||
*/
|
||||
addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily,
|
||||
NULL, NULL);
|
||||
if ((err = sk_addr_error(addr)) != NULL) {
|
||||
char *err_ret = dupstr(err);
|
||||
sk_addr_free(addr);
|
||||
sfree(dummy_realhost);
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open socket.
|
||||
*/
|
||||
pf = new_portfwd_state();
|
||||
*chan_ret = &pf->chan;
|
||||
pf->plugvt = &PortForwarding_plugvt;
|
||||
pf->chan.initial_fixed_window_size = 0;
|
||||
pf->chan.vt = &PortForwarding_channelvt;
|
||||
pf->input_wanted = TRUE;
|
||||
pf->ready = 1;
|
||||
pf->c = c;
|
||||
pf->ssh = NULL; /* we shouldn't need this */
|
||||
pf->socks_state = SOCKS_NONE;
|
||||
|
||||
pf->s = new_connection(addr, dummy_realhost, port,
|
||||
0, 1, 0, 0, &pf->plugvt, conf);
|
||||
sfree(dummy_realhost);
|
||||
if ((err = sk_socket_error(pf->s)) != NULL) {
|
||||
char *err_ret = dupstr(err);
|
||||
sk_close(pf->s);
|
||||
free_portfwd_state(pf);
|
||||
*chan_ret = NULL;
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
called when someone connects to the local port
|
||||
*/
|
||||
|
@ -560,12 +519,14 @@ static const Plug_vtable PortListener_plugvt = {
|
|||
/*
|
||||
* Add a new port-forwarding listener from srcaddr:port -> desthost:destport.
|
||||
*
|
||||
* desthost == NULL indicates dynamic SOCKS port forwarding.
|
||||
*
|
||||
* On success, returns NULL and fills in *pl_ret. On error, returns a
|
||||
* dynamically allocated error message string.
|
||||
*/
|
||||
char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
||||
int port, Ssh ssh, Conf *conf,
|
||||
struct PortListener **pl_ret, int address_family)
|
||||
static char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
||||
int port, Ssh ssh, Conf *conf,
|
||||
struct PortListener **pl_ret, int address_family)
|
||||
{
|
||||
const char *err;
|
||||
struct PortListener *pl;
|
||||
|
@ -614,7 +575,7 @@ static void pfd_close(struct PortForwarding *pf)
|
|||
/*
|
||||
* Terminate a listener.
|
||||
*/
|
||||
void pfl_terminate(struct PortListener *pl)
|
||||
static void pfl_terminate(struct PortListener *pl)
|
||||
{
|
||||
if (!pl)
|
||||
return;
|
||||
|
@ -676,9 +637,431 @@ static void pfd_open_failure(Channel *chan, const char *errtext)
|
|||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
|
||||
char *msg = dupprintf(
|
||||
"Forwarded connection refused by server%s%s",
|
||||
errtext ? ": " : "", errtext ? errtext : "");
|
||||
logevent(ssh_get_frontend(pf->ssh), msg);
|
||||
sfree(msg);
|
||||
logeventf(ssh_get_frontend(pf->ssh),
|
||||
"Forwarded connection refused by server%s%s",
|
||||
errtext ? ": " : "", errtext ? errtext : "");
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Code to manage the complete set of currently active port
|
||||
* forwardings, and update it from Conf.
|
||||
*/
|
||||
|
||||
struct PortFwdRecord {
|
||||
enum { DESTROY, KEEP, CREATE } status;
|
||||
int type;
|
||||
unsigned sport, dport;
|
||||
char *saddr, *daddr;
|
||||
char *sserv, *dserv;
|
||||
struct ssh_rportfwd *remote;
|
||||
int addressfamily;
|
||||
struct PortListener *local;
|
||||
};
|
||||
|
||||
static int pfr_cmp(void *av, void *bv)
|
||||
{
|
||||
PortFwdRecord *a = (PortFwdRecord *) av;
|
||||
PortFwdRecord *b = (PortFwdRecord *) bv;
|
||||
int i;
|
||||
if (a->type > b->type)
|
||||
return +1;
|
||||
if (a->type < b->type)
|
||||
return -1;
|
||||
if (a->addressfamily > b->addressfamily)
|
||||
return +1;
|
||||
if (a->addressfamily < b->addressfamily)
|
||||
return -1;
|
||||
if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
|
||||
return i < 0 ? -1 : +1;
|
||||
if (a->sport > b->sport)
|
||||
return +1;
|
||||
if (a->sport < b->sport)
|
||||
return -1;
|
||||
if (a->type != 'D') {
|
||||
if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
|
||||
return i < 0 ? -1 : +1;
|
||||
if (a->dport > b->dport)
|
||||
return +1;
|
||||
if (a->dport < b->dport)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pfr_free(PortFwdRecord *pfr)
|
||||
{
|
||||
/* Dispose of any listening socket. */
|
||||
if (pfr->local)
|
||||
pfl_terminate(pfr->local);
|
||||
|
||||
sfree(pfr->saddr);
|
||||
sfree(pfr->daddr);
|
||||
sfree(pfr->sserv);
|
||||
sfree(pfr->dserv);
|
||||
sfree(pfr);
|
||||
}
|
||||
|
||||
struct PortFwdManager {
|
||||
Ssh ssh;
|
||||
Frontend *frontend;
|
||||
Conf *conf;
|
||||
tree234 *forwardings;
|
||||
};
|
||||
|
||||
PortFwdManager *portfwdmgr_new(Ssh ssh)
|
||||
{
|
||||
PortFwdManager *mgr = snew(PortFwdManager);
|
||||
|
||||
mgr->ssh = ssh;
|
||||
mgr->frontend = ssh_get_frontend(ssh);
|
||||
mgr->conf = NULL;
|
||||
mgr->forwardings = newtree234(pfr_cmp);
|
||||
|
||||
return mgr;
|
||||
}
|
||||
|
||||
void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr)
|
||||
{
|
||||
PortFwdRecord *realpfr = del234(mgr->forwardings, pfr);
|
||||
if (realpfr == pfr)
|
||||
pfr_free(pfr);
|
||||
}
|
||||
|
||||
void portfwdmgr_close_all(PortFwdManager *mgr)
|
||||
{
|
||||
PortFwdRecord *pfr;
|
||||
|
||||
while ((pfr = delpos234(mgr->forwardings, 0)) != NULL)
|
||||
pfr_free(pfr);
|
||||
}
|
||||
|
||||
void portfwdmgr_free(PortFwdManager *mgr)
|
||||
{
|
||||
portfwdmgr_close_all(mgr);
|
||||
freetree234(mgr->forwardings);
|
||||
if (mgr->conf)
|
||||
conf_free(mgr->conf);
|
||||
sfree(mgr);
|
||||
}
|
||||
|
||||
void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
|
||||
{
|
||||
PortFwdRecord *pfr;
|
||||
int i;
|
||||
char *key, *val;
|
||||
|
||||
if (mgr->conf)
|
||||
conf_free(mgr->conf);
|
||||
mgr->conf = conf_copy(conf);
|
||||
|
||||
/*
|
||||
* Go through the existing port forwardings and tag them
|
||||
* with status==DESTROY. Any that we want to keep will be
|
||||
* re-enabled (status==KEEP) as we go through the
|
||||
* configuration and find out which bits are the same as
|
||||
* they were before.
|
||||
*/
|
||||
for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++)
|
||||
pfr->status = DESTROY;
|
||||
|
||||
for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
|
||||
val != NULL;
|
||||
val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
|
||||
char *kp, *kp2, *vp, *vp2;
|
||||
char address_family, type;
|
||||
int sport, dport, sserv, dserv;
|
||||
char *sports, *dports, *saddr, *host;
|
||||
|
||||
kp = key;
|
||||
|
||||
address_family = 'A';
|
||||
type = 'L';
|
||||
if (*kp == 'A' || *kp == '4' || *kp == '6')
|
||||
address_family = *kp++;
|
||||
if (*kp == 'L' || *kp == 'R')
|
||||
type = *kp++;
|
||||
|
||||
if ((kp2 = host_strchr(kp, ':')) != NULL) {
|
||||
/*
|
||||
* There's a colon in the middle of the source port
|
||||
* string, which means that the part before it is
|
||||
* actually a source address.
|
||||
*/
|
||||
char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp);
|
||||
saddr = host_strduptrim(saddr_tmp);
|
||||
sfree(saddr_tmp);
|
||||
sports = kp2+1;
|
||||
} else {
|
||||
saddr = NULL;
|
||||
sports = kp;
|
||||
}
|
||||
sport = atoi(sports);
|
||||
sserv = 0;
|
||||
if (sport == 0) {
|
||||
sserv = 1;
|
||||
sport = net_service_lookup(sports);
|
||||
if (!sport) {
|
||||
logeventf(mgr->frontend, "Service lookup failed for source"
|
||||
" port \"%s\"", sports);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == 'L' && !strcmp(val, "D")) {
|
||||
/* dynamic forwarding */
|
||||
host = NULL;
|
||||
dports = NULL;
|
||||
dport = -1;
|
||||
dserv = 0;
|
||||
type = 'D';
|
||||
} else {
|
||||
/* ordinary forwarding */
|
||||
vp = val;
|
||||
vp2 = vp + host_strcspn(vp, ":");
|
||||
host = dupprintf("%.*s", (int)(vp2 - vp), vp);
|
||||
if (*vp2)
|
||||
vp2++;
|
||||
dports = vp2;
|
||||
dport = atoi(dports);
|
||||
dserv = 0;
|
||||
if (dport == 0) {
|
||||
dserv = 1;
|
||||
dport = net_service_lookup(dports);
|
||||
if (!dport) {
|
||||
logeventf(mgr->frontend,
|
||||
"Service lookup failed for destination"
|
||||
" port \"%s\"", dports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sport && dport) {
|
||||
/* Set up a description of the source port. */
|
||||
pfr = snew(PortFwdRecord);
|
||||
pfr->type = type;
|
||||
pfr->saddr = saddr;
|
||||
pfr->sserv = sserv ? dupstr(sports) : NULL;
|
||||
pfr->sport = sport;
|
||||
pfr->daddr = host;
|
||||
pfr->dserv = dserv ? dupstr(dports) : NULL;
|
||||
pfr->dport = dport;
|
||||
pfr->local = NULL;
|
||||
pfr->remote = NULL;
|
||||
pfr->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :
|
||||
address_family == '6' ? ADDRTYPE_IPV6 :
|
||||
ADDRTYPE_UNSPEC);
|
||||
|
||||
PortFwdRecord *existing = add234(mgr->forwardings, pfr);
|
||||
if (existing != pfr) {
|
||||
if (existing->status == DESTROY) {
|
||||
/*
|
||||
* We already have a port forwarding up and running
|
||||
* with precisely these parameters. Hence, no need
|
||||
* to do anything; simply re-tag the existing one
|
||||
* as KEEP.
|
||||
*/
|
||||
existing->status = KEEP;
|
||||
}
|
||||
/*
|
||||
* Anything else indicates that there was a duplicate
|
||||
* in our input, which we'll silently ignore.
|
||||
*/
|
||||
pfr_free(pfr);
|
||||
} else {
|
||||
pfr->status = CREATE;
|
||||
}
|
||||
} else {
|
||||
sfree(saddr);
|
||||
sfree(host);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now go through and destroy any port forwardings which were
|
||||
* not re-enabled.
|
||||
*/
|
||||
for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {
|
||||
if (pfr->status == DESTROY) {
|
||||
char *message;
|
||||
|
||||
message = dupprintf("%s port forwarding from %s%s%d",
|
||||
pfr->type == 'L' ? "local" :
|
||||
pfr->type == 'R' ? "remote" : "dynamic",
|
||||
pfr->saddr ? pfr->saddr : "",
|
||||
pfr->saddr ? ":" : "",
|
||||
pfr->sport);
|
||||
|
||||
if (pfr->type != 'D') {
|
||||
char *msg2 = dupprintf("%s to %s:%d", message,
|
||||
pfr->daddr, pfr->dport);
|
||||
sfree(message);
|
||||
message = msg2;
|
||||
}
|
||||
|
||||
logeventf(mgr->frontend, "Cancelling %s", message);
|
||||
sfree(message);
|
||||
|
||||
/* pfr->remote or pfr->local may be NULL if setting up a
|
||||
* forwarding failed. */
|
||||
if (pfr->remote) {
|
||||
/*
|
||||
* Cancel the port forwarding at the server
|
||||
* end.
|
||||
*
|
||||
* Actually closing the listening port on the server
|
||||
* side may fail - because in SSH-1 there's no message
|
||||
* in the protocol to request it!
|
||||
*
|
||||
* Instead, we simply remove the record of the
|
||||
* forwarding from our local end, so that any
|
||||
* connections the server tries to make on it are
|
||||
* rejected.
|
||||
*/
|
||||
ssh_rportfwd_remove(mgr->ssh, pfr->remote);
|
||||
} else if (pfr->local) {
|
||||
pfl_terminate(pfr->local);
|
||||
}
|
||||
|
||||
delpos234(mgr->forwardings, i);
|
||||
pfr_free(pfr);
|
||||
i--; /* so we don't skip one in the list */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* And finally, set up any new port forwardings (status==CREATE).
|
||||
*/
|
||||
for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {
|
||||
if (pfr->status == CREATE) {
|
||||
char *sportdesc, *dportdesc;
|
||||
sportdesc = dupprintf("%s%s%s%s%d%s",
|
||||
pfr->saddr ? pfr->saddr : "",
|
||||
pfr->saddr ? ":" : "",
|
||||
pfr->sserv ? pfr->sserv : "",
|
||||
pfr->sserv ? "(" : "",
|
||||
pfr->sport,
|
||||
pfr->sserv ? ")" : "");
|
||||
if (pfr->type == 'D') {
|
||||
dportdesc = NULL;
|
||||
} else {
|
||||
dportdesc = dupprintf("%s:%s%s%d%s",
|
||||
pfr->daddr,
|
||||
pfr->dserv ? pfr->dserv : "",
|
||||
pfr->dserv ? "(" : "",
|
||||
pfr->dport,
|
||||
pfr->dserv ? ")" : "");
|
||||
}
|
||||
|
||||
if (pfr->type == 'L') {
|
||||
char *err = pfl_listen(pfr->daddr, pfr->dport,
|
||||
pfr->saddr, pfr->sport,
|
||||
mgr->ssh, conf, &pfr->local,
|
||||
pfr->addressfamily);
|
||||
|
||||
logeventf(mgr->frontend,
|
||||
"Local %sport %s forwarding to %s%s%s",
|
||||
pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
||||
pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
||||
sportdesc, dportdesc,
|
||||
err ? " failed: " : "", err ? err : "");
|
||||
if (err)
|
||||
sfree(err);
|
||||
} else if (pfr->type == 'D') {
|
||||
char *err = pfl_listen(NULL, -1, pfr->saddr, pfr->sport,
|
||||
mgr->ssh, conf, &pfr->local,
|
||||
pfr->addressfamily);
|
||||
|
||||
logeventf(mgr->frontend,
|
||||
"Local %sport %s SOCKS dynamic forwarding%s%s",
|
||||
pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
||||
pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
||||
sportdesc,
|
||||
err ? " failed: " : "", err ? err : "");
|
||||
|
||||
if (err)
|
||||
sfree(err);
|
||||
} else {
|
||||
const char *shost;
|
||||
|
||||
if (pfr->saddr) {
|
||||
shost = pfr->saddr;
|
||||
} else if (conf_get_int(conf, CONF_rport_acceptall)) {
|
||||
shost = "";
|
||||
} else {
|
||||
shost = "localhost";
|
||||
}
|
||||
|
||||
pfr->remote = ssh_rportfwd_alloc(
|
||||
mgr->ssh, shost, pfr->sport, pfr->daddr, pfr->dport,
|
||||
pfr->addressfamily, sportdesc, pfr, NULL);
|
||||
|
||||
if (!pfr->remote) {
|
||||
logeventf(mgr->frontend,
|
||||
"Duplicate remote port forwarding to %s:%d",
|
||||
pfr->daddr, pfr->dport);
|
||||
pfr_free(pfr);
|
||||
} else {
|
||||
logeventf(mgr->frontend, "Requesting remote port %s"
|
||||
" forward to %s", sportdesc, dportdesc);
|
||||
}
|
||||
}
|
||||
sfree(sportdesc);
|
||||
sfree(dportdesc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
|
||||
char *hostname, int port, SshChannel *c,
|
||||
int addressfamily)
|
||||
{
|
||||
SockAddr addr;
|
||||
const char *err;
|
||||
char *dummy_realhost = NULL;
|
||||
struct PortForwarding *pf;
|
||||
|
||||
/*
|
||||
* Try to find host.
|
||||
*/
|
||||
addr = name_lookup(hostname, port, &dummy_realhost, mgr->conf,
|
||||
addressfamily, NULL, NULL);
|
||||
if ((err = sk_addr_error(addr)) != NULL) {
|
||||
char *err_ret = dupstr(err);
|
||||
sk_addr_free(addr);
|
||||
sfree(dummy_realhost);
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open socket.
|
||||
*/
|
||||
pf = new_portfwd_state();
|
||||
*chan_ret = &pf->chan;
|
||||
pf->plugvt = &PortForwarding_plugvt;
|
||||
pf->chan.initial_fixed_window_size = 0;
|
||||
pf->chan.vt = &PortForwarding_channelvt;
|
||||
pf->input_wanted = TRUE;
|
||||
pf->ready = 1;
|
||||
pf->c = c;
|
||||
pf->ssh = mgr->ssh;
|
||||
pf->socks_state = SOCKS_NONE;
|
||||
|
||||
pf->s = new_connection(addr, dummy_realhost, port,
|
||||
0, 1, 0, 0, &pf->plugvt, mgr->conf);
|
||||
sfree(dummy_realhost);
|
||||
if ((err = sk_socket_error(pf->s)) != NULL) {
|
||||
char *err_ret = dupstr(err);
|
||||
sk_close(pf->s);
|
||||
free_portfwd_state(pf);
|
||||
*chan_ret = NULL;
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
556
ssh.c
556
ssh.c
|
@ -527,42 +527,22 @@ struct ssh_portfwd; /* forward declaration */
|
|||
struct ssh_rportfwd {
|
||||
unsigned sport, dport;
|
||||
char *shost, *dhost;
|
||||
char *sportdesc;
|
||||
int addressfamily;
|
||||
char *log_description; /* name of remote listening port, for logging */
|
||||
ssh_sharing_connstate *share_ctx;
|
||||
struct ssh_portfwd *pfrec;
|
||||
PortFwdRecord *pfr;
|
||||
};
|
||||
|
||||
static void free_rportfwd(struct ssh_rportfwd *pf)
|
||||
{
|
||||
if (pf) {
|
||||
sfree(pf->sportdesc);
|
||||
sfree(pf->log_description);
|
||||
sfree(pf->shost);
|
||||
sfree(pf->dhost);
|
||||
sfree(pf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Separately to the rportfwd tree (which is for looking up port
|
||||
* open requests from the server), a tree of _these_ structures is
|
||||
* used to keep track of all the currently open port forwardings,
|
||||
* so that we can reconfigure in mid-session if the user requests
|
||||
* it.
|
||||
*/
|
||||
struct ssh_portfwd {
|
||||
enum { DESTROY, KEEP, CREATE } status;
|
||||
int type;
|
||||
unsigned sport, dport;
|
||||
char *saddr, *daddr;
|
||||
char *sserv, *dserv;
|
||||
struct ssh_rportfwd *remote;
|
||||
int addressfamily;
|
||||
struct PortListener *local;
|
||||
};
|
||||
#define free_portfwd(pf) ( \
|
||||
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
|
||||
sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
|
||||
|
||||
static void ssh1_protocol_setup(Ssh ssh);
|
||||
static void ssh2_protocol_setup(Ssh ssh);
|
||||
static void ssh2_bare_connection_protocol_setup(Ssh ssh);
|
||||
|
@ -746,7 +726,8 @@ struct ssh_tag {
|
|||
int clean_exit;
|
||||
int disconnect_message_seen;
|
||||
|
||||
tree234 *rportfwds, *portfwds;
|
||||
tree234 *rportfwds;
|
||||
PortFwdManager *portfwdmgr;
|
||||
|
||||
enum {
|
||||
SSH_STATE_PREPACKET,
|
||||
|
@ -1063,51 +1044,6 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special form of strcmp which can cope with NULL inputs. NULL is
|
||||
* defined to sort before even the empty string.
|
||||
*/
|
||||
static int nullstrcmp(const char *a, const char *b)
|
||||
{
|
||||
if (a == NULL && b == NULL)
|
||||
return 0;
|
||||
if (a == NULL)
|
||||
return -1;
|
||||
if (b == NULL)
|
||||
return +1;
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
static int ssh_portcmp(void *av, void *bv)
|
||||
{
|
||||
struct ssh_portfwd *a = (struct ssh_portfwd *) av;
|
||||
struct ssh_portfwd *b = (struct ssh_portfwd *) bv;
|
||||
int i;
|
||||
if (a->type > b->type)
|
||||
return +1;
|
||||
if (a->type < b->type)
|
||||
return -1;
|
||||
if (a->addressfamily > b->addressfamily)
|
||||
return +1;
|
||||
if (a->addressfamily < b->addressfamily)
|
||||
return -1;
|
||||
if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
|
||||
return i < 0 ? -1 : +1;
|
||||
if (a->sport > b->sport)
|
||||
return +1;
|
||||
if (a->sport < b->sport)
|
||||
return -1;
|
||||
if (a->type != 'D') {
|
||||
if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
|
||||
return i < 0 ? -1 : +1;
|
||||
if (a->dport > b->dport)
|
||||
return +1;
|
||||
if (a->dport < b->dport)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alloc_channel_id(Ssh ssh)
|
||||
{
|
||||
const unsigned CHANNEL_NUMBER_OFFSET = 256;
|
||||
|
@ -2230,18 +2166,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
|
|||
* Go through port-forwardings, and close any associated
|
||||
* listening sockets.
|
||||
*/
|
||||
if (ssh->portfwds) {
|
||||
struct ssh_portfwd *pf;
|
||||
while (NULL != (pf = index234(ssh->portfwds, 0))) {
|
||||
/* Dispose of any listening socket. */
|
||||
if (pf->local)
|
||||
pfl_terminate(pf->local);
|
||||
del234(ssh->portfwds, pf); /* moving next one to index 0 */
|
||||
free_portfwd(pf);
|
||||
}
|
||||
freetree234(ssh->portfwds);
|
||||
ssh->portfwds = NULL;
|
||||
}
|
||||
portfwdmgr_close_all(ssh->portfwdmgr);
|
||||
|
||||
/*
|
||||
* Also stop attempting to connection-share.
|
||||
|
@ -3816,58 +3741,110 @@ static void ssh_queue_handler(Ssh ssh, int msg1, int msg2,
|
|||
|
||||
static void ssh_rportfwd_succfail(Ssh ssh, PktIn *pktin, void *ctx)
|
||||
{
|
||||
struct ssh_rportfwd *rpf, *pf = (struct ssh_rportfwd *)ctx;
|
||||
struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
|
||||
|
||||
if (pktin->type == (ssh->version == 1 ? SSH1_SMSG_SUCCESS :
|
||||
SSH2_MSG_REQUEST_SUCCESS)) {
|
||||
logeventf(ssh, "Remote port forwarding from %s enabled",
|
||||
pf->sportdesc);
|
||||
rpf->log_description);
|
||||
} else {
|
||||
logeventf(ssh, "Remote port forwarding from %s refused",
|
||||
pf->sportdesc);
|
||||
rpf->log_description);
|
||||
|
||||
rpf = del234(ssh->rportfwds, pf);
|
||||
assert(rpf == pf);
|
||||
pf->pfrec->remote = NULL;
|
||||
free_rportfwd(pf);
|
||||
struct ssh_rportfwd *realpf = del234(ssh->rportfwds, rpf);
|
||||
assert(realpf == rpf);
|
||||
portfwdmgr_close(ssh->portfwdmgr, rpf->pfr);
|
||||
free_rportfwd(rpf);
|
||||
}
|
||||
}
|
||||
|
||||
int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
|
||||
ssh_sharing_connstate *share_ctx)
|
||||
struct ssh_rportfwd *ssh_rportfwd_alloc(
|
||||
Ssh ssh, const char *shost, int sport, const char *dhost, int dport,
|
||||
int addressfamily, const char *log_description, PortFwdRecord *pfr,
|
||||
ssh_sharing_connstate *share_ctx)
|
||||
{
|
||||
struct ssh_rportfwd *pf = snew(struct ssh_rportfwd);
|
||||
pf->dhost = NULL;
|
||||
pf->dport = 0;
|
||||
pf->share_ctx = share_ctx;
|
||||
pf->shost = dupstr(shost);
|
||||
pf->sport = sport;
|
||||
pf->sportdesc = NULL;
|
||||
/*
|
||||
* Ensure the remote port forwardings tree exists.
|
||||
*/
|
||||
if (!ssh->rportfwds) {
|
||||
assert(ssh->version == 2);
|
||||
ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
|
||||
if (ssh->version == 1)
|
||||
ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
|
||||
else
|
||||
ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
|
||||
}
|
||||
if (add234(ssh->rportfwds, pf) != pf) {
|
||||
sfree(pf->shost);
|
||||
sfree(pf);
|
||||
return FALSE;
|
||||
|
||||
struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd);
|
||||
|
||||
rpf->shost = dupstr(shost);
|
||||
rpf->sport = sport;
|
||||
rpf->dhost = dupstr(dhost);
|
||||
rpf->dport = dport;
|
||||
rpf->addressfamily = addressfamily;
|
||||
rpf->log_description = dupstr(log_description);
|
||||
rpf->pfr = pfr;
|
||||
rpf->share_ctx = share_ctx;
|
||||
|
||||
if (add234(ssh->rportfwds, rpf) != rpf) {
|
||||
free_rportfwd(rpf);
|
||||
return NULL;
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
if (!rpf->share_ctx) {
|
||||
PktOut *pktout;
|
||||
|
||||
if (ssh->version == 1) {
|
||||
pktout = ssh_bpp_new_pktout(
|
||||
ssh->bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
|
||||
put_uint32(pktout, rpf->sport);
|
||||
put_stringz(pktout, rpf->dhost);
|
||||
put_uint32(pktout, rpf->dport);
|
||||
ssh_pkt_write(ssh, pktout);
|
||||
ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
|
||||
SSH1_SMSG_FAILURE,
|
||||
ssh_rportfwd_succfail, rpf);
|
||||
} else {
|
||||
pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
|
||||
put_stringz(pktout, "tcpip-forward");
|
||||
put_bool(pktout, 1); /* want reply */
|
||||
put_stringz(pktout, rpf->shost);
|
||||
put_uint32(pktout, rpf->sport);
|
||||
ssh2_pkt_send(ssh, pktout);
|
||||
|
||||
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
|
||||
SSH2_MSG_REQUEST_FAILURE,
|
||||
ssh_rportfwd_succfail, rpf);
|
||||
}
|
||||
}
|
||||
|
||||
return rpf;
|
||||
}
|
||||
|
||||
void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
|
||||
ssh_sharing_connstate *share_ctx)
|
||||
void ssh_rportfwd_remove(Ssh ssh, struct ssh_rportfwd *rpf)
|
||||
{
|
||||
struct ssh_rportfwd pf, *realpf;
|
||||
if (ssh->version == 1) {
|
||||
/*
|
||||
* We cannot cancel listening ports on the server side in
|
||||
* SSH-1! There's no message to support it.
|
||||
*/
|
||||
} else if (rpf->share_ctx) {
|
||||
/*
|
||||
* We don't manufacture a cancel-tcpip-forward message for
|
||||
* remote port forwardings being removed on behalf of a
|
||||
* downstream; we just pass through the one the downstream
|
||||
* sent to us.
|
||||
*/
|
||||
} else {
|
||||
PktOut *pktout = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
|
||||
put_stringz(pktout, "cancel-tcpip-forward");
|
||||
put_bool(pktout, 0); /* _don't_ want reply */
|
||||
put_stringz(pktout, rpf->shost);
|
||||
put_uint32(pktout, rpf->sport);
|
||||
ssh2_pkt_send(ssh, pktout);
|
||||
}
|
||||
|
||||
assert(ssh->rportfwds);
|
||||
pf.shost = dupstr(shost);
|
||||
pf.sport = sport;
|
||||
realpf = del234(ssh->rportfwds, &pf);
|
||||
assert(realpf);
|
||||
assert(realpf->share_ctx == share_ctx);
|
||||
sfree(realpf->shost);
|
||||
sfree(realpf);
|
||||
struct ssh_rportfwd *realpf = del234(ssh->rportfwds, rpf);
|
||||
assert(realpf == rpf);
|
||||
free_rportfwd(rpf);
|
||||
}
|
||||
|
||||
static void ssh_sharing_global_request_response(Ssh ssh, PktIn *pktin,
|
||||
|
@ -3885,334 +3862,6 @@ void ssh_sharing_queue_global_request(Ssh ssh,
|
|||
ssh_sharing_global_request_response, share_ctx);
|
||||
}
|
||||
|
||||
static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
|
||||
{
|
||||
struct ssh_portfwd *epf;
|
||||
int i;
|
||||
char *key, *val;
|
||||
|
||||
if (!ssh->portfwds) {
|
||||
ssh->portfwds = newtree234(ssh_portcmp);
|
||||
} else {
|
||||
/*
|
||||
* Go through the existing port forwardings and tag them
|
||||
* with status==DESTROY. Any that we want to keep will be
|
||||
* re-enabled (status==KEEP) as we go through the
|
||||
* configuration and find out which bits are the same as
|
||||
* they were before.
|
||||
*/
|
||||
struct ssh_portfwd *epf;
|
||||
int i;
|
||||
for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
|
||||
epf->status = DESTROY;
|
||||
}
|
||||
|
||||
for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
|
||||
val != NULL;
|
||||
val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
|
||||
char *kp, *kp2, *vp, *vp2;
|
||||
char address_family, type;
|
||||
int sport,dport,sserv,dserv;
|
||||
char *sports, *dports, *saddr, *host;
|
||||
|
||||
kp = key;
|
||||
|
||||
address_family = 'A';
|
||||
type = 'L';
|
||||
if (*kp == 'A' || *kp == '4' || *kp == '6')
|
||||
address_family = *kp++;
|
||||
if (*kp == 'L' || *kp == 'R')
|
||||
type = *kp++;
|
||||
|
||||
if ((kp2 = host_strchr(kp, ':')) != NULL) {
|
||||
/*
|
||||
* There's a colon in the middle of the source port
|
||||
* string, which means that the part before it is
|
||||
* actually a source address.
|
||||
*/
|
||||
char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp);
|
||||
saddr = host_strduptrim(saddr_tmp);
|
||||
sfree(saddr_tmp);
|
||||
sports = kp2+1;
|
||||
} else {
|
||||
saddr = NULL;
|
||||
sports = kp;
|
||||
}
|
||||
sport = atoi(sports);
|
||||
sserv = 0;
|
||||
if (sport == 0) {
|
||||
sserv = 1;
|
||||
sport = net_service_lookup(sports);
|
||||
if (!sport) {
|
||||
logeventf(ssh, "Service lookup failed for source"
|
||||
" port \"%s\"", sports);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == 'L' && !strcmp(val, "D")) {
|
||||
/* dynamic forwarding */
|
||||
host = NULL;
|
||||
dports = NULL;
|
||||
dport = -1;
|
||||
dserv = 0;
|
||||
type = 'D';
|
||||
} else {
|
||||
/* ordinary forwarding */
|
||||
vp = val;
|
||||
vp2 = vp + host_strcspn(vp, ":");
|
||||
host = dupprintf("%.*s", (int)(vp2 - vp), vp);
|
||||
if (*vp2)
|
||||
vp2++;
|
||||
dports = vp2;
|
||||
dport = atoi(dports);
|
||||
dserv = 0;
|
||||
if (dport == 0) {
|
||||
dserv = 1;
|
||||
dport = net_service_lookup(dports);
|
||||
if (!dport) {
|
||||
logeventf(ssh, "Service lookup failed for destination"
|
||||
" port \"%s\"", dports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sport && dport) {
|
||||
/* Set up a description of the source port. */
|
||||
struct ssh_portfwd *pfrec, *epfrec;
|
||||
|
||||
pfrec = snew(struct ssh_portfwd);
|
||||
pfrec->type = type;
|
||||
pfrec->saddr = saddr;
|
||||
pfrec->sserv = sserv ? dupstr(sports) : NULL;
|
||||
pfrec->sport = sport;
|
||||
pfrec->daddr = host;
|
||||
pfrec->dserv = dserv ? dupstr(dports) : NULL;
|
||||
pfrec->dport = dport;
|
||||
pfrec->local = NULL;
|
||||
pfrec->remote = NULL;
|
||||
pfrec->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :
|
||||
address_family == '6' ? ADDRTYPE_IPV6 :
|
||||
ADDRTYPE_UNSPEC);
|
||||
|
||||
epfrec = add234(ssh->portfwds, pfrec);
|
||||
if (epfrec != pfrec) {
|
||||
if (epfrec->status == DESTROY) {
|
||||
/*
|
||||
* We already have a port forwarding up and running
|
||||
* with precisely these parameters. Hence, no need
|
||||
* to do anything; simply re-tag the existing one
|
||||
* as KEEP.
|
||||
*/
|
||||
epfrec->status = KEEP;
|
||||
}
|
||||
/*
|
||||
* Anything else indicates that there was a duplicate
|
||||
* in our input, which we'll silently ignore.
|
||||
*/
|
||||
free_portfwd(pfrec);
|
||||
} else {
|
||||
pfrec->status = CREATE;
|
||||
}
|
||||
} else {
|
||||
sfree(saddr);
|
||||
sfree(host);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now go through and destroy any port forwardings which were
|
||||
* not re-enabled.
|
||||
*/
|
||||
for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
|
||||
if (epf->status == DESTROY) {
|
||||
char *message;
|
||||
|
||||
message = dupprintf("%s port forwarding from %s%s%d",
|
||||
epf->type == 'L' ? "local" :
|
||||
epf->type == 'R' ? "remote" : "dynamic",
|
||||
epf->saddr ? epf->saddr : "",
|
||||
epf->saddr ? ":" : "",
|
||||
epf->sport);
|
||||
|
||||
if (epf->type != 'D') {
|
||||
char *msg2 = dupprintf("%s to %s:%d", message,
|
||||
epf->daddr, epf->dport);
|
||||
sfree(message);
|
||||
message = msg2;
|
||||
}
|
||||
|
||||
logeventf(ssh, "Cancelling %s", message);
|
||||
sfree(message);
|
||||
|
||||
/* epf->remote or epf->local may be NULL if setting up a
|
||||
* forwarding failed. */
|
||||
if (epf->remote) {
|
||||
struct ssh_rportfwd *rpf = epf->remote;
|
||||
PktOut *pktout;
|
||||
|
||||
/*
|
||||
* Cancel the port forwarding at the server
|
||||
* end.
|
||||
*/
|
||||
if (ssh->version == 1) {
|
||||
/*
|
||||
* We cannot cancel listening ports on the
|
||||
* server side in SSH-1! There's no message
|
||||
* to support it. Instead, we simply remove
|
||||
* the rportfwd record from the local end
|
||||
* so that any connections the server tries
|
||||
* to make on it are rejected.
|
||||
*/
|
||||
} else {
|
||||
pktout = ssh_bpp_new_pktout(
|
||||
ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
|
||||
put_stringz(pktout, "cancel-tcpip-forward");
|
||||
put_bool(pktout, 0);/* _don't_ want reply */
|
||||
if (epf->saddr) {
|
||||
put_stringz(pktout, epf->saddr);
|
||||
} else if (conf_get_int(conf, CONF_rport_acceptall)) {
|
||||
/* XXX: rport_acceptall may not represent
|
||||
* what was used to open the original connection,
|
||||
* since it's reconfigurable. */
|
||||
put_stringz(pktout, "");
|
||||
} else {
|
||||
put_stringz(pktout, "localhost");
|
||||
}
|
||||
put_uint32(pktout, epf->sport);
|
||||
ssh2_pkt_send(ssh, pktout);
|
||||
}
|
||||
|
||||
del234(ssh->rportfwds, rpf);
|
||||
free_rportfwd(rpf);
|
||||
} else if (epf->local) {
|
||||
pfl_terminate(epf->local);
|
||||
}
|
||||
|
||||
delpos234(ssh->portfwds, i);
|
||||
free_portfwd(epf);
|
||||
i--; /* so we don't skip one in the list */
|
||||
}
|
||||
|
||||
/*
|
||||
* And finally, set up any new port forwardings (status==CREATE).
|
||||
*/
|
||||
for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
|
||||
if (epf->status == CREATE) {
|
||||
char *sportdesc, *dportdesc;
|
||||
sportdesc = dupprintf("%s%s%s%s%d%s",
|
||||
epf->saddr ? epf->saddr : "",
|
||||
epf->saddr ? ":" : "",
|
||||
epf->sserv ? epf->sserv : "",
|
||||
epf->sserv ? "(" : "",
|
||||
epf->sport,
|
||||
epf->sserv ? ")" : "");
|
||||
if (epf->type == 'D') {
|
||||
dportdesc = NULL;
|
||||
} else {
|
||||
dportdesc = dupprintf("%s:%s%s%d%s",
|
||||
epf->daddr,
|
||||
epf->dserv ? epf->dserv : "",
|
||||
epf->dserv ? "(" : "",
|
||||
epf->dport,
|
||||
epf->dserv ? ")" : "");
|
||||
}
|
||||
|
||||
if (epf->type == 'L') {
|
||||
char *err = pfl_listen(epf->daddr, epf->dport,
|
||||
epf->saddr, epf->sport,
|
||||
ssh, conf, &epf->local,
|
||||
epf->addressfamily);
|
||||
|
||||
logeventf(ssh, "Local %sport %s forwarding to %s%s%s",
|
||||
epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
||||
epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
||||
sportdesc, dportdesc,
|
||||
err ? " failed: " : "", err ? err : "");
|
||||
if (err)
|
||||
sfree(err);
|
||||
} else if (epf->type == 'D') {
|
||||
char *err = pfl_listen(NULL, -1, epf->saddr, epf->sport,
|
||||
ssh, conf, &epf->local,
|
||||
epf->addressfamily);
|
||||
|
||||
logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s",
|
||||
epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
|
||||
epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
|
||||
sportdesc,
|
||||
err ? " failed: " : "", err ? err : "");
|
||||
|
||||
if (err)
|
||||
sfree(err);
|
||||
} else {
|
||||
struct ssh_rportfwd *pf;
|
||||
|
||||
/*
|
||||
* Ensure the remote port forwardings tree exists.
|
||||
*/
|
||||
if (!ssh->rportfwds) {
|
||||
if (ssh->version == 1)
|
||||
ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
|
||||
else
|
||||
ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
|
||||
}
|
||||
|
||||
pf = snew(struct ssh_rportfwd);
|
||||
pf->share_ctx = NULL;
|
||||
pf->dhost = dupstr(epf->daddr);
|
||||
pf->dport = epf->dport;
|
||||
if (epf->saddr) {
|
||||
pf->shost = dupstr(epf->saddr);
|
||||
} else if (conf_get_int(conf, CONF_rport_acceptall)) {
|
||||
pf->shost = dupstr("");
|
||||
} else {
|
||||
pf->shost = dupstr("localhost");
|
||||
}
|
||||
pf->sport = epf->sport;
|
||||
if (add234(ssh->rportfwds, pf) != pf) {
|
||||
logeventf(ssh, "Duplicate remote port forwarding to %s:%d",
|
||||
epf->daddr, epf->dport);
|
||||
sfree(pf);
|
||||
} else {
|
||||
PktOut *pktout;
|
||||
|
||||
logeventf(ssh, "Requesting remote port %s"
|
||||
" forward to %s", sportdesc, dportdesc);
|
||||
|
||||
pf->sportdesc = sportdesc;
|
||||
sportdesc = NULL;
|
||||
epf->remote = pf;
|
||||
pf->pfrec = epf;
|
||||
|
||||
if (ssh->version == 1) {
|
||||
pktout = ssh_bpp_new_pktout(
|
||||
ssh->bpp, SSH1_CMSG_PORT_FORWARD_REQUEST);
|
||||
put_uint32(pktout, epf->sport);
|
||||
put_stringz(pktout, epf->daddr);
|
||||
put_uint32(pktout, epf->dport);
|
||||
ssh_pkt_write(ssh, pktout);
|
||||
ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
|
||||
SSH1_SMSG_FAILURE,
|
||||
ssh_rportfwd_succfail, pf);
|
||||
} else {
|
||||
pktout = ssh_bpp_new_pktout(
|
||||
ssh->bpp, SSH2_MSG_GLOBAL_REQUEST);
|
||||
put_stringz(pktout, "tcpip-forward");
|
||||
put_bool(pktout, 1);/* want reply */
|
||||
put_stringz(pktout, pf->shost);
|
||||
put_uint32(pktout, pf->sport);
|
||||
ssh2_pkt_send(ssh, pktout);
|
||||
|
||||
ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
|
||||
SSH2_MSG_REQUEST_FAILURE,
|
||||
ssh_rportfwd_succfail, pf);
|
||||
}
|
||||
}
|
||||
}
|
||||
sfree(sportdesc);
|
||||
sfree(dportdesc);
|
||||
}
|
||||
}
|
||||
|
||||
static void ssh1_smsg_stdout_stderr_data(Ssh ssh, PktIn *pktin)
|
||||
{
|
||||
ptrlen string;
|
||||
|
@ -4321,8 +3970,8 @@ static void ssh1_msg_port_open(Ssh ssh, PktIn *pktin)
|
|||
|
||||
logeventf(ssh, "Received remote port open request for %s:%d",
|
||||
pf.dhost, port);
|
||||
err = pfd_connect(&c->chan, pf.dhost, port,
|
||||
&c->sc, ssh->conf, pfp->pfrec->addressfamily);
|
||||
err = portfwdmgr_connect(ssh->portfwdmgr, &c->chan, pf.dhost, port,
|
||||
&c->sc, pfp->addressfamily);
|
||||
if (err != NULL) {
|
||||
logeventf(ssh, "Port open failed: %s", err);
|
||||
sfree(err);
|
||||
|
@ -4562,7 +4211,7 @@ static void do_ssh1_connection(void *vctx)
|
|||
}
|
||||
}
|
||||
|
||||
ssh_setup_portfwd(ssh, ssh->conf);
|
||||
portfwdmgr_config(ssh->portfwdmgr, ssh->conf);
|
||||
ssh->packet_dispatch[SSH1_MSG_PORT_OPEN] = ssh1_msg_port_open;
|
||||
|
||||
if (!conf_get_int(ssh->conf, CONF_nopty)) {
|
||||
|
@ -7858,8 +7507,9 @@ static void ssh2_msg_channel_open(Ssh ssh, PktIn *pktin)
|
|||
return;
|
||||
}
|
||||
|
||||
err = pfd_connect(&c->chan, realpf->dhost, realpf->dport,
|
||||
&c->sc, ssh->conf, realpf->pfrec->addressfamily);
|
||||
err = portfwdmgr_connect(
|
||||
ssh->portfwdmgr, &c->chan, realpf->dhost, realpf->dport,
|
||||
&c->sc, realpf->addressfamily);
|
||||
logeventf(ssh, "Attempting to forward remote port to "
|
||||
"%s:%d", realpf->dhost, realpf->dport);
|
||||
if (err != NULL) {
|
||||
|
@ -9925,7 +9575,7 @@ static void do_ssh2_connection(void *vctx)
|
|||
/*
|
||||
* Enable port forwardings.
|
||||
*/
|
||||
ssh_setup_portfwd(ssh, ssh->conf);
|
||||
portfwdmgr_config(ssh->portfwdmgr, ssh->conf);
|
||||
|
||||
if (ssh->mainchan && !ssh->ncmode) {
|
||||
/*
|
||||
|
@ -10703,7 +10353,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle,
|
|||
|
||||
ssh->channels = NULL;
|
||||
ssh->rportfwds = NULL;
|
||||
ssh->portfwds = NULL;
|
||||
ssh->portfwdmgr = portfwdmgr_new(ssh);
|
||||
|
||||
ssh->send_ok = 0;
|
||||
ssh->editing = 0;
|
||||
|
@ -10798,6 +10448,7 @@ static void ssh_free(Backend *be)
|
|||
freetree234(ssh->rportfwds);
|
||||
ssh->rportfwds = NULL;
|
||||
}
|
||||
portfwdmgr_free(ssh->portfwdmgr);
|
||||
if (ssh->x11disp)
|
||||
x11_free_display(ssh->x11disp);
|
||||
while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
|
||||
|
@ -10864,8 +10515,7 @@ static void ssh_reconfig(Backend *be, Conf *conf)
|
|||
int i, rekey_time;
|
||||
|
||||
pinger_reconfig(ssh->pinger, ssh->conf, conf);
|
||||
if (ssh->portfwds)
|
||||
ssh_setup_portfwd(ssh, conf);
|
||||
portfwdmgr_config(ssh->portfwdmgr, conf);
|
||||
|
||||
rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
|
||||
if (ssh2_timer_update(ssh, rekey_mins(rekey_time, 60)))
|
||||
|
|
33
ssh.h
33
ssh.h
|
@ -149,10 +149,6 @@ void ssh_connshare_log(Ssh ssh, int event, const char *logtext,
|
|||
const char *ds_err, const char *us_err);
|
||||
unsigned ssh_alloc_sharing_channel(Ssh ssh, ssh_sharing_connstate *connstate);
|
||||
void ssh_delete_sharing_channel(Ssh ssh, unsigned localid);
|
||||
int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
|
||||
ssh_sharing_connstate *connstate);
|
||||
void ssh_remove_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
|
||||
ssh_sharing_connstate *connstate);
|
||||
void ssh_sharing_queue_global_request(
|
||||
Ssh ssh, ssh_sharing_connstate *connstate);
|
||||
struct X11FakeAuth *ssh_sharing_add_x11_display(
|
||||
|
@ -175,6 +171,23 @@ void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
|
|||
int protomajor, int protominor,
|
||||
const void *initial_data, int initial_len);
|
||||
|
||||
struct ssh_rportfwd;
|
||||
struct ssh_rportfwd *ssh_rportfwd_alloc(
|
||||
Ssh ssh, const char *shost, int sport, const char *dhost, int dport,
|
||||
int addressfamily, const char *log_description, PortFwdRecord *pfr,
|
||||
ssh_sharing_connstate *share_ctx);
|
||||
void ssh_rportfwd_remove(Ssh ssh, struct ssh_rportfwd *rpf);
|
||||
|
||||
/* Exports from portfwd.c */
|
||||
PortFwdManager *portfwdmgr_new(Ssh ssh);
|
||||
void portfwdmgr_free(PortFwdManager *mgr);
|
||||
void portfwdmgr_config(PortFwdManager *mgr, Conf *conf);
|
||||
void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr);
|
||||
void portfwdmgr_close_all(PortFwdManager *mgr);
|
||||
char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
|
||||
char *hostname, int port, SshChannel *c,
|
||||
int addressfamily);
|
||||
|
||||
Frontend *ssh_get_frontend(Ssh ssh);
|
||||
|
||||
#define SSH_CIPHER_IDEA 1
|
||||
|
@ -718,22 +731,10 @@ void random_add_heavynoise(void *noise, int length);
|
|||
|
||||
void logevent(Frontend *, const char *);
|
||||
|
||||
struct PortForwarding;
|
||||
|
||||
/* Allocate and register a new channel for port forwarding */
|
||||
SshChannel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
|
||||
const char *org, Channel *chan);
|
||||
|
||||
/* Exports from portfwd.c */
|
||||
extern char *pfd_connect(Channel **chan_ret, char *hostname, int port,
|
||||
SshChannel *c, Conf *conf, int addressfamily);
|
||||
struct PortListener;
|
||||
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
|
||||
extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
||||
int port, Ssh ssh, Conf *conf,
|
||||
struct PortListener **pl, int address_family);
|
||||
extern void pfl_terminate(struct PortListener *);
|
||||
|
||||
/* Exports from x11fwd.c */
|
||||
enum {
|
||||
X11_TRANS_IPV4 = 0, X11_TRANS_IPV6 = 6, X11_TRANS_UNIX = 256
|
||||
|
|
16
sshshare.c
16
sshshare.c
|
@ -242,6 +242,7 @@ struct share_forwarding {
|
|||
char *host;
|
||||
int port;
|
||||
int active; /* has the server sent REQUEST_SUCCESS? */
|
||||
struct ssh_rportfwd *rpf;
|
||||
};
|
||||
|
||||
struct share_xchannel_message {
|
||||
|
@ -856,8 +857,7 @@ static void share_try_cleanup(struct ssh_sharing_connstate *cs)
|
|||
"cleanup after downstream went away");
|
||||
strbuf_free(packet);
|
||||
|
||||
ssh_remove_sharing_rportfwd(cs->parent->ssh,
|
||||
fwd->host, fwd->port, cs);
|
||||
ssh_rportfwd_remove(cs->parent->ssh, fwd->rpf);
|
||||
share_remove_forwarding(cs, fwd);
|
||||
i--; /* don't accidentally skip one as a result */
|
||||
}
|
||||
|
@ -1306,7 +1306,8 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
|
|||
if (ptrlen_eq_string(request_name, "tcpip-forward")) {
|
||||
ptrlen hostpl;
|
||||
char *host;
|
||||
int port, ret;
|
||||
int port;
|
||||
struct ssh_rportfwd *rpf;
|
||||
|
||||
/*
|
||||
* Pick the packet apart to find the want_reply field and
|
||||
|
@ -1328,8 +1329,9 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
|
|||
* ourselves to manufacture a failure packet and send it
|
||||
* back to downstream.
|
||||
*/
|
||||
ret = ssh_alloc_sharing_rportfwd(cs->parent->ssh, host, port, cs);
|
||||
if (!ret) {
|
||||
rpf = ssh_rportfwd_alloc(
|
||||
cs->parent->ssh, host, port, NULL, 0, 0, NULL, NULL, cs);
|
||||
if (!rpf) {
|
||||
if (orig_wantreply) {
|
||||
send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
|
||||
"", 0, NULL);
|
||||
|
@ -1359,6 +1361,8 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
|
|||
globreq->fwd = fwd;
|
||||
globreq->want_reply = orig_wantreply;
|
||||
globreq->type = GLOBREQ_TCPIP_FORWARD;
|
||||
|
||||
fwd->rpf = rpf;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1395,7 +1399,7 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
|
|||
* Tell ssh.c to stop sending us channel-opens for
|
||||
* this forwarding.
|
||||
*/
|
||||
ssh_remove_sharing_rportfwd(cs->parent->ssh, host, port, cs);
|
||||
ssh_rportfwd_remove(cs->parent->ssh, fwd->rpf);
|
||||
|
||||
/*
|
||||
* Pass the cancel request on to the SSH server, but
|
||||
|
|
Загрузка…
Ссылка в новой задаче