You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mina.apache.org by "Feng Jiajie (Jira)" <ji...@apache.org> on 2020/09/02 08:43:00 UTC
[jira] [Commented] (SSHD-1055) Remote port forwarding mode does not
handle EOF properly
[ https://issues.apache.org/jira/browse/SSHD-1055?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17189083#comment-17189083 ]
Feng Jiajie commented on SSHD-1055:
-----------------------------------
Hi [~lgoldstein], [~roberto.deandrea]
I got some log using openssh server debug mode:
{code:java}
debug1: Connection to port 15678 forwarding to 0.0.0.0 port 0 requested.
debug2: fd 12 setting TCP_NODELAY
debug2: fd 12 setting O_NONBLOCK
debug3: fd 12 is O_NONBLOCK
debug1: channel 2: new [forwarded-tcpip]
debug3: send packet: type 90
debug3: receive packet: type 91
debug2: channel 2: open confirm rwindow 2097152 rmax 32768
debug3: receive packet: type 96
debug2: channel 2: rcvd eof // ============================================= begin eof handle
debug2: channel 2: output open -> drain
debug2: channel 2: obuf empty
debug2: channel 2: chan_shutdown_write (i0 o1 sock 12 wfd 12 efd -1 [closed])
debug2: channel 2: output drain -> closed
debug2: channel 2: read<=0 rfd 12 len 0
debug2: channel 2: read failed
debug2: channel 2: chan_shutdown_read (i0 o3 sock 12 wfd 12 efd -1 [closed])
debug2: channel 2: input open -> drain
debug2: channel 2: ibuf empty
debug2: channel 2: send eof
debug3: send packet: type 96
debug2: channel 2: input drain -> closed
debug2: channel 2: send close
debug3: send packet: type 97
debug3: channel 2: will not send data after close
debug3: receive packet: type 97
debug2: channel 2: rcvd close
debug3: channel 2: will not send data after close
debug2: channel 2: is dead
debug2: channel 2: garbage collecting
debug1: channel 2: free: forwarded-tcpip: listening port 15678 for 0.0.0.0 port 0, connect from ::1 port 40480 to ::1 port 15678, nchannels 3
debug3: channel 2: status: The following connections are open:
#2 forwarded-tcpip: listening port 15678 for 0.0.0.0 port 0, connect from ::1 port 40480 to ::1 port 15678 (t4 r0 i3/0 o3/0 e[closed]/0 fd 12/12/-1 sock 12 cc -1)
{code}
[https://github.com/openssh/openssh-portable/blob/V_7_9_P1/serverloop.c#L906]
{code:java}
ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); // ============= call channel_input_ieof
{code}
[https://github.com/openssh/openssh-portable/blob/V_7_9_P1/channels.c#L3095]
{code:java}
int
channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
{
Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
ssh_packet_check_eom(ssh);
if (channel_proxy_upstream(c, type, seq, ssh))
return 0;
chan_rcvd_ieof(ssh, c); // =================================== call chan_rcvd_ieof
/* XXX force input close */
if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
debug("channel %d: FORCE input drain", c->self);
c->istate = CHAN_INPUT_WAIT_DRAIN;
if (sshbuf_len(c->input) == 0)
chan_ibuf_empty(ssh, c);
}
return 0;
}
{code}
[https://github.com/openssh/openssh-portable/blob/V_7_9_P1/nchan.c]
{code:java}
void
chan_rcvd_ieof(struct ssh *ssh, Channel *c)
{
debug2("channel %d: rcvd eof", c->self); // =============== log debug2: channel 2: rcvd eof
c->flags |= CHAN_EOF_RCVD;
if (c->ostate == CHAN_OUTPUT_OPEN)
chan_set_ostate(c, CHAN_OUTPUT_WAIT_DRAIN); // ================= log debug2: channel 2: output open -> drain
if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN &&
sshbuf_len(c->output) == 0 &&
!CHANNEL_EFD_OUTPUT_ACTIVE(c))
chan_obuf_empty(ssh, c); // ======================= call chan_obuf_empty
}{code}
[https://github.com/openssh/openssh-portable/blob/V_7_9_P1/nchan.c#L147]
{code:java}
void
chan_obuf_empty(struct ssh *ssh, Channel *c)
{
debug2("channel %d: obuf empty", c->self); // ===================== debug2: channel 2: obuf empty
if (sshbuf_len(c->output)) {
error("channel %d: chan_obuf_empty for non empty buffer",
c->self);
return;
}
switch (c->ostate) {
case CHAN_OUTPUT_WAIT_DRAIN:
chan_shutdown_write(ssh, c); // ====================== call chan_shutdown_write
chan_set_ostate(c, CHAN_OUTPUT_CLOSED); // ================ log debug2: channel 2: output drain -> closed
break;
default:
error("channel %d: internal error: obuf_empty for ostate %d",
c->self, c->ostate);
break;
}
}{code}
[https://github.com/openssh/openssh-portable/blob/V_7_9_P1/nchan.c#L373]
{code:java}
static void
chan_shutdown_write(struct ssh *ssh, Channel *c)
{
sshbuf_reset(c->output);
if (c->type == SSH_CHANNEL_LARVAL)
return;
/* shutdown failure is allowed if write failed already */
debug2("channel %d: %s (i%d o%d sock %d wfd %d efd %d [%s])", // ======== log debug2: channel 2: chan_shutdown_write (i0 o1 sock 12 wfd 12 efd -1 [closed])
c->self, __func__, c->istate, c->ostate, c->sock, c->wfd, c->efd,
channel_format_extended_usage(c));
if (c->sock != -1) {
if (shutdown(c->sock, SHUT_WR) < 0) { // ================ call shutdown fd SHUT_WR
debug2("channel %d: %s: shutdown() failed for "
"fd %d [i%d o%d]: %.100s", c->self, __func__,
c->sock, c->istate, c->ostate,
strerror(errno));
}
} else {
if (channel_close_fd(ssh, &c->wfd) < 0) {
logit("channel %d: %s: close() failed for "
"fd %d [i%d o%d]: %.100s",
c->self, __func__, c->wfd, c->istate, c->ostate,
strerror(errno));
}
}
}{code}
I find MINA SSHD serverSession.shutdownOutputStream(); do shutdown fd:
{code:java}
@Override
public final AsynchronousSocketChannel shutdownOutput() throws IOException {
try {
begin();
if (remoteAddress == null)
throw new NotYetConnectedException();
synchronized (writeLock) {
if (!writeShutdown) {
Net.shutdown(fd, Net.SHUT_WR); // ======================
writeShutdown = true;
}
}
} finally {
end();
}
return this;
}
{code}
but [https://github.com/apache/mina-sshd/commit/385f63731e74d0c73b6624170bfd1ad385ebffc6] remove the call.
> Remote port forwarding mode does not handle EOF properly
> --------------------------------------------------------
>
> Key: SSHD-1055
> URL: https://issues.apache.org/jira/browse/SSHD-1055
> Project: MINA SSHD
> Issue Type: Bug
> Affects Versions: 2.5.1
> Reporter: Feng Jiajie
> Priority: Major
>
> I want to call the remote server's gRPC service locally through an SSH tunnel.
> MyApp -> MINA SSHD -> \{Internet} -> gRPC Server
> It works just fine with OpenSSH, but there is a small problem(no problems with core functions, only in unusual circumstances) with Mina SSHD.
> I think the problem is Mina SSHD's handling of EOF.
> Here is the example:
> Step 1. Start a gRPC server:
> Because we only need a gRPC server to reproduce the problem, so I write a simple version without any service:
> {code:java}
> <dependency>
> <groupId>io.grpc</groupId>
> <artifactId>grpc-netty-shaded</artifactId>
> <version>1.27.2</version>
> </dependency>
> <dependency>
> <groupId>io.grpc</groupId>
> <artifactId>grpc-protobuf</artifactId>
> <version>1.27.2</version>
> </dependency>
> <dependency>
> <groupId>io.grpc</groupId>
> <artifactId>grpc-stub</artifactId>
> <version>1.27.2</version>
> </dependency>
> {code}
> main:
> {code:java}
> import io.grpc.Server;
> import io.grpc.ServerBuilder;
> public class EmptyGrpcServer {
> public static void main(String[] args) throws Exception {
> Server server = ServerBuilder.forPort(23645).build().start();
> server.awaitTermination();
> }
> }
> {code}
> Full example can be fond here: [https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java]
> Step 2. Start a MINA SSHD server:
> {code:java}
> import org.apache.sshd.server.SshServer;
> import org.apache.sshd.server.forward.AcceptAllForwardingFilter;
> import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
> import java.nio.file.Paths;
> public class Example1 {
> public static void main(String[] args) throws Exception {
> SshServer sshd = SshServer.setUpDefaultServer();
> sshd.setPort(12133);
> sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(Paths.get("/tmp/a.ser")));
> sshd.setPasswordAuthenticator((username, password, session) -> true);
> sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE);
> sshd.start();
> Thread.sleep(10000000);
> }
> }
> {code}
> Step 3. Create a channel using ssh client
> {code:java}
> ssh -o 'ExitOnForwardFailure yes' -vvv -p 12133 -f -x -N -T -R 0.0.0.0:0:127.0.0.1:23645 test5@127.0.0.1
> {code}
> Step 4. Reproduce
> If I connect directly to the gRPC server using curl, cause gRPC using http/2, I would get error output like this:
> {code:java}
> $ curl 127.0.0.1:23645
> ���+Unexpected HTTP/1.x request: GET /
> $
> {code}
> Then if I do step 3 with an OpenSSH server, I would get same error output:
> {code:java}
> $ ssh -o 'ExitOnForwardFailure yes' -f -x -N -T -R 0.0.0.0:0:127.0.0.1:23645 work@dev.kbyte.cn
> Allocated port 13525 for remote forward to 127.0.0.1:23645
> $
> $ curl dev.kbyte.cn:13525
> ���+Unexpected HTTP/1.x request: GET /
> $
> {code}
> But when I do step 3 with MINA SSHD, curl would stuck without any output:
> {code:java}
> $ curl 127.0.0.1:55604
> {code}
> I found MINA SSHD had already got and wrote the package with the string "Unexpected.HTTP/1.x.request:.GET", and received SSH_MSG_CHANNEL_EOF.
> So I think handleEof should do more? like send SSH_MSG_CHANNEL_EOF to curl?
>
--
This message was sent by Atlassian Jira
(v8.3.4#803005)
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@mina.apache.org
For additional commands, e-mail: dev-help@mina.apache.org