diff options
author | jp161948 <none@none> | 2007-10-26 07:29:16 -0700 |
---|---|---|
committer | jp161948 <none@none> | 2007-10-26 07:29:16 -0700 |
commit | 9b03ea0f916d36e6ec001b90683afcaee8e29a40 (patch) | |
tree | c15358939ef4c4b1318e3f463c0482391fbfb094 /usr/src/cmd/ssh/libssh/common | |
parent | 6f1fe22336d824f58bda6680410a6b1c2a72ea2d (diff) | |
download | illumos-gate-9b03ea0f916d36e6ec001b90683afcaee8e29a40.tar.gz |
PSARC/2007/610 ssh(1) binding address for port forwarding
6506674 allow specific binding address to be used with -LRD options for ssh(1)
6619347 SunSSH is not fully compatible with RFC4254 with regard to port forwarding
Diffstat (limited to 'usr/src/cmd/ssh/libssh/common')
-rw-r--r-- | usr/src/cmd/ssh/libssh/common/channels.c | 161 | ||||
-rw-r--r-- | usr/src/cmd/ssh/libssh/common/compat.c | 34 | ||||
-rw-r--r-- | usr/src/cmd/ssh/libssh/common/misc.c | 42 | ||||
-rw-r--r-- | usr/src/cmd/ssh/libssh/common/readconf.c | 165 |
4 files changed, 328 insertions, 74 deletions
diff --git a/usr/src/cmd/ssh/libssh/common/channels.c b/usr/src/cmd/ssh/libssh/common/channels.c index 78c5a72f1f..03720994e3 100644 --- a/usr/src/cmd/ssh/libssh/common/channels.c +++ b/usr/src/cmd/ssh/libssh/common/channels.c @@ -1,8 +1,4 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ -/* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * All rights reserved @@ -41,6 +37,10 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ #include "includes.h" RCSID("$OpenBSD: channels.c,v 1.183 2002/09/17 07:47:02 itojun Exp $"); @@ -2165,35 +2165,77 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por const char *host_to_connect, u_short port_to_connect, int gateway_ports) { Channel *c; - int success, sock, on = 1; + int sock, r, is_client, on = 1, wildcard = 0, success = 0; struct addrinfo hints, *ai, *aitop; - const char *host; + const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - success = 0; host = (type == SSH_CHANNEL_RPORT_LISTENER) ? listen_addr : host_to_connect; + is_client = (type == SSH_CHANNEL_PORT_LISTENER); if (host == NULL) { error("No forward host name."); - return success; + return 0; } if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { error("Forward host name too long."); - return success; + return 0; } /* + * Determine whether or not a port forward listens to loopback, + * specified address or wildcard. On the client, a specified bind + * address will always override gateway_ports. On the server, a + * gateway_ports of 1 (``yes'') will override the client's + * specification and force a wildcard bind, whereas a value of 2 + * (``clientspecified'') will bind to whatever address the client + * asked for. + * + * Special-case listen_addrs are: + * + * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR + * "" (empty string), "*" -> wildcard v4/v6 + * "localhost" -> loopback v4/v6 + */ + addr = NULL; + if (listen_addr == NULL) { + /* No address specified: default to gateway_ports setting */ + if (gateway_ports) + wildcard = 1; + } else if (gateway_ports || is_client) { + if (((datafellows & SSH_OLD_FORWARD_ADDR) && + strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || + *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || + (!is_client && gateway_ports == 1)) + wildcard = 1; + else if (strcmp(listen_addr, "localhost") != 0) + addr = listen_addr; + } + + debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s", + type, wildcard, (addr == NULL) ? "NULL" : addr); + + /* * getaddrinfo returns a loopback address if the hostname is * set to NULL and hints.ai_flags is not AI_PASSIVE */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; - hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; + hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", listen_port); - if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) - packet_disconnect("getaddrinfo: fatal error"); + if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { + if (addr == NULL) { + /* This really shouldn't happen */ + packet_disconnect("getaddrinfo: fatal error: %s", + gai_strerror(r)); + } else { + error("channel_setup_fwd_listener: " + "getaddrinfo(%.64s): %s", addr, gai_strerror(r)); + } + return 0; + } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) @@ -2204,7 +2246,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por continue; } /* Create a port to listen for the host. */ - sock = socket(ai->ai_family, SOCK_STREAM, 0); + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { /* this is no error since kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); @@ -2253,13 +2295,35 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por return success; } +int +channel_cancel_rport_listener(const char *host, u_short port) +{ + u_int i; + int found = 0; + + for (i = 0; i < channels_alloc; i++) { + Channel *c = channels[i]; + + if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && + strncmp(c->path, host, sizeof(c->path)) == 0 && + c->listening_port == port) { + debug2("%s: close channel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + /* protocol local port fwd, used by ssh (and sshd in v1) */ int -channel_setup_local_fwd_listener(u_short listen_port, +channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect, int gateway_ports) { return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, - NULL, listen_port, host_to_connect, port_to_connect, gateway_ports); + listen_host, listen_port, host_to_connect, port_to_connect, + gateway_ports); } /* protocol v2 remote port fwd, used by sshd */ @@ -2276,8 +2340,8 @@ channel_setup_remote_fwd_listener(const char *listen_address, * the secure channel to host:port from local side. */ -void -channel_request_remote_forwarding(u_short listen_port, +int +channel_request_remote_forwarding(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect) { int type, success = 0; @@ -2286,9 +2350,29 @@ channel_request_remote_forwarding(u_short listen_port, if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("channel_request_remote_forwarding: too many forwards"); + if (listen_host != NULL && + strlen(listen_host) > SSH_CHANNEL_PATH_LEN - 1) { + error("Binding address too long."); + return -1; + } + /* Send the forward request to the remote side. */ if (compat20) { - const char *address_to_bind = "0.0.0.0"; + const char *address_to_bind; + if (listen_host == NULL) { + if (datafellows & SSH_BUG_RFWD_ADDR) + address_to_bind = "127.0.0.1"; + else + address_to_bind = "localhost"; + } else if (*listen_host == '\0' || + strcmp(listen_host, "*") == 0) { + if (datafellows & SSH_BUG_RFWD_ADDR) + address_to_bind = "0.0.0.0"; + else + address_to_bind = ""; + } else + address_to_bind = listen_host; + packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("tcpip-forward"); packet_put_char(1); /* boolean: want reply */ @@ -2327,6 +2411,41 @@ channel_request_remote_forwarding(u_short listen_port, permitted_opens[num_permitted_opens].listen_port = listen_port; num_permitted_opens++; } + return (success ? 0 : -1); +} + +/* + * Request cancellation of remote forwarding of connection host:port from + * local side. + */ +void +channel_request_rforward_cancel(const char *host, u_short port) +{ + int i; + + if (!compat20) + return; + + for (i = 0; i < num_permitted_opens; i++) { + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == port) + break; + } + if (i >= num_permitted_opens) { + debug("%s: requested forward not found", __func__); + return; + } + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-tcpip-forward"); + packet_put_char(0); + packet_put_cstring(host == NULL ? "" : host); + packet_put_int(port); + packet_send(); + + permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; + xfree(permitted_opens[i].host_to_connect); + permitted_opens[i].host_to_connect = NULL; } /* @@ -2356,7 +2475,8 @@ channel_input_port_forward_request(int is_root, int gateway_ports) port); #endif /* Initiate forwarding */ - channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports); + channel_setup_local_fwd_listener(NULL, port, hostname, + host_port, gateway_ports); /* Free the argument string. */ xfree(hostname); @@ -2378,7 +2498,7 @@ void channel_add_permitted_opens(char *host, int port) { if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) - fatal("channel_request_remote_forwarding: too many forwards"); + fatal("channel_add_permitted_opens: too many forwards"); debug("allow port forwarding to host %s port %d", host, port); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); @@ -2396,7 +2516,6 @@ channel_clear_permitted_opens(void) for (i = 0; i < num_permitted_opens; i++) xfree(permitted_opens[i].host_to_connect); num_permitted_opens = 0; - } diff --git a/usr/src/cmd/ssh/libssh/common/compat.c b/usr/src/cmd/ssh/libssh/common/compat.c index f63c6c0139..a6d6ef8ad3 100644 --- a/usr/src/cmd/ssh/libssh/common/compat.c +++ b/usr/src/cmd/ssh/libssh/common/compat.c @@ -68,42 +68,47 @@ compat_datafellows(const char *version) "OpenSSH_2.1*," "OpenSSH_2.2*", SSH_OLD_SESSIONID|SSH_BUG_BANNER| SSH_OLD_DHGEX|SSH_BUG_NOREKEY| - SSH_BUG_EXTEOF}, + SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.3.0*", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES| SSH_OLD_DHGEX|SSH_BUG_NOREKEY| - SSH_BUG_EXTEOF}, + SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.3.*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| - SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, + SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| + SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.5.0p1*," "OpenSSH_2.5.1p1*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| - SSH_OLD_GSSAPI}, + SSH_OLD_GSSAPI|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.5.0*," "OpenSSH_2.5.1*," "OpenSSH_2.5.2*", SSH_OLD_DHGEX|SSH_BUG_NOREKEY| - SSH_BUG_EXTEOF}, - { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, + SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, + { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| + SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.9p*", SSH_BUG_EXTEOF|SSH_OLD_GSSAPI| - SSH_BUG_GSSKEX_HOSTKEY}, + SSH_BUG_GSSKEX_HOSTKEY|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.*," "OpenSSH_3.0*," - "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_GSSAPI| - SSH_BUG_GSSAPI_BER| + "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR| + SSH_OLD_GSSAPI|SSH_BUG_GSSAPI_BER| SSH_BUG_GSSKEX_HOSTKEY}, { "OpenSSH_3.2*," "OpenSSH_3.3*," "OpenSSH_3.4*," "OpenSSH_3.5*", SSH_BUG_GSSAPI_BER|SSH_OLD_GSSAPI| - SSH_BUG_GSSKEX_HOSTKEY}, + SSH_BUG_GSSKEX_HOSTKEY|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_3.6*," "OpenSSH_3.7*," - "OpenSSH_3.8*", SSH_BUG_GSSKEX_HOSTKEY}, + "OpenSSH_3.8*", SSH_BUG_GSSKEX_HOSTKEY|SSH_OLD_FORWARD_ADDR}, + { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR}, { "OpenSSH*", 0 }, { "Sun_SSH_1.0.*", SSH_BUG_NOREKEY| - SSH_BUG_LOCALES_NOT_LANGTAGS}, + SSH_BUG_LOCALES_NOT_LANGTAGS|SSH_OLD_FORWARD_ADDR}, { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| - SSH_BUG_LOCALES_NOT_LANGTAGS}, + SSH_BUG_LOCALES_NOT_LANGTAGS|SSH_OLD_FORWARD_ADDR}, + { "Sun_SSH_1.1*", SSH_OLD_FORWARD_ADDR}, + { "Sun_SSH_1.2*", 0 }, { "*MindTerm*", 0 }, { "2.1.0*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| @@ -145,7 +150,8 @@ compat_datafellows(const char *version) { "2.3.*", SSH_BUG_DEBUG|SSH_BUG_RSASIGMD5| SSH_BUG_FIRSTKEX }, { "2.4", SSH_OLD_SESSIONID }, /* Van Dyke */ - { "2.*", SSH_BUG_DEBUG|SSH_BUG_FIRSTKEX }, + { "2.*", SSH_BUG_DEBUG|SSH_BUG_FIRSTKEX| + SSH_BUG_RFWD_ADDR}, { "3.0.*", SSH_BUG_DEBUG }, { "3.0 SecureCRT*", SSH_OLD_SESSIONID }, { "1.7 SecureFX*", SSH_OLD_SESSIONID }, diff --git a/usr/src/cmd/ssh/libssh/common/misc.c b/usr/src/cmd/ssh/libssh/common/misc.c index 6431255004..dcd7902021 100644 --- a/usr/src/cmd/ssh/libssh/common/misc.c +++ b/usr/src/cmd/ssh/libssh/common/misc.c @@ -306,6 +306,48 @@ convtime(const char *s) return total; } +/* + * Search for next delimiter between hostnames/addresses and ports. + * Argument may be modified (for termination). + * Returns *cp if parsing succeeds. + * *cp is set to the start of the next delimiter, if one was found. + * If this is the last field, *cp is set to NULL. + */ +char * +hpdelim(char **cp) +{ + char *s, *old; + + if (cp == NULL || *cp == NULL) + return NULL; + + old = s = *cp; + if (*s == '[') { + if ((s = strchr(s, ']')) == NULL) + return NULL; + else + s++; + } else if ((s = strpbrk(s, ":/")) == NULL) + s = *cp + strlen(*cp); /* skip to end (see first case below) */ + + switch (*s) { + case '\0': + *cp = NULL; /* no more fields*/ + break; + + case ':': + case '/': + *s = '\0'; /* terminate */ + *cp = s + 1; + break; + + default: + return NULL; + } + + return old; +} + char * cleanhostname(char *host) { diff --git a/usr/src/cmd/ssh/libssh/common/readconf.c b/usr/src/cmd/ssh/libssh/common/readconf.c index e0d1ac2e71..86caa54913 100644 --- a/usr/src/cmd/ssh/libssh/common/readconf.c +++ b/usr/src/cmd/ssh/libssh/common/readconf.c @@ -230,21 +230,23 @@ static struct { */ void -add_local_forward(Options *options, u_short port, const char *host, - u_short host_port) +add_local_forward(Options *options, const Forward *newfwd) { Forward *fwd; #ifndef NO_IPPORT_RESERVED_CONCEPT extern uid_t original_real_uid; - if (port < IPPORT_RESERVED && original_real_uid != 0) + if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0) fatal("Privileged ports can only be forwarded by root."); #endif if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); fwd = &options->local_forwards[options->num_local_forwards++]; - fwd->port = port; - fwd->host = xstrdup(host); - fwd->host_port = host_port; + + fwd->listen_host = (newfwd->listen_host == NULL) ? + NULL : xstrdup(newfwd->listen_host); + fwd->listen_port = newfwd->listen_port; + fwd->connect_host = xstrdup(newfwd->connect_host); + fwd->connect_port = newfwd->connect_port; } /* @@ -253,17 +255,19 @@ add_local_forward(Options *options, u_short port, const char *host, */ void -add_remote_forward(Options *options, u_short port, const char *host, - u_short host_port) +add_remote_forward(Options *options, const Forward *newfwd) { Forward *fwd; if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) fatal("Too many remote forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); fwd = &options->remote_forwards[options->num_remote_forwards++]; - fwd->port = port; - fwd->host = xstrdup(host); - fwd->host_port = host_port; + + fwd->listen_host = (newfwd->listen_host == NULL) ? + NULL : xstrdup(newfwd->listen_host); + fwd->listen_port = newfwd->listen_port; + fwd->connect_host = xstrdup(newfwd->connect_host); + fwd->connect_port = newfwd->connect_port; } static void @@ -271,11 +275,17 @@ clear_forwardings(Options *options) { int i; - for (i = 0; i < options->num_local_forwards; i++) - xfree(options->local_forwards[i].host); + for (i = 0; i < options->num_local_forwards; i++) { + if (options->local_forwards[i].listen_host != NULL) + xfree(options->local_forwards[i].listen_host); + xfree(options->local_forwards[i].connect_host); + } options->num_local_forwards = 0; - for (i = 0; i < options->num_remote_forwards; i++) - xfree(options->remote_forwards[i].host); + for (i = 0; i < options->num_remote_forwards; i++) { + if (options->remote_forwards[i].listen_host != NULL) + xfree(options->remote_forwards[i].listen_host); + xfree(options->remote_forwards[i].connect_host); + } options->num_remote_forwards = 0; } @@ -307,11 +317,10 @@ process_config_line(Options *options, const char *host, char *line, const char *filename, int linenum, int *activep) { - char buf[256], *s, *string, **charptr, *endofnumber, *keyword, *arg; + char *s, *string, **charptr, *endofnumber, *keyword, *arg, *arg2, fwdarg[256]; int opcode, *intptr, value, i; - u_short fwd_port, fwd_host_port; - char sfwd_host_port[6]; StoredOption *so; + Forward fwd; s = line; /* Get the keyword. (Each line is supposed to begin with a keyword). */ @@ -694,30 +703,26 @@ parse_int: case oLocalForward: case oRemoteForward: arg = strdelim(&s); - if (!arg || *arg == '\0') + if (arg == NULL || *arg == '\0') fatal("%.200s line %d: Missing port argument.", filename, linenum); - if ((fwd_port = a2port(arg)) == 0) - fatal("%.200s line %d: Bad listen port.", - filename, linenum); - arg = strdelim(&s); - if (!arg || *arg == '\0') - fatal("%.200s line %d: Missing second argument.", + arg2 = strdelim(&s); + if (arg2 == NULL || *arg2 == '\0') + fatal("%.200s line %d: Missing target argument.", filename, linenum); - if (sscanf(arg, "%255[^:]:%5[0-9]", buf, sfwd_host_port) != 2 && - sscanf(arg, "%255[^/]/%5[0-9]", buf, sfwd_host_port) != 2) + + /* construct a string for parse_forward */ + snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2); + + if (parse_forward(1, &fwd, fwdarg) == 0) fatal("%.200s line %d: Bad forwarding specification.", filename, linenum); - if ((fwd_host_port = a2port(sfwd_host_port)) == 0) - fatal("%.200s line %d: Bad forwarding port.", - filename, linenum); + if (*activep) { if (opcode == oLocalForward) - add_local_forward(options, fwd_port, buf, - fwd_host_port); + add_local_forward(options, &fwd); else if (opcode == oRemoteForward) - add_remote_forward(options, fwd_port, buf, - fwd_host_port); + add_remote_forward(options, &fwd); } break; @@ -726,12 +731,16 @@ parse_int: if (!arg || *arg == '\0') fatal("%.200s line %d: Missing port argument.", filename, linenum); - fwd_port = a2port(arg); - if (fwd_port == 0) - fatal("%.200s line %d: Badly formatted port number.", + + if (parse_forward(0, &fwd, arg) == 0) { + fatal("%.200s line %d: Bad dynamic forwarding specification.", filename, linenum); - if (*activep) - add_local_forward(options, fwd_port, "socks4", 0); + } + + if (*activep) { + fwd.connect_host = "socks"; + add_local_forward(options, &fwd); + } break; case oClearAllForwardings: @@ -1091,6 +1100,84 @@ fill_default_options(Options * options) } /* + * Parses a string containing a port forwarding specification of one of the + * two forms, short or long: + * + * [listenhost:]listenport + * [listenhost:]listenport:connecthost:connectport + * + * short forwarding specification is used for dynamic port forwarding and for + * port forwarding cancelation in process_cmdline(). The function returns number + * of arguments parsed or zero on any error. + */ +int +parse_forward(int long_form, Forward *fwd, const char *fwdspec) +{ + int i; + char *p, *cp, *fwdarg[5]; + + memset(fwd, '\0', sizeof(*fwd)); + + cp = p = xstrdup(fwdspec); + + /* skip leading spaces */ + while (isspace(*cp)) + cp++; + + for (i = 0; i < 5; ++i) + if ((fwdarg[i] = hpdelim(&cp)) == NULL) + break; + + if ((long_form == 0 && i > 2) || (long_form == 1 && i < 3) || (i == 5)) + goto fail_free; + + switch (i) { + case 0: + goto fail_free; + + case 1: + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdarg[0]); + break; + + case 2: + fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); + fwd->listen_port = a2port(fwdarg[1]); + break; + + case 3: + fwd->listen_host = NULL; + fwd->listen_port = a2port(fwdarg[0]); + fwd->connect_host = xstrdup(cleanhostname(fwdarg[1])); + fwd->connect_port = a2port(fwdarg[2]); + break; + + case 4: + fwd->listen_host = xstrdup(cleanhostname(fwdarg[0])); + fwd->listen_port = a2port(fwdarg[1]); + fwd->connect_host = xstrdup(cleanhostname(fwdarg[2])); + fwd->connect_port = a2port(fwdarg[3]); + break; + } + + xfree(p); + + if (fwd->listen_port == 0 || (fwd->connect_port == 0 && i > 2)) + goto fail_free; + + return (i); + + fail_free: + if (p != NULL) + xfree(p); + if (fwd->connect_host != NULL) + xfree(fwd->connect_host); + if (fwd->listen_host != NULL) + xfree(fwd->listen_host); + return (0); +} + +/* * Process previously stored unknown options. When this function is called we * already have IgnoreIfUnknown set so finally we can decide whether each * unknown option is to be ignored or not. |