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:
Simon Tatham 2018-09-14 17:04:39 +01:00
Родитель aa08e6ca91
Коммит 895b09a4c6
7 изменённых файлов: 597 добавлений и 539 удалений

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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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
Просмотреть файл

@ -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

Просмотреть файл

@ -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