version 1.275, 2008/05/08 12:02:23 |
version 1.276, 2008/05/09 04:55:56 |
|
|
/* helper */ |
/* helper */ |
static void port_open_helper(Channel *c, char *rtype); |
static void port_open_helper(Channel *c, char *rtype); |
|
|
|
/* non-blocking connect helpers */ |
|
static int connect_next(struct channel_connect *); |
|
static void channel_connect_ctx_free(struct channel_connect *); |
|
|
/* -- channel core */ |
/* -- channel core */ |
|
|
Channel * |
Channel * |
|
|
static void |
static void |
channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) |
channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) |
{ |
{ |
int err = 0; |
int err = 0, sock; |
socklen_t sz = sizeof(err); |
socklen_t sz = sizeof(err); |
|
|
if (FD_ISSET(c->sock, writeset)) { |
if (FD_ISSET(c->sock, writeset)) { |
|
|
error("getsockopt SO_ERROR failed"); |
error("getsockopt SO_ERROR failed"); |
} |
} |
if (err == 0) { |
if (err == 0) { |
debug("channel %d: connected", c->self); |
debug("channel %d: connected to %s port %d", |
|
c->self, c->connect_ctx.host, c->connect_ctx.port); |
|
channel_connect_ctx_free(&c->connect_ctx); |
c->type = SSH_CHANNEL_OPEN; |
c->type = SSH_CHANNEL_OPEN; |
if (compat20) { |
if (compat20) { |
packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); |
packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); |
|
|
packet_put_int(c->self); |
packet_put_int(c->self); |
} |
} |
} else { |
} else { |
debug("channel %d: not connected: %s", |
debug("channel %d: connection failed: %s", |
c->self, strerror(err)); |
c->self, strerror(err)); |
|
/* Try next address, if any */ |
|
if ((sock = connect_next(&c->connect_ctx)) > 0) { |
|
close(c->sock); |
|
c->sock = c->rfd = c->wfd = sock; |
|
channel_max_fd = channel_find_maxfd(); |
|
return; |
|
} |
|
/* Exhausted all addresses */ |
|
error("connect_to %.100s port %d: failed.", |
|
c->connect_ctx.host, c->connect_ctx.port); |
|
channel_connect_ctx_free(&c->connect_ctx); |
if (compat20) { |
if (compat20) { |
packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); |
packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); |
packet_put_int(c->remote_id); |
packet_put_int(c->remote_id); |
|
|
Channel *c = NULL; |
Channel *c = NULL; |
u_short host_port; |
u_short host_port; |
char *host, *originator_string; |
char *host, *originator_string; |
int remote_id, sock = -1; |
int remote_id; |
|
|
remote_id = packet_get_int(); |
remote_id = packet_get_int(); |
host = packet_get_string(NULL); |
host = packet_get_string(NULL); |
|
|
originator_string = xstrdup("unknown (remote did not supply name)"); |
originator_string = xstrdup("unknown (remote did not supply name)"); |
} |
} |
packet_check_eom(); |
packet_check_eom(); |
sock = channel_connect_to(host, host_port); |
c = channel_connect_to(host, host_port, |
if (sock != -1) { |
"connected socket", originator_string); |
c = channel_new("connected socket", |
|
SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0, |
|
originator_string, 1); |
|
c->remote_id = remote_id; |
|
} |
|
xfree(originator_string); |
xfree(originator_string); |
|
xfree(host); |
if (c == NULL) { |
if (c == NULL) { |
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
packet_put_int(remote_id); |
packet_put_int(remote_id); |
packet_send(); |
packet_send(); |
} |
} else |
xfree(host); |
c->remote_id = remote_id; |
} |
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
|
|
num_adm_permitted_opens = 0; |
num_adm_permitted_opens = 0; |
} |
} |
|
|
/* return socket to remote host, port */ |
/* Try to start non-blocking connect to next host in cctx list */ |
static int |
static int |
connect_to(const char *host, u_short port) |
connect_next(struct channel_connect *cctx) |
{ |
{ |
struct addrinfo hints, *ai, *aitop; |
int sock, saved_errno; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
char ntop[NI_MAXHOST], strport[NI_MAXSERV]; |
int gaierr; |
|
int sock = -1; |
|
|
|
memset(&hints, 0, sizeof(hints)); |
for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { |
hints.ai_family = IPv4or6; |
if (cctx->ai->ai_family != AF_INET && |
hints.ai_socktype = SOCK_STREAM; |
cctx->ai->ai_family != AF_INET6) |
snprintf(strport, sizeof strport, "%d", port); |
|
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { |
|
error("connect_to %.100s: unknown host (%s)", host, |
|
ssh_gai_strerror(gaierr)); |
|
return -1; |
|
} |
|
for (ai = aitop; ai; ai = ai->ai_next) { |
|
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
|
continue; |
continue; |
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), |
if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, |
strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { |
ntop, sizeof(ntop), strport, sizeof(strport), |
error("connect_to: getnameinfo failed"); |
NI_NUMERICHOST|NI_NUMERICSERV) != 0) { |
|
error("connect_next: getnameinfo failed"); |
continue; |
continue; |
} |
} |
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, |
if (sock < 0) { |
cctx->ai->ai_protocol)) == -1) { |
if (ai->ai_next == NULL) |
if (cctx->ai->ai_next == NULL) |
error("socket: %.100s", strerror(errno)); |
error("socket: %.100s", strerror(errno)); |
else |
else |
verbose("socket: %.100s", strerror(errno)); |
verbose("socket: %.100s", strerror(errno)); |
|
|
} |
} |
if (set_nonblock(sock) == -1) |
if (set_nonblock(sock) == -1) |
fatal("%s: set_nonblock(%d)", __func__, sock); |
fatal("%s: set_nonblock(%d)", __func__, sock); |
if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && |
if (connect(sock, cctx->ai->ai_addr, |
errno != EINPROGRESS) { |
cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { |
error("connect_to %.100s port %s: %.100s", ntop, strport, |
debug("connect_next: host %.100s ([%.100s]:%s): " |
|
"%.100s", cctx->host, ntop, strport, |
strerror(errno)); |
strerror(errno)); |
|
saved_errno = errno; |
close(sock); |
close(sock); |
|
errno = saved_errno; |
continue; /* fail -- try next */ |
continue; /* fail -- try next */ |
} |
} |
break; /* success */ |
debug("connect_next: host %.100s ([%.100s]:%s) " |
|
"in progress, fd=%d", cctx->host, ntop, strport, sock); |
|
cctx->ai = cctx->ai->ai_next; |
|
set_nodelay(sock); |
|
return sock; |
|
} |
|
return -1; |
|
} |
|
|
|
static void |
|
channel_connect_ctx_free(struct channel_connect *cctx) |
|
{ |
|
xfree(cctx->host); |
|
if (cctx->aitop) |
|
freeaddrinfo(cctx->aitop); |
|
bzero(cctx, sizeof(*cctx)); |
|
cctx->host = NULL; |
|
cctx->ai = cctx->aitop = NULL; |
|
} |
|
|
|
/* Return CONNECTING channel to remote host, port */ |
|
static Channel * |
|
connect_to(const char *host, u_short port, char *ctype, char *rname) |
|
{ |
|
struct addrinfo hints; |
|
int gaierr; |
|
int sock = -1; |
|
char strport[NI_MAXSERV]; |
|
struct channel_connect cctx; |
|
Channel *c; |
|
|
|
memset(&hints, 0, sizeof(hints)); |
|
hints.ai_family = IPv4or6; |
|
hints.ai_socktype = SOCK_STREAM; |
|
snprintf(strport, sizeof strport, "%d", port); |
|
if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) { |
|
error("connect_to %.100s: unknown host (%s)", host, |
|
ssh_gai_strerror(gaierr)); |
|
return NULL; |
} |
} |
freeaddrinfo(aitop); |
|
if (!ai) { |
cctx.host = xstrdup(host); |
error("connect_to %.100s port %d: failed.", host, port); |
cctx.port = port; |
return -1; |
cctx.ai = cctx.aitop; |
|
|
|
if ((sock = connect_next(&cctx)) == -1) { |
|
error("connect to %.100s port %d failed: %s", |
|
host, port, strerror(errno)); |
|
channel_connect_ctx_free(&cctx); |
|
return NULL; |
} |
} |
/* success */ |
c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, |
set_nodelay(sock); |
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); |
return sock; |
c->connect_ctx = cctx; |
|
return c; |
} |
} |
|
|
int |
Channel * |
channel_connect_by_listen_address(u_short listen_port) |
channel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname) |
{ |
{ |
int i; |
int i; |
|
|
for (i = 0; i < num_permitted_opens; i++) |
for (i = 0; i < num_permitted_opens; i++) { |
if (permitted_opens[i].host_to_connect != NULL && |
if (permitted_opens[i].host_to_connect != NULL && |
permitted_opens[i].listen_port == listen_port) |
permitted_opens[i].listen_port == listen_port) { |
return connect_to( |
return connect_to( |
permitted_opens[i].host_to_connect, |
permitted_opens[i].host_to_connect, |
permitted_opens[i].port_to_connect); |
permitted_opens[i].port_to_connect, ctype, rname); |
|
} |
|
} |
error("WARNING: Server requests forwarding for unknown listen_port %d", |
error("WARNING: Server requests forwarding for unknown listen_port %d", |
listen_port); |
listen_port); |
return -1; |
return NULL; |
} |
} |
|
|
/* Check if connecting to that port is permitted and connect. */ |
/* Check if connecting to that port is permitted and connect. */ |
int |
Channel * |
channel_connect_to(const char *host, u_short port) |
channel_connect_to(const char *host, u_short port, char *ctype, char *rname) |
{ |
{ |
int i, permit, permit_adm = 1; |
int i, permit, permit_adm = 1; |
|
|
|
|
if (!permit || !permit_adm) { |
if (!permit || !permit_adm) { |
logit("Received request to connect to host %.100s port %d, " |
logit("Received request to connect to host %.100s port %d, " |
"but the request was denied.", host, port); |
"but the request was denied.", host, port); |
return -1; |
return NULL; |
} |
} |
return connect_to(host, port); |
return connect_to(host, port, ctype, rname); |
} |
} |
|
|
void |
void |