2 * Copyright (c) 2013 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.controller.netconf.ssh;
10 import com.google.common.base.Preconditions;
11 import java.io.IOException;
12 import java.net.InetSocketAddress;
13 import java.net.ServerSocket;
14 import java.net.Socket;
15 import java.util.concurrent.ExecutorService;
16 import java.util.concurrent.Executors;
17 import java.util.concurrent.atomic.AtomicLong;
19 import javax.annotation.concurrent.ThreadSafe;
21 import org.opendaylight.controller.netconf.auth.AuthProvider;
22 import org.opendaylight.controller.netconf.ssh.threads.Handshaker;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
26 import com.google.common.annotations.VisibleForTesting;
27 import com.google.common.base.Optional;
29 import io.netty.channel.EventLoopGroup;
30 import io.netty.channel.local.LocalAddress;
33 * Thread that accepts client connections. Accepted socket is forwarded to {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker},
34 * which is executed in {@link #handshakeExecutor}.
37 public final class NetconfSSHServer extends Thread implements AutoCloseable {
39 private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class);
40 private static final AtomicLong sessionIdCounter = new AtomicLong();
42 private final ServerSocket serverSocket;
43 private final LocalAddress localAddress;
44 private final EventLoopGroup bossGroup;
45 private Optional<AuthProvider> authProvider = Optional.absent();
46 private final ExecutorService handshakeExecutor;
47 private final char[] pem;
48 private volatile boolean up;
50 private NetconfSSHServer(final int serverPort, final LocalAddress localAddress, final EventLoopGroup bossGroup, final char[] pem) throws IOException {
51 super(NetconfSSHServer.class.getSimpleName());
52 this.bossGroup = bossGroup;
54 logger.trace("Creating SSH server socket on port {}", serverPort);
55 this.serverSocket = new ServerSocket(serverPort);
56 if (serverSocket.isBound() == false) {
57 throw new IllegalStateException("Socket can't be bound to requested port :" + serverPort);
59 logger.trace("Server socket created.");
60 this.localAddress = localAddress;
62 handshakeExecutor = Executors.newFixedThreadPool(10);
65 public static NetconfSSHServer start(final int serverPort, final LocalAddress localAddress, final EventLoopGroup bossGroup, final char[] pemArray) throws IOException {
66 final NetconfSSHServer netconfSSHServer = new NetconfSSHServer(serverPort, localAddress, bossGroup, pemArray);
67 netconfSSHServer.start();
68 return netconfSSHServer;
71 public synchronized AuthProvider getAuthProvider() {
72 Preconditions.checkState(authProvider.isPresent(), "AuthenticationProvider is not set up, cannot authenticate user");
73 return authProvider.get();
76 public synchronized void setAuthProvider(final AuthProvider authProvider) {
77 if(this.authProvider != null) {
78 logger.debug("Changing auth provider to {}", authProvider);
80 this.authProvider = Optional.fromNullable(authProvider);
84 public void close() throws IOException {
86 logger.trace("Closing SSH server socket.");
88 bossGroup.shutdownGracefully();
89 logger.trace("SSH server socket closed.");
93 public InetSocketAddress getLocalSocketAddress() {
94 return (InetSocketAddress) serverSocket.getLocalSocketAddress();
100 Socket acceptedSocket = null;
102 acceptedSocket = serverSocket.accept();
103 } catch (final IOException e) {
105 logger.trace("Exiting server thread", e);
107 logger.warn("Exception occurred during socket.accept", e);
110 if (acceptedSocket != null) {
112 final Handshaker task = new Handshaker(acceptedSocket, localAddress, sessionIdCounter.incrementAndGet(), getAuthProvider(), bossGroup, pem);
113 handshakeExecutor.submit(task);
114 } catch (final IOException e) {
115 logger.warn("Cannot set PEMHostKey, closing connection", e);
116 closeSocket(acceptedSocket);
117 } catch (final IllegalStateException e) {
118 logger.warn("Cannot accept connection, closing", e);
119 closeSocket(acceptedSocket);
123 logger.debug("Server thread is exiting");
126 private void closeSocket(final Socket acceptedSocket) {
128 acceptedSocket.close();
129 } catch (final IOException e) {
130 logger.warn("Ignoring exception while closing socket", e);