-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Open
Labels
Description
I know this may not be within the scope of your answer, but I still hope you can help me out.
I want to use Java to construct a Trojan request, and it does follow the data in the document, but it never works. This is my code.
I'm sorry, I don't know where the problem is.
package org.example;
import org.example.entity.TrojanWrapperRequest;
import org.example.util.Sha224Util;
import javax.net.ssl.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author mohuangNPC
* @version 1.0
* @date 2024/11/4 9:19
*/
public class TrojanMainTest {
private static final int LISTEN_PORT = 7890;
private static final String TROJAN_HOST = "xxx.xxx.xxx";
private static final int TROJAN_PORT = 443;
private static final String password = "password1";
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(LISTEN_PORT)) {
System.out.println("Trojan proxy server listening on port " + LISTEN_PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The specific thread that accepts the request
* +-----------------------+---------+----------------+---------+----------+
* | hex(SHA224(password)) | CRLF | Trojan Request | CRLF | Payload |
* +-----------------------+---------+----------------+---------+----------+
* | 56 | X'0D0A' | Variable | X'0D0A' | Variable |
* +-----------------------+---------+----------------+---------+----------+
*
* where Trojan Request is a SOCKS5-like request:
*
* +-----+------+----------+----------+
* | CMD | ATYP | DST.ADDR | DST.PORT |
* +-----+------+----------+----------+
* | 1 | 1 | Variable | 2 |
* +-----+------+----------+----------+
*/
static class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
long threadId = Thread.currentThread().getId();
String requestLine = "";
try (InputStream clientInput = clientSocket.getInputStream();
OutputStream clientOutput = clientSocket.getOutputStream()) {
TrojanWrapperRequest trojanWrapperRequest = new TrojanWrapperRequest();
TrojanWrapperRequest.TrojanRequest trojanRequest = new TrojanWrapperRequest.TrojanRequest();
// Set password and Request
{
trojanRequest.setCmd(0X01);
String dstAddr = "";
int dstPort = 443;
int atyp = 1;
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int byteRead;
while ((byteRead = clientInput.read()) != -1) {
if (byteRead == '\n') {
break;
}
baos.write(byteRead);
}
requestLine = baos.toString(StandardCharsets.UTF_8.name());
System.err.println(threadId+"----Received line: " + requestLine);
if (requestLine != null) {
String[] parts = requestLine.split(" ");
if (parts.length > 1) {
String url = parts[1];
String host = extractHostDomain(url);
int port = extractPort(url);
System.err.println(threadId+"----Host: " + host);
dstAddr = host;
System.err.println(threadId+"----Port: " + port);
dstPort = port;
System.err.println(threadId+"----Is IP: " + isValidIPAddress(host));
if (isValidIPAddress(host)) {
atyp = 1;
} else {
atyp = 3;
}
}
}
}
trojanRequest.setAtyp(atyp);
trojanRequest.setDstPort(dstPort);
trojanRequest.setDstAddr(dstAddr);
}
trojanWrapperRequest.setPassword(Sha224Util.encryptThisString(password));
trojanWrapperRequest.setTrojanRequest(trojanRequest);
// Do not verify certificates
{
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory factory = sc.getSocketFactory();
try (SSLSocket trojanSocket = (SSLSocket) factory.createSocket(TROJAN_HOST, TROJAN_PORT);
InputStream trojanInput = trojanSocket.getInputStream();
OutputStream trojanOutput = trojanSocket.getOutputStream()) {
// begin handshake
trojanSocket.startHandshake();
System.err.println(threadId+"----trojan connect success");
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Assemble the data according to the Trojan protocol
{
out.write(trojanWrapperRequest.getPassword().getBytes(StandardCharsets.UTF_8));
out.write(0X0D);
out.write(0X0A);
out.write(trojanRequest.getCmd());
out.write(trojanRequest.getAtyp());
encodeAddress(trojanRequest.getAtyp(), out, trojanRequest.getDstAddr());
out.write((trojanRequest.getDstPort() >> 8) & 0xFF);
out.write(trojanRequest.getDstPort() & 0xFF);
out.write(0X0D);
out.write(0X0A);
// The domain name port has been read once when obtaining it, so it needs to be added here
out.write(requestLine.getBytes(StandardCharsets.UTF_8));
}
byte[] byteArray = out.toByteArray();
trojanOutput.write(byteArray);
Thread thread = new Thread(() -> {
clientToProxy(threadId, clientInput, trojanOutput);
});
thread.start();
clientToProxy(threadId,clientInput, trojanOutput);
proxyToClient(threadId,trojanInput, clientOutput);
// Waiting for the client to proxy forwarding to complete
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* Data from the client to the server
* @param threadId
* @param input
* @param output
*/
private static void clientToProxy(long threadId,InputStream input, OutputStream output) {
byte[] buffer = new byte[4096];
int bytesRead;
try {
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Response from the proxy server
* @param threadId
* @param input
* @param output
*/
private static void proxyToClient(long threadId,InputStream input, OutputStream output) {
byte[] buffer = new byte[4096];
int bytesRead;
try {
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static String extractHost(String url) {
if (url.startsWith("http://")) {
url = url.substring(7);
} else if (url.startsWith("https://")) {
url = url.substring(8);
}
String s = url.split("/")[0];
return url.split("/")[0];
}
/**
* Get the domain name or IP
* @param url
* @return
*/
private static String extractHostDomain(String url) {
if (url.startsWith("http://")) {
url = url.substring(7);
} else if (url.startsWith("https://")) {
url = url.substring(8);
}
String s = url.split("/")[0];
return s.split(":")[0];
}
/**
* Get Port
* @param url
* @return
*/
private static int extractPort(String url) {
int defaultPort = url.startsWith("https://") ? 443 : 80;
String host = extractHost(url);
if (host.contains(":")) {
String[] parts = host.split(":");
return Integer.parseInt(parts[1]);
}
return defaultPort;
}
/**
* Determine whether it is an IP or a domain name
* @param ip
* @return
*/
private static boolean isValidIPAddress(String ip) {
String ipPattern =
"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
return ip.matches(ipPattern);
}
/**
* Processing IP or domain name
* @param addressType
* @param out
* @param dstAddr
* @throws IOException
*/
private static void encodeAddress(int addressType, ByteArrayOutputStream out, String dstAddr) throws IOException {
if (addressType == 1) {
String[] split = dstAddr.split("\\.");
for (String item : split) {
int b = Integer.parseInt(item);
out.write(b);
}
} else if (addressType == 3) {
out.write(dstAddr.length());
out.write(dstAddr.getBytes(StandardCharsets.UTF_8));
} else {
throw new RuntimeException("error address");
}
}
}