2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.ssh;
10 import static java.util.Objects.requireNonNull;
12 import io.netty.bootstrap.Bootstrap;
13 import io.netty.channel.Channel;
14 import io.netty.channel.ChannelFuture;
15 import io.netty.channel.ChannelInitializer;
16 import io.netty.channel.EventLoopGroup;
17 import io.netty.channel.local.LocalAddress;
18 import io.netty.channel.local.LocalChannel;
19 import java.io.InputStream;
20 import java.io.OutputStream;
21 import java.net.InetSocketAddress;
22 import java.net.SocketAddress;
23 import org.opendaylight.netconf.api.messages.NetconfHelloMessageAdditionalHeader;
24 import org.opendaylight.netconf.shaded.sshd.common.NamedFactory;
25 import org.opendaylight.netconf.shaded.sshd.common.io.IoInputStream;
26 import org.opendaylight.netconf.shaded.sshd.common.io.IoOutputStream;
27 import org.opendaylight.netconf.shaded.sshd.server.Environment;
28 import org.opendaylight.netconf.shaded.sshd.server.ExitCallback;
29 import org.opendaylight.netconf.shaded.sshd.server.channel.ChannelSession;
30 import org.opendaylight.netconf.shaded.sshd.server.command.AsyncCommand;
31 import org.opendaylight.netconf.shaded.sshd.server.command.Command;
32 import org.opendaylight.netconf.shaded.sshd.server.session.ServerSession;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
37 * This command handles all netconf related rpc and forwards to delegate server.
38 * Uses netty to make a local connection to delegate server.
41 * Command is Apache Mina SSH terminology for objects handling ssh data.
43 public class RemoteNetconfCommand implements AsyncCommand {
45 private static final Logger LOG = LoggerFactory.getLogger(RemoteNetconfCommand.class);
47 private final EventLoopGroup clientEventGroup;
48 private final LocalAddress localAddress;
50 private IoInputStream in;
51 private IoOutputStream out;
52 private ExitCallback callback;
53 private NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
55 private Channel clientChannel;
56 private ChannelFuture clientChannelFuture;
58 public RemoteNetconfCommand(final EventLoopGroup clientEventGroup, final LocalAddress localAddress) {
59 this.clientEventGroup = clientEventGroup;
60 this.localAddress = localAddress;
64 @SuppressWarnings("checkstyle:hiddenField")
65 public void setIoInputStream(final IoInputStream in) {
70 @SuppressWarnings("checkstyle:hiddenField")
71 public void setIoOutputStream(final IoOutputStream out) {
76 public void setIoErrorStream(final IoOutputStream err) {
77 // TODO do we want to use error stream in some way ?
81 @SuppressWarnings("checkstyle:hiddenField")
82 public void setInputStream(final InputStream in) {
83 throw new UnsupportedOperationException("Synchronous IO is unsupported");
87 @SuppressWarnings("checkstyle:hiddenField")
88 public void setOutputStream(final OutputStream out) {
89 throw new UnsupportedOperationException("Synchronous IO is unsupported");
94 public void setErrorStream(final OutputStream err) {
95 throw new UnsupportedOperationException("Synchronous IO is unsupported");
100 @SuppressWarnings("checkstyle:hiddenField")
101 public void setExitCallback(final ExitCallback callback) {
102 this.callback = callback;
106 public void start(final ChannelSession channel, final Environment env) {
107 final ServerSession session = channel.getServerSession();
108 final SocketAddress remoteAddress = session.getIoSession().getRemoteAddress();
109 final String hostName;
111 if (remoteAddress instanceof InetSocketAddress) {
112 hostName = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
113 port = Integer.toString(((InetSocketAddress) remoteAddress).getPort());
118 netconfHelloMessageAdditionalHeader = new NetconfHelloMessageAdditionalHeader(session.getUsername(), hostName,
119 port, "ssh", "client");
121 LOG.trace("Establishing internal connection to netconf server for client: {}", getClientAddress());
123 final Bootstrap clientBootstrap = new Bootstrap();
124 clientBootstrap.group(clientEventGroup).channel(LocalChannel.class);
126 clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
128 public void initChannel(final LocalChannel ch) {
130 .addLast(new SshProxyClientHandler(in, out, netconfHelloMessageAdditionalHeader, callback));
133 clientChannelFuture = clientBootstrap.connect(localAddress);
134 clientChannelFuture.addListener(future -> {
135 if (future.isSuccess()) {
136 clientChannel = clientChannelFuture.channel();
138 LOG.warn("Unable to establish internal connection to netconf server for client: {}",
140 requireNonNull(callback, "Exit callback must be set").onExit(1,
141 "Unable to establish internal connection to netconf server for client: " + getClientAddress());
147 public void destroy(final ChannelSession channel) {
148 LOG.trace("Releasing internal connection to netconf server for client: {} on channel: {}",
149 getClientAddress(), clientChannel);
151 clientChannelFuture.cancel(true);
152 if (clientChannel != null) {
153 clientChannel.close().addListener(future -> {
154 if (!future.isSuccess()) {
155 LOG.warn("Unable to release internal connection to netconf server on channel: {}",
162 private String getClientAddress() {
163 return netconfHelloMessageAdditionalHeader.getAddress();
166 public static class NetconfCommandFactory implements NamedFactory<Command> {
168 public static final String NETCONF = "netconf";
170 private final EventLoopGroup clientBootstrap;
171 private final LocalAddress localAddress;
173 public NetconfCommandFactory(final EventLoopGroup clientBootstrap, final LocalAddress localAddress) {
175 this.clientBootstrap = clientBootstrap;
176 this.localAddress = localAddress;
180 public String getName() {
185 public RemoteNetconfCommand create() {
186 return new RemoteNetconfCommand(clientBootstrap, localAddress);