Skip to content

Commit f52c803

Browse files
Better IP resolve to prevent timeouts
1 parent cd666e8 commit f52c803

10 files changed

Lines changed: 311 additions & 34 deletions

src/io/github/explodingbottle/jmagicproxy/HardcodedConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public static String getConfigFileName() {
4646
* @return The buffer size.
4747
*/
4848
public static int returnBufferSize() {
49-
return 16384; // Note: I tried before allowing to change the buffer size but, for example, a
49+
return 65536; // Note: I tried before allowing to change the buffer size but, for example, a
5050
// buffer size of 16 makes cURL not getting the whole content.
5151
}
5252
}

src/io/github/explodingbottle/jmagicproxy/ProxyMain.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ public static void main(String[] args) {
124124
if (ovc != null) {
125125
config = ovc;
126126
}
127+
System.out.println("Please report any bugs to https://github.com/ExplodingBottle/JMagicProxy/issues so a fix can be found.");
128+
System.out.println();
127129
lgp = new LoggerProvider(true);
128130
shutdownThread = new ShutdownThread();
129131
Runtime.getRuntime().addShutdownHook(shutdownThread);

src/io/github/explodingbottle/jmagicproxy/proxy/ConnectionDirectiveHandler.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.io.IOException;
2222
import java.io.InputStream;
2323
import java.io.OutputStream;
24-
import java.net.InetAddress;
2524
import java.net.Socket;
2625

2726
import io.github.explodingbottle.jmagicproxy.ProxyMain;
@@ -30,6 +29,8 @@
3029
import io.github.explodingbottle.jmagicproxy.logging.LoggingLevel;
3130
import io.github.explodingbottle.jmagicproxy.logging.ProxyLogger;
3231
import io.github.explodingbottle.jmagicproxy.proxy.ssl.SSLComunicator;
32+
import io.github.explodingbottle.jmagicproxy.socketopener.SocketOpeningTool;
33+
import io.github.explodingbottle.jmagicproxy.socketopener.StandardSocketOpener;
3334

3435
/**
3536
* This class contains code to handle directives.
@@ -173,14 +174,37 @@ public void openSocket() {
173174
} else {
174175
logger.log(LoggingLevel.INFO, "Opening outgoing socket for " + directive.getHost() + ":"
175176
+ directive.getPort() + " with request " + directive.getOutcomingRequest().toHttpRequestLine());
177+
SocketOpeningTool openingTool = new SocketOpeningTool(directive.getHost(), directive.getPort(),
178+
new StandardSocketOpener(), (s) -> {
179+
if (s == null) {
180+
logger.log(LoggingLevel.WARN, "Failed to open the outgoing socket.");
181+
closeSocket();
182+
} else {
183+
try {
184+
referenceSocket = s;
185+
inputStream = referenceSocket.getInputStream();
186+
outputStream = referenceSocket.getOutputStream();
187+
rewriteDirectiveLine();
188+
pipeThread = new SimpleInputOutputPipeThread(inputStream,
189+
handlerThread.getOutputStream(), this);
190+
pipeThread.start();
191+
logger.log(LoggingLevel.INFO, "Outgoing socket opened.");
192+
} catch (IOException e) {
193+
logger.log(LoggingLevel.WARN, "Failed to open the outgoing socket.", e);
194+
closeSocket();
195+
}
196+
}
197+
});
176198
try {
177-
referenceSocket = new Socket(InetAddress.getByName(directive.getHost()), directive.getPort());
178-
inputStream = referenceSocket.getInputStream();
179-
outputStream = referenceSocket.getOutputStream();
180-
rewriteDirectiveLine();
181-
pipeThread = new SimpleInputOutputPipeThread(inputStream, handlerThread.getOutputStream(), this);
182-
pipeThread.start();
183-
logger.log(LoggingLevel.INFO, "Outgoing socket opened.");
199+
200+
openingTool.run();
201+
/*
202+
* referenceSocket = new Socket(InetAddress.getByName(directive.getHost()),
203+
* directive.getPort()); inputStream = referenceSocket.getInputStream();
204+
* outputStream = referenceSocket.getOutputStream(); rewriteDirectiveLine();
205+
* pipeThread = new SimpleInputOutputPipeThread(inputStream,
206+
* handlerThread.getOutputStream(), this); pipeThread.start();
207+
*/
184208
} catch (Exception e) {
185209
logger.log(LoggingLevel.WARN, "Failed to open the outgoing socket.", e);
186210
closeSocket();

src/io/github/explodingbottle/jmagicproxy/proxy/ssl/SSLDirectiveHandler.java

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.io.IOException;
2222
import java.io.InputStream;
2323
import java.io.OutputStream;
24-
import java.net.InetAddress;
2524
import java.net.Socket;
2625

2726
import javax.net.ssl.SSLSocket;
@@ -30,6 +29,9 @@
3029
import io.github.explodingbottle.jmagicproxy.api.SSLControlDirective;
3130
import io.github.explodingbottle.jmagicproxy.logging.LoggingLevel;
3231
import io.github.explodingbottle.jmagicproxy.logging.ProxyLogger;
32+
import io.github.explodingbottle.jmagicproxy.socketopener.SSLSocketOpener;
33+
import io.github.explodingbottle.jmagicproxy.socketopener.SocketOpeningTool;
34+
import io.github.explodingbottle.jmagicproxy.socketopener.StandardSocketOpener;
3335

3436
/**
3537
* The main goal of this class is to assure an outgoing SSL connection.
@@ -113,32 +115,51 @@ public void openSocket() {
113115
} else {
114116
if (directive.isSSL()) {
115117
selfLogger.log(LoggingLevel.INFO, "The connection will be using outgoing SSL");
116-
try {
117-
outgoingSocket = obProv.getFactoryOutgoing()
118-
.createSocket(InetAddress.getByName(directive.getHost()), directive.getPort());
119-
((SSLSocket) outgoingSocket).startHandshake();
120-
inputStream = outgoingSocket.getInputStream();
121-
outputStream = outgoingSocket.getOutputStream();
122-
rewriteDirectiveLine();
123-
ioPipe = new SSLInputOutputPipeThread(inputStream, parent.getHeartOutput(), this);
124-
ioPipe.start();
125-
} catch (IOException e) {
126-
selfLogger.log(LoggingLevel.WARN, "Failed to open the outgoing SSL socket.", e);
127-
finishHandler(true);
128-
}
118+
SocketOpeningTool openingTool = new SocketOpeningTool(directive.getHost(), directive.getPort(),
119+
new SSLSocketOpener(obProv.getFactoryOutgoing()), (s) -> {
120+
if (s == null) {
121+
selfLogger.log(LoggingLevel.WARN, "Failed to open the outgoing SSL socket.");
122+
finishHandler(true);
123+
} else {
124+
try {
125+
outgoingSocket = s;
126+
((SSLSocket) outgoingSocket).startHandshake();
127+
inputStream = outgoingSocket.getInputStream();
128+
outputStream = outgoingSocket.getOutputStream();
129+
rewriteDirectiveLine();
130+
ioPipe = new SSLInputOutputPipeThread(inputStream, parent.getHeartOutput(), this);
131+
ioPipe.start();
132+
} catch (IOException e) {
133+
selfLogger.log(LoggingLevel.WARN, "Failed to open the outgoing SSL socket.", e);
134+
finishHandler(true);
135+
}
136+
}
137+
});
138+
openingTool.run();
139+
129140
} else {
130141
selfLogger.log(LoggingLevel.INFO, "The connection will be using outgoing standard HTTP.");
131-
try {
132-
outgoingSocket = new Socket(InetAddress.getByName(directive.getHost()), directive.getPort());
133-
inputStream = outgoingSocket.getInputStream();
134-
outputStream = outgoingSocket.getOutputStream();
135-
rewriteDirectiveLine();
136-
ioPipe = new SSLInputOutputPipeThread(inputStream, parent.getHeartOutput(), this);
137-
ioPipe.start();
138-
} catch (IOException e) {
139-
selfLogger.log(LoggingLevel.WARN, "Failed to open the outgoing standard socket.", e);
140-
finishHandler(true);
141-
}
142+
SocketOpeningTool openingTool = new SocketOpeningTool(directive.getHost(), directive.getPort(),
143+
new StandardSocketOpener(), (s) -> {
144+
if (s == null) {
145+
selfLogger.log(LoggingLevel.WARN, "Failed to open the outgoing standard socket.");
146+
finishHandler(true);
147+
} else {
148+
try {
149+
outgoingSocket = s;
150+
inputStream = outgoingSocket.getInputStream();
151+
outputStream = outgoingSocket.getOutputStream();
152+
rewriteDirectiveLine();
153+
ioPipe = new SSLInputOutputPipeThread(inputStream, parent.getHeartOutput(), this);
154+
ioPipe.start();
155+
} catch (IOException e) {
156+
selfLogger.log(LoggingLevel.WARN, "Failed to open the outgoing standard socket.",
157+
e);
158+
finishHandler(true);
159+
}
160+
}
161+
});
162+
openingTool.run();
142163
}
143164
}
144165
}

src/io/github/explodingbottle/jmagicproxy/proxy/ssl/SSLInputOutputPipeThread.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ public void run() {
143143
logger.log(LoggingLevel.INFO, "Signaling pipe startup for SSL.");
144144
try {
145145
int read = in.read(transferBuffer, 0, transferBuffer.length);
146+
logger.log(LoggingLevel.INFO, "SSL Pipe has read for the first time " + read + " bytes.");
146147
while (!interrupted() && read != -1) {
147148
Integer offset = null;
148149
if (canParseHeader) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.github.explodingbottle.jmagicproxy.socketopener;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
import java.net.Socket;
6+
7+
import javax.net.ssl.SSLSocketFactory;
8+
9+
/**
10+
*
11+
* This opener will create a SSL socket.
12+
*
13+
* @author ExplodingBottle
14+
*
15+
*/
16+
public class SSLSocketOpener implements SocketOpener {
17+
18+
private SSLSocketFactory socketFactory;
19+
20+
/**
21+
* This constructor will prepare the opener using a SSL Socket Factory.
22+
*
23+
* @param socketFactory Represents the SSL Socket Factory that will be used.
24+
*/
25+
public SSLSocketOpener(SSLSocketFactory socketFactory) {
26+
this.socketFactory = socketFactory;
27+
}
28+
29+
@Override
30+
public Socket openedSocket(InetAddress host, int port) throws IOException {
31+
return socketFactory.createSocket(host, port);
32+
}
33+
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.github.explodingbottle.jmagicproxy.socketopener;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
import java.net.Socket;
6+
7+
/**
8+
* This class represents a Socket opener.
9+
*
10+
* @author ExplodingBottle
11+
*
12+
*/
13+
public interface SocketOpener {
14+
15+
/**
16+
* Opens a socket using parameters.
17+
*
18+
* @param host The host to connect to.
19+
* @param port The port to connect to.
20+
* @return The opened socket.
21+
* @throws IOException If there was any issues while opening the socket.
22+
*/
23+
public Socket openedSocket(InetAddress host, int port) throws IOException;
24+
25+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.github.explodingbottle.jmagicproxy.socketopener;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
6+
/**
7+
*
8+
* This class is used to try to initiate a connection.
9+
*
10+
* @author ExplodingBottle
11+
*
12+
*/
13+
class SocketOpeningThread extends Thread {
14+
15+
private InetAddress inetAddress;
16+
private int port;
17+
private SocketOpeningTool parent;
18+
private SocketOpener opener;
19+
20+
SocketOpeningThread(InetAddress inetAddress, int port, SocketOpeningTool parent, SocketOpener opener) {
21+
this.inetAddress = inetAddress;
22+
this.port = port;
23+
this.parent = parent;
24+
this.opener = opener;
25+
}
26+
27+
@Override
28+
public void run() {
29+
try {
30+
parent.receiveSocket(opener.openedSocket(inetAddress, port), this);
31+
} catch (IOException e) {
32+
parent.receiveSocket(null, this);
33+
}
34+
}
35+
36+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package io.github.explodingbottle.jmagicproxy.socketopener;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
import java.net.Socket;
6+
import java.net.UnknownHostException;
7+
import java.util.ArrayList;
8+
import java.util.Collections;
9+
import java.util.List;
10+
import java.util.function.Consumer;
11+
12+
import io.github.explodingbottle.jmagicproxy.ProxyMain;
13+
import io.github.explodingbottle.jmagicproxy.logging.LoggingLevel;
14+
import io.github.explodingbottle.jmagicproxy.logging.ProxyLogger;
15+
16+
/**
17+
* This class will create a socket by resolving the IP. It is now here because
18+
* it appeared that some issues were occurring with
19+
* {@code InetAddress.getByName}.
20+
*
21+
* @author ExplodingBottle
22+
*
23+
*/
24+
public class SocketOpeningTool {
25+
26+
private String host;
27+
private int port;
28+
private SocketOpener opener;
29+
30+
private boolean hasBeenFound = false;
31+
private Consumer<Socket> callback;
32+
33+
private boolean callBackCalled = false;
34+
35+
private List<SocketOpeningThread> threads;
36+
37+
private ProxyLogger logger;
38+
39+
/**
40+
* Builds an opening tool using the parameters.
41+
*
42+
* @param host The host name to try to connect to.
43+
* @param port The port to try to connect to.
44+
* @param opener The opener that will be used in order to open a Socket.
45+
* @param callback The callback that will be ran once the socket is ready. Will
46+
* obtain a socket or {@code null} if every sockets has failed
47+
* to connect.
48+
*/
49+
public SocketOpeningTool(String host, int port, SocketOpener opener, Consumer<Socket> callback) {
50+
this.host = host;
51+
this.port = port;
52+
this.opener = opener;
53+
this.callback = callback;
54+
this.logger = ProxyMain.getLoggerProvider().createLogger();
55+
this.threads = Collections.synchronizedList(new ArrayList<SocketOpeningThread>());
56+
}
57+
58+
/**
59+
* This method will start the test.
60+
*
61+
*/
62+
public void run() {
63+
InetAddress[] resolvedAddresses;
64+
try {
65+
resolvedAddresses = InetAddress.getAllByName(host);
66+
} catch (UnknownHostException e) {
67+
logger.log(LoggingLevel.WARN, "Failed to get IPs of an unknown host.", e);
68+
callback.accept(null);
69+
return;
70+
}
71+
for (InetAddress address : resolvedAddresses) {
72+
SocketOpeningThread thread = new SocketOpeningThread(address, port, this, opener);
73+
threads.add(thread);
74+
thread.start();
75+
}
76+
// If someone has a proper fix, please do a Pull Request. This code is
77+
// technically bad.
78+
while (!callBackCalled) {
79+
Thread.yield();
80+
}
81+
}
82+
83+
/**
84+
* This function will be called by a started check thread in order to send the
85+
* first opened socket.
86+
*
87+
* @param received The socket that has been the first opened.
88+
* @param thread The thread from which comes the request.
89+
*/
90+
void receiveSocket(Socket received, SocketOpeningThread thread) {
91+
threads.remove(thread);
92+
if (received != null) {
93+
if (!hasBeenFound) {
94+
hasBeenFound = true;
95+
logger.log(LoggingLevel.INFO, "We found a socket for connection " + host + ":" + port + " for IP "
96+
+ received.getInetAddress() + ".");
97+
callback.accept(received);
98+
callBackCalled = true;
99+
} else {
100+
try {
101+
received.close();
102+
} catch (IOException e) {
103+
logger.log(LoggingLevel.WARN, "Failed to close a connection.", e);
104+
}
105+
}
106+
}
107+
if (!hasBeenFound && threads.size() == 0) {
108+
logger.log(LoggingLevel.WARN, "We found NO socket for connection " + host + ":" + port + ".");
109+
callback.accept(null);
110+
}
111+
}
112+
113+
}

0 commit comments

Comments
 (0)