@Override public void run() { Socket socket = new Socket(); InetSocketAddress address = new InetSocketAddress(this.peer.getIp(), this.peer.getPort()); try { logger.info("Connecting to {}...", this.peer); socket.connect(address, 3 * 1000); this.handler.sendHandshake(socket); Handshake hs = this.handler.validateHandshake( socket, (this.peer.hasPeerId() ? this.peer.getPeerId().array() : null)); logger.info( "Handshaked with {}, peer ID is {}.", this.peer, Torrent.byteArrayToHexString(hs.getPeerId())); this.handler.fireNewPeerConnection(socket, hs.getPeerId()); } catch (IOException ioe) { try { socket.close(); } catch (IOException e) { } this.handler.fireFailedConnection(this.peer, ioe); } catch (ParseException pe) { try { socket.close(); } catch (IOException e) { } this.handler.fireFailedConnection(this.peer, pe); } }
/** * Accept the next incoming connection. * * <p>When a new peer connects to this service, wait for it to send its handshake. We then parse * and check that the handshake advertises the torrent hash we expect, then reply with our own * handshake. * * <p>If everything goes according to plan, notify the <code>IncomingConnectionListener</code>s * with the connected socket and the parsed peer ID. */ private void accept() throws IOException, SocketTimeoutException { Socket socket = this.socket.accept(); try { logger.debug("New incoming connection ..."); Handshake hs = this.validateHandshake(socket, null); this.sendHandshake(socket); this.fireNewPeerConnection(socket, hs.getPeerId()); } catch (ParseException pe) { logger.debug("Invalid handshake from {}: {}", this.socketRepr(socket), pe.getMessage()); try { socket.close(); } catch (IOException e) { } } catch (IOException ioe) { logger.debug( "An error occured while reading an incoming " + "handshake: {}", ioe.getMessage()); try { if (!socket.isClosed()) { socket.close(); } } catch (IOException e) { // Ignore } } }
public static void main(String[] args) throws IOException { OutputStream outputStream = System.out; in = System.in; out = new PrintWriter(outputStream); Handshake a = new Handshake(); a.solve(); out.close(); }
/** * Validate an expected handshake on a connection. * * <p>Reads an expected handshake message from the given connected socket, parses it and validates * that the torrent hash_info corresponds to the torrent we're sharing, and that the peerId * matches the peer ID we expect to see coming from the remote peer. * * @param socket The connected socket to the remote peer. * @param peerId The peer ID we expect in the handshake. If <em>null</em>, any peer ID is accepted * (this is the case for incoming connections). * @return The validated handshake message object. */ private Handshake validateHandshake(Socket socket, byte[] peerId) throws IOException, ParseException { InputStream is = socket.getInputStream(); // Read the handshake from the wire int pstrlen = is.read(); byte[] data = new byte[Handshake.BASE_HANDSHAKE_LENGTH + pstrlen]; data[0] = (byte) pstrlen; is.read(data, 1, data.length - 1); // Parse and check the handshake Handshake hs = Handshake.parse(ByteBuffer.wrap(data)); if (!Arrays.equals(hs.getInfoHash(), this.torrent.getInfoHash())) { throw new ParseException( "Handshake for unknow torrent " + Torrent.byteArrayToHexString(hs.getInfoHash()) + " from " + this.socketRepr(socket) + ".", pstrlen + 9); } if (peerId != null && !Arrays.equals(hs.getPeerId(), peerId)) { throw new ParseException( "Announced peer ID " + Torrent.byteArrayToHexString(hs.getPeerId()) + " did not match expected peer ID " + Torrent.byteArrayToHexString(peerId) + ".", pstrlen + 29); } return hs; }
@Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { Entry entry = channelMap.get(ctx.getChannel().getRemoteAddress()); if (entry != null) { entry.channel.write(e.getMessage()); return; } ChannelBuffer buffer = (ChannelBuffer) e.getMessage(); Handshake handshake = new Handshake(buffer); Client client = null; ByteString token = null; try { if (handshake.type() == Type.CLIENT_HELLO) token = new ByteString(((ClientHello) handshake.body()).sessionId()); } catch (Exception x) { } client = ClientMap.INSTANCE.clientForToken(token); entry = new Entry(); entry.client = client; entry.dataBuffers.add(buffer.copy()); final Entry entry2 = entry; connectionClient .bootstrap() .connect(client.address()) .addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { Channel channel = future.getChannel(); entry2.channel = channel; ChannelBuffer buffer = entry2.dataBuffers.poll(); if (buffer != null) entry2.channel.write(buffer); } }); channelMap.put(ctx.getChannel().getRemoteAddress(), entry); }
/** Create an {@code SSLSocket} and perform the TLS handshake and certificate validation. */ private void upgradeToTls(TunnelRequest tunnelRequest) throws IOException { Platform platform = Platform.get(); // Make an SSL Tunnel on the first message pair of each SSL + proxy connection. if (requiresTunnel()) { makeTunnel(tunnelRequest); } // Create the wrapper over connected socket. socket = route.address.sslSocketFactory.createSocket( socket, route.address.uriHost, route.address.uriPort, true /* autoClose */); SSLSocket sslSocket = (SSLSocket) socket; if (route.modernTls) { platform.enableTlsExtensions(sslSocket, route.address.uriHost); } else { platform.supportTlsIntolerantServer(sslSocket); } boolean useNpn = route.modernTls && ( // Contains a spdy variant. route.address.protocols.contains(Protocol.HTTP_2) || route.address.protocols.contains(Protocol.SPDY_3)); if (useNpn) { if (route.address.protocols.contains(Protocol.HTTP_2) // Contains both spdy variants. && route.address.protocols.contains(Protocol.SPDY_3)) { platform.setNpnProtocols(sslSocket, Protocol.HTTP2_SPDY3_AND_HTTP); } else if (route.address.protocols.contains(Protocol.HTTP_2)) { platform.setNpnProtocols(sslSocket, Protocol.HTTP2_AND_HTTP_11); } else { platform.setNpnProtocols(sslSocket, Protocol.SPDY3_AND_HTTP11); } } // Force handshake. This can throw! sslSocket.startHandshake(); // Verify that the socket's certificates are acceptable for the target host. if (!route.address.hostnameVerifier.verify(route.address.uriHost, sslSocket.getSession())) { throw new IOException("Hostname '" + route.address.uriHost + "' was not verified"); } out = sslSocket.getOutputStream(); in = sslSocket.getInputStream(); handshake = Handshake.get(sslSocket.getSession()); streamWrapper(); ByteString maybeProtocol; if (useNpn && (maybeProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) { Protocol selectedProtocol = Protocol.find(maybeProtocol); // Throws IOE on unknown. if (selectedProtocol.spdyVariant) { sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream. spdyConnection = new SpdyConnection.Builder(route.address.getUriHost(), true, in, out) .protocol(selectedProtocol) .build(); spdyConnection.sendConnectionHeader(); } } }
/** * Send our handshake message to the socket. * * @param socket The socket to the remote peer. */ private void sendHandshake(Socket socket) throws IOException { OutputStream os = socket.getOutputStream(); os.write( Handshake.craft(this.torrent.getInfoHash(), this.id.getBytes(Torrent.BYTE_ENCODING)) .getBytes()); }
/** Create an {@code SSLSocket} and perform the TLS handshake and certificate validation. */ private void upgradeToTls(TunnelRequest tunnelRequest) throws IOException { Platform platform = Platform.get(); // Make an SSL Tunnel on the first message pair of each SSL + proxy connection. if (requiresTunnel()) { makeTunnel(tunnelRequest); } // Create the wrapper over connected socket. socket = route.address.sslSocketFactory.createSocket( socket, route.address.uriHost, route.address.uriPort, true /* autoClose */); SSLSocket sslSocket = (SSLSocket) socket; if (route.modernTls) { platform.enableTlsExtensions(sslSocket, route.address.uriHost); } else { platform.supportTlsIntolerantServer(sslSocket); } boolean useNpn = route.modernTls && (route.address.transports.contains("HTTP-draft-09/2.0") || route.address.transports.contains("spdy/3")); if (useNpn) { if (route.address.transports.contains("HTTP-draft-09/2.0") && route.address.transports.contains("spdy/3")) { platform.setNpnProtocols(sslSocket, ALL_PROTOCOLS); } else if (route.address.transports.contains("HTTP-draft-09/2.0")) { platform.setNpnProtocols(sslSocket, HTTP2_AND_HTTP); } else { platform.setNpnProtocols(sslSocket, SPDY3_AND_HTTP11); } } // Force handshake. This can throw! sslSocket.startHandshake(); // Verify that the socket's certificates are acceptable for the target host. if (!route.address.hostnameVerifier.verify(route.address.uriHost, sslSocket.getSession())) { throw new IOException("Hostname '" + route.address.uriHost + "' was not verified"); } out = sslSocket.getOutputStream(); in = sslSocket.getInputStream(); handshake = Handshake.get(sslSocket.getSession()); streamWrapper(); byte[] selectedProtocol; if (useNpn && (selectedProtocol = platform.getNpnSelectedProtocol(sslSocket)) != null) { if (Arrays.equals(selectedProtocol, HTTP_20_DRAFT_09) || Arrays.equals(selectedProtocol, SPDY3)) { SpdyConnection.Builder builder = new SpdyConnection.Builder(route.address.getUriHost(), true, in, out); if (Arrays.equals(selectedProtocol, HTTP_20_DRAFT_09)) builder.http20Draft09(); sslSocket.setSoTimeout(0); // SPDY timeouts are set per-stream. spdyConnection = builder.build(); spdyConnection.sendConnectionHeader(); } else if (!Arrays.equals(selectedProtocol, HTTP_11)) { throw new IOException( "Unexpected NPN transport " + new String(selectedProtocol, "ISO-8859-1")); } } }