control_socket = new Socket( Proxy.NO_PROXY );
control_socket.connect( new InetSocketAddress( socks_host, socks_port ));
DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( control_socket.getOutputStream(), 256 ));
DataInputStream dis = new DataInputStream( control_socket.getInputStream());
dos.writeByte( (byte)5 ); // socks 5
dos.writeByte( (byte)2 ); // 2 methods
dos.writeByte( (byte)0 ); // no auth
dos.writeByte( (byte)2 ); // user/pw
dos.flush();
dis.readByte(); // version byte
byte method = dis.readByte();
if ( method != 0 && method != 2 ){
throw new IOException( "SOCKS 5: no valid method [" + method + "]" );
}
// auth
if ( method == 2 ) {
dos.writeByte( (byte)1 ); // user/pw version
dos.writeByte( (byte)socks_user.length() ); // user length
dos.write( socks_user.getBytes() );
dos.writeByte( (byte)socks_password.length() ); // password length
dos.write( socks_password.getBytes() );
dos.flush();
dis.readByte(); // version byte
byte status = dis.readByte();
if ( status != 0 ){
throw( new IOException( "SOCKS 5: authentication fails [status=" +status+ "]" ));
}
}
String mapped_ip;
if ( target.isUnresolved() || target.getAddress() == null ){
// deal with long "hostnames" that we get for, e.g., I2P destinations
mapped_ip = AEProxyFactory.getAddressMapper().internalise( target.getHostName() );
}else{
mapped_ip = target.getAddress().getHostName();
}
dos.writeByte( (byte)5 ); // version
dos.writeByte( (byte)3 ); // udp associate
dos.writeByte( (byte)0 ); // reserved
dos.writeByte((byte)1);
dos.write( new byte[4] );
dos.writeShort( (short)delegate.getPort()); // port
dos.flush();
dis.readByte(); // ver
byte reply = dis.readByte();
if ( reply != 0 ){
throw( new IOException( "SOCKS 5: udp association fails [reply=" +reply+ "]" ));
}
dis.readByte(); // reserved
InetAddress relay_address;
byte atype = dis.readByte();
if ( atype == 1 ){
byte[] bytes = new byte[4];
dis.readFully( bytes );
relay_address = InetAddress.getByAddress( bytes );
}else if ( atype == 3 ){
byte len = dis.readByte();
byte[] bytes = new byte[(int)len&0xff ];
dis.readFully( bytes );
relay_address = InetAddress.getByName( new String( bytes ));
}else{
byte[] bytes = new byte[16];
dis.readFully( bytes );
relay_address = InetAddress.getByAddress( bytes );
}
int relay_port = ((dis.readByte()<<8)&0xff00) | (dis.readByte() & 0x00ff );
if ( relay_address.isAnyLocalAddress()){
relay_address = control_socket.getInetAddress();
}
relay = new InetSocketAddress( relay_address, relay_port );
// use the maped ip for dns resolution so we don't leak the
// actual address if this is a secure one (e.g. I2P one)
ByteArrayOutputStream baos_temp = new ByteArrayOutputStream();
DataOutputStream dos_temp = new DataOutputStream( baos_temp );
dos_temp.writeByte(0); // resv
dos_temp.writeByte(0); // resv
dos_temp.writeByte(0); // frag (none)
try {
byte[] ip_bytes = HostNameToIPResolver.syncResolve( mapped_ip ).getAddress();
dos_temp.writeByte( ip_bytes.length==4?(byte)1:(byte)4 );
dos_temp.write( ip_bytes );
}catch( Throwable e ){
dos_temp.writeByte( (byte)3 ); // address type = domain name
dos_temp.writeByte( (byte)mapped_ip.length() ); // address type = domain name
dos_temp.write( mapped_ip.getBytes() );
}
dos_temp.writeShort( (short)target.getPort() ); // port
dos_temp.flush();
packet_out_header = baos_temp.toByteArray();
ok = true;