+ if (pktin->type == (ssh->version == 1 ? SSH1_SMSG_SUCCESS :
+ SSH2_MSG_REQUEST_SUCCESS)) {
+ logeventf(ssh, "Remote port forwarding from %s enabled",
+ pf->sportdesc);
+ } else {
+ logeventf(ssh, "Remote port forwarding from %s refused",
+ pf->sportdesc);
+
+ rpf = del234(ssh->rportfwds, pf);
+ assert(rpf == pf);
+ pf->pfrec->remote = NULL;
+ free_rportfwd(pf);
+ }
+}
+
+int ssh_alloc_sharing_rportfwd(Ssh ssh, const char *shost, int sport,
+ void *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;
+ if (!ssh->rportfwds) {
+ assert(ssh->version == 2);
+ ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
+ }
+ if (add234(ssh->rportfwds, pf) != pf) {
+ sfree(pf->shost);
+ sfree(pf);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void ssh_sharing_global_request_response(Ssh ssh, struct Packet *pktin,
+ void *ctx)
+{
+ share_got_pkt_from_server(ctx, pktin->type,
+ pktin->body, pktin->length);
+}
+
+void ssh_sharing_queue_global_request(Ssh ssh, void *share_ctx)
+{
+ ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, SSH2_MSG_REQUEST_FAILURE,
+ 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;
+ struct Packet *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 = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ ssh2_pkt_addstring(pktout, "cancel-tcpip-forward");
+ ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */
+ if (epf->saddr) {
+ ssh2_pkt_addstring(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. */
+ ssh2_pkt_addstring(pktout, "");
+ } else {
+ ssh2_pkt_addstring(pktout, "localhost");
+ }
+ ssh2_pkt_adduint32(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 {
+ 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) {
+ send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,
+ PKT_INT, epf->sport,
+ PKT_STR, epf->daddr,
+ PKT_INT, epf->dport,
+ PKT_END);
+ ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS,
+ SSH1_SMSG_FAILURE,
+ ssh_rportfwd_succfail, pf);
+ } else {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ ssh2_pkt_addstring(pktout, "tcpip-forward");
+ ssh2_pkt_addbool(pktout, 1);/* want reply */
+ ssh2_pkt_addstring(pktout, pf->shost);
+ ssh2_pkt_adduint32(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, struct Packet *pktin)
+{
+ char *string;
+ int stringlen, bufsize;
+
+ ssh_pkt_getstring(pktin, &string, &stringlen);
+ if (string == NULL) {
+ bombout(("Incoming terminal data packet was badly formed"));
+ return;
+ }
+
+ bufsize = from_backend(ssh->frontend, pktin->type == SSH1_SMSG_STDERR_DATA,
+ string, stringlen);
+ if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
+ ssh->v1_stdout_throttling = 1;
+ ssh_throttle_conn(ssh, +1);
+ }
+}
+
+static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
+{
+ /* Remote side is trying to open a channel to talk to our
+ * X-Server. Give them back a local channel number. */
+ struct ssh_channel *c;
+ int remoteid = ssh_pkt_getuint32(pktin);
+
+ logevent("Received X11 connect request");
+ /* Refuse if X11 forwarding is disabled. */
+ if (!ssh->X11_fwd_enabled) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, remoteid, PKT_END);
+ logevent("Rejected X11 connect request");
+ } else {
+ c = snew(struct ssh_channel);
+ c->ssh = ssh;
+
+ c->u.x11.xconn = x11_init(ssh->x11authtree, c, NULL, -1);
+ c->remoteid = remoteid;
+ c->halfopen = FALSE;
+ c->localid = alloc_channel_id(ssh);
+ c->closes = 0;
+ c->pending_eof = FALSE;
+ c->throttling_conn = 0;
+ c->type = CHAN_X11; /* identify channel type */
+ add234(ssh->channels, c);
+ send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+ PKT_INT, c->remoteid, PKT_INT,
+ c->localid, PKT_END);
+ logevent("Opened X11 forward channel");
+ }
+}
+
+static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
+{
+ /* Remote side is trying to open a channel to talk to our
+ * agent. Give them back a local channel number. */
+ struct ssh_channel *c;
+ int remoteid = ssh_pkt_getuint32(pktin);
+
+ /* Refuse if agent forwarding is disabled. */
+ if (!ssh->agentfwd_enabled) {
+ send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+ PKT_INT, remoteid, PKT_END);