public String start() throws Exception { authSocket = AprLibrary.createLocalSocketAddress(); pool = Pool.create(AprLibrary.getInstance().getRootPool()); handle = Local.create(authSocket, pool); int result = Local.bind(handle, 0); if (result != Status.APR_SUCCESS) { throwException(result); } AprLibrary.secureLocalSocket(authSocket, handle); result = Local.listen(handle, 0); if (result != Status.APR_SUCCESS) { throwException(result); } thread = new Thread() { public void run() { try { while (true) { long clientSock = Local.accept(handle); Socket.timeoutSet(clientSock, 10000000); new SshAgentSession(clientSock, agent); } } catch (Exception e) { e.printStackTrace(); } } }; thread.start(); return authSocket; }
@Override protected OpenFuture doInit(Buffer buffer) { OpenFuture f = new DefaultOpenFuture(this); try { out = new ChannelOutputStream( this, getRemoteWindow(), log, SshConstants.SSH_MSG_CHANNEL_DATA, true); authSocket = PropertyResolverUtils.getString(this, SshAgent.SSH_AUTHSOCKET_ENV_NAME); pool = Pool.create(AprLibrary.getInstance().getRootPool()); handle = Local.create(authSocket, pool); int result = Local.connect(handle, 0); if (result != Status.APR_SUCCESS) { throwException(result); } ExecutorService service = getExecutorService(); forwardService = (service == null) ? ThreadUtils.newSingleThreadExecutor("ChannelAgentForwarding[" + authSocket + "]") : service; shutdownForwarder = service != forwardService || isShutdownOnExit(); final int copyBufSize = PropertyResolverUtils.getIntProperty( this, FORWARDER_BUFFER_SIZE, DEFAULT_FORWARDER_BUF_SIZE); ValidateUtils.checkTrue( copyBufSize >= MIN_FORWARDER_BUF_SIZE, "Copy buf size below min.: %d", copyBufSize); ValidateUtils.checkTrue( copyBufSize <= MAX_FORWARDER_BUF_SIZE, "Copy buf size above max.: %d", copyBufSize); forwarder = forwardService.submit( () -> { try { byte[] buf = new byte[copyBufSize]; while (true) { int len = Socket.recv(handle, buf, 0, buf.length); if (len > 0) { out.write(buf, 0, len); out.flush(); } } } catch (IOException e) { close(true); } }); signalChannelOpenSuccess(); f.setOpened(); } catch (Throwable t) { Throwable e = GenericUtils.peelException(t); signalChannelOpenFailure(e); f.setException(e); } return f; }
void destroyPull() { if (shutdowned) return; // Causes segfault in AprSocketSource.poll() method, so this function must be called from it try { Socket.close(socket); // or // Socket.shutdown(socket, Socket.APR_SHUTDOWN_READWRITE); Pool.destroy(pool); } catch (Exception e) { s_logger.info("[ignored]" + "failure during network cleanup: " + e.getLocalizedMessage()); } }
public class AprSocketWrapperImpl extends PipelineImpl implements SocketWrapper { private static final Logger s_logger = Logger.getLogger(AprSocketWrapperImpl.class); static { try { Library.initialize(null); SSL.initialize(null); } catch (Exception e) { throw new RuntimeException("Cannot load Tomcat Native Library (Apache Portable Runtime).", e); } } private final SSLState sslState; final long pool = Pool.create(0); long inetAddress; long socket; private AprSocketSource source; private AprSocketSink sink; boolean shutdown = false; private final boolean shutdowned = false; public AprSocketWrapperImpl(String id, SSLState sslState) { super(id); this.sslState = sslState; } @Override protected HashMap<String, Element> initElementMap(String id) { HashMap<String, Element> map = new HashMap<String, Element>(); source = new AprSocketSource(id + "." + OUT, this); sink = new AprSocketSink(id + "." + IN, this); // Pass requests to read data to socket input stream map.put(OUT, source); // All incoming data, which is sent to this socket wrapper, will be sent // to socket remote map.put(IN, sink); return map; } /** * Connect this socket wrapper to remote server and start main loop on AprSocketSource stdout * link, to watch for incoming data, and AprSocketSink stdin link, to pull for outgoing data. */ @Override public void connect(InetSocketAddress address) throws IOException { try { inetAddress = Address.info(address.getHostName(), Socket.APR_UNSPEC, address.getPort(), 0, pool); socket = Socket.create( Address.getInfo(inetAddress).family, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool); } catch (Exception e) { throw new IOException( "[" + this + "] ERROR: Cannot create socket for \"" + address + "\".", e); } int ret = Socket.connect(socket, inetAddress); if (ret != 0) throw new IOException( "[" + this + "] ERROR: Cannot connect to remote host \"" + address + "\": " + Error.strerror(ret)); source.setSocket(socket); sink.setSocket(socket); // Start polling for data to send to remote sever runMainLoop(IN, STDIN, true, true); // Push incoming data from server to handlers runMainLoop(OUT, STDOUT, false, false); } @Override public void handleEvent(Event event, Direction direction) { switch (event) { case SOCKET_UPGRADE_TO_SSL: upgradeToSsl(); break; default: super.handleEvent(event, direction); break; } } @Override public void validate() { if (getPads(Direction.IN).size() == 0) throw new RuntimeException("[ " + this + "] BUG: Input of socket is not connected."); if (getPads(Direction.OUT).size() == 0) throw new RuntimeException("[ " + this + "] BUG: Output of socket is not connected."); } @Override public void upgradeToSsl() { try { long sslContext; try { sslContext = SSLContext.make(pool, SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_MODE_CLIENT); } catch (Exception e) { throw new RuntimeException("Cannot create SSL context using Tomcat native library.", e); } SSLContext.setOptions( sslContext, SSL.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS | SSL.SSL_OP_TLS_BLOCK_PADDING_BUG | SSL.SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER | SSL.SSL_OP_MSIE_SSLV2_RSA_PADDING); // FIXME: verify certificate by default SSLContext.setVerify(sslContext, SSL.SSL_CVERIFY_NONE, 0); int ret; try { ret = SSLSocket.attach(sslContext, socket); } catch (Exception e) { throw new RuntimeException( "[" + this + "] ERROR: Cannot attach SSL context to socket: ", e); } if (ret != 0) throw new RuntimeException( "[" + this + "] ERROR: Cannot attach SSL context to socket(" + ret + "): " + SSL.getLastError()); try { ret = SSLSocket.handshake(socket); } catch (Exception e) { throw new RuntimeException( "[" + this + "] ERROR: Cannot make SSL handshake with server: ", e); } if (ret != 0 && ret != 20014) // 20014: bad certificate signature FIXME: show prompt for self signed // certificate throw new RuntimeException( "[" + this + "] ERROR: Cannot make SSL handshake with server(" + ret + "): " + SSL.getLastError()); try { byte[] key = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT); // *DEBUG*/System.out.println("DEBUG: Server cert:\n"+new ByteBuffer(key).dump()); sslState.serverCertificateSubjectPublicKeyInfo = new X509CertImpl(key).getPublicKey().getEncoded(); } catch (Exception e) { throw new RuntimeException("[" + this + "] ERROR: Cannot get server public key: ", e); } } catch (RuntimeException e) { shutdown(); throw e; } } @Override public void shutdown() { if (shutdown) return; shutdown = true; try { handleEvent(Event.STREAM_CLOSE, Direction.IN); } catch (Exception e) { s_logger.info( "[ignored]" + "handling stream close event failed on input: " + e.getLocalizedMessage()); } try { handleEvent(Event.STREAM_CLOSE, Direction.OUT); } catch (Exception e) { s_logger.info( "[ignored]" + "handling event close event failed on output: " + e.getLocalizedMessage()); } } void destroyPull() { if (shutdowned) return; // Causes segfault in AprSocketSource.poll() method, so this function must be called from it try { Socket.close(socket); // or // Socket.shutdown(socket, Socket.APR_SHUTDOWN_READWRITE); Pool.destroy(pool); } catch (Exception e) { s_logger.info("[ignored]" + "failure during network cleanup: " + e.getLocalizedMessage()); } } @Override public String toString() { return "AprSocketWrapper(" + id + ")"; } /** Example. */ public static void main(String args[]) { try { System.setProperty("streamer.Link.debug", "true"); System.setProperty("streamer.Element.debug", "true"); System.setProperty("rdpclient.MockServer.debug", "true"); Pipeline pipeline = new PipelineImpl("echo client"); AprSocketWrapperImpl socketWrapper = new AprSocketWrapperImpl("socket", null); pipeline.add(socketWrapper); pipeline.add(new BaseElement("echo")); pipeline.add(new Queue("queue")); // To decouple input and output pipeline.link("socket", "echo", "queue", "socket"); final byte[] mockData = new byte[] {0x01, 0x02, 0x03}; MockServer server = new MockServer( new Packet[] { new Packet("Server hello") { { type = SERVER; data = mockData; } }, new Packet("Client hello") { { type = CLIENT; data = mockData; } }, new Packet("Server hello") { { type = SERVER; data = mockData; } }, new Packet("Client hello") { { type = CLIENT; data = mockData; } } }); server.start(); InetSocketAddress address = server.getAddress(); socketWrapper.connect(address); } catch (Exception e) { e.printStackTrace(System.err); } } }