Skip to content

Commit 6a8c1de

Browse files
committed
Clean up session creation, so it either works or fails explicitly
1 parent 264280c commit 6a8c1de

File tree

3 files changed

+43
-99
lines changed

3 files changed

+43
-99
lines changed

app/src/main/java/tech/httptoolkit/android/ProxyVpnRunnable.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import tech.httptoolkit.android.vpn.ClientPacketWriter
77
import tech.httptoolkit.android.vpn.SessionHandler
88
import tech.httptoolkit.android.vpn.SessionManager
99
import tech.httptoolkit.android.vpn.socket.SocketNIODataService
10-
import tech.httptoolkit.android.vpn.transport.PacketHeaderException
1110
import io.sentry.Sentry
1211
import java.io.FileInputStream
1312
import java.io.FileOutputStream

app/src/main/java/tech/httptoolkit/android/vpn/SessionHandler.java

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,29 +91,24 @@ public void handlePacket(@NonNull ByteBuffer stream) throws PacketHeaderExceptio
9191
handleICMPPacket(stream, ipHeader);
9292
} else {
9393
Log.w(TAG, "Unsupported IP protocol: " + ipHeader.getProtocol());
94-
return;
9594
}
9695
}
9796

98-
private void handleUDPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader) throws PacketHeaderException {
97+
private void handleUDPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader) throws PacketHeaderException, IOException {
9998
UDPHeader udpheader = UDPPacketFactory.createUDPHeader(clientPacketData);
10099

101100
Session session = manager.getSession(
102101
ipHeader.getDestinationIP(), udpheader.getDestinationPort(),
103102
ipHeader.getSourceIP(), udpheader.getSourcePort()
104103
);
105104

106-
if (session == null){
105+
if (session == null) {
107106
session = manager.createNewUDPSession(
108107
ipHeader.getDestinationIP(), udpheader.getDestinationPort(),
109108
ipHeader.getSourceIP(), udpheader.getSourcePort()
110109
);
111110
}
112111

113-
if(session == null){
114-
return;
115-
}
116-
117112
synchronized (session) {
118113
session.setLastIpHeader(ipHeader);
119114
session.setLastUdpHeader(udpheader);
@@ -130,15 +125,15 @@ private void handleUDPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader) t
130125
manager.keepSessionAlive(session);
131126
}
132127

133-
private void handleTCPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader) throws PacketHeaderException {
128+
private void handleTCPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader) throws PacketHeaderException, IOException {
134129
TCPHeader tcpheader = TCPPacketFactory.createTCPHeader(clientPacketData);
135130
int dataLength = clientPacketData.limit() - clientPacketData.position();
136131
int sourceIP = ipHeader.getSourceIP();
137132
int destinationIP = ipHeader.getDestinationIP();
138133
int sourcePort = tcpheader.getSourcePort();
139134
int destinationPort = tcpheader.getDestinationPort();
140135

141-
if(tcpheader.isSYN()) {
136+
if (tcpheader.isSYN()) {
142137
// 3-way handshake + create new session
143138
replySynAck(ipHeader,tcpheader);
144139
} else if(tcpheader.isACK()) {
@@ -373,18 +368,16 @@ private void resetConnection(IPv4Header ip, TCPHeader tcp){
373368
* @param ip IP
374369
* @param tcp TCP
375370
*/
376-
private void replySynAck(IPv4Header ip, TCPHeader tcp){
371+
private void replySynAck(IPv4Header ip, TCPHeader tcp) throws IOException {
377372
ip.setIdentification(0);
378373
Packet packet = TCPPacketFactory.createSynAckPacketData(ip, tcp);
379374

380375
TCPHeader tcpheader = (TCPHeader) packet.getTransportHeader();
381376

382377
Session session = manager.createNewTCPSession(
383-
ip.getDestinationIP(), tcp.getDestinationPort(),
384-
ip.getSourceIP(), tcp.getSourcePort()
378+
ip.getDestinationIP(), tcp.getDestinationPort(),
379+
ip.getSourceIP(), tcp.getSourcePort()
385380
);
386-
if(session == null)
387-
return;
388381

389382
synchronized (session) {
390383
session.setMaxSegmentSize(tcpheader.getMaxSegmentSize());

app/src/main/java/tech/httptoolkit/android/vpn/SessionManager.java

Lines changed: 36 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import android.util.Log;
2222
import android.util.SparseArray;
2323

24+
import org.jetbrains.annotations.NotNull;
25+
2426
import tech.httptoolkit.android.TagKt;
2527
import tech.httptoolkit.android.vpn.socket.DataConst;
2628
import tech.httptoolkit.android.vpn.socket.ICloseSession;
@@ -89,7 +91,7 @@ public Session getSession(int ip, int port, int srcIp, int srcPort) {
8991

9092
@Nullable
9193
public Session getSessionByKey(String key) {
92-
if(table.containsKey(key)){
94+
if (table.containsKey(key)) {
9395
return table.get(key);
9496
}
9597

@@ -126,92 +128,64 @@ public void closeSession(@NonNull Session session){
126128
session.getSourcePort());
127129
}
128130

129-
@Nullable
130-
public Session createNewUDPSession(int ip, int port, int srcIp, int srcPort) {
131+
@NotNull
132+
public Session createNewUDPSession(int ip, int port, int srcIp, int srcPort) throws IOException {
131133
String keys = Session.getSessionKey(ip, port, srcIp, srcPort);
132134

133-
if (table.containsKey(keys))
135+
if (table.containsKey(keys)) {
136+
// For TCP, we freak out if you try to create an already existing session.
137+
// With UDP though, it's totally fine:
134138
return table.get(keys);
139+
}
135140

136141
Session session = new Session(srcIp, srcPort, ip, port, this);
137142

138143
DatagramChannel channel;
139144

140-
try {
141-
channel = DatagramChannel.open();
142-
channel.socket().setSoTimeout(0);
143-
channel.configureBlocking(false);
144-
} catch (IOException e) {
145-
e.printStackTrace();
146-
return null;
147-
}
145+
channel = DatagramChannel.open();
146+
channel.socket().setSoTimeout(0);
147+
channel.configureBlocking(false);
148148
protector.protect(channel.socket());
149149

150150
session.setChannel(channel);
151151

152-
//initiate connection to reduce latency
152+
// Initiate connection early to reduce latency
153153
String ips = PacketUtil.intToIPAddress(ip);
154154
String sourceIpAddress = PacketUtil.intToIPAddress(srcIp);
155155
SocketAddress socketAddress = new InetSocketAddress(ips, port);
156156
Log.d(TAG,"initialized connection to remote UDP server: " + ips + ":" +
157157
port + " from " + sourceIpAddress + ":" + srcPort);
158158

159-
try {
160-
channel.connect(socketAddress);
161-
session.setConnected(channel.isConnected());
162-
} catch(IOException e) {
163-
e.printStackTrace();
164-
return null;
165-
}
166-
167-
try {
168-
nioService.registerSession(session);
169-
} catch (ClosedChannelException e) {
170-
e.printStackTrace();
171-
Log.e(TAG,"failed to register udp channel with selector: "+ e.getMessage());
172-
return null;
173-
}
159+
channel.connect(socketAddress);
160+
session.setConnected(channel.isConnected());
174161

175-
if (table.containsKey(keys)) {
176-
try {
177-
channel.close();
178-
} catch (IOException e) {
179-
e.printStackTrace();
180-
return null;
181-
}
182-
} else {
183-
table.put(keys, session);
184-
}
162+
nioService.registerSession(session);
163+
table.put(keys, session);
185164

186165
Log.d(TAG,"new UDP session successfully created.");
187166
return session;
188167
}
189168

190-
@Nullable
191-
public Session createNewTCPSession(int ip, int port, int srcIp, int srcPort){
169+
@NotNull
170+
public Session createNewTCPSession(int ip, int port, int srcIp, int srcPort) throws IOException {
192171
String key = Session.getSessionKey(ip, port, srcIp, srcPort);
193172
if (table.containsKey(key)) {
194-
Log.e(TAG, "Session " + key + " was already created.");
195-
return null;
173+
// This can happen if we receive two SYN packets somehow. That shouldn't happen, especially
174+
// given that our connection is local & should be 100% reliable, but it does. We probably
175+
// should RST, for now we just throw here (and so drop the packet entirely).
176+
throw new IllegalArgumentException("Can't create duplicate session");
196177
}
197178

198179
Session session = new Session(srcIp, srcPort, ip, port, this);
199180

200181
SocketChannel channel;
201-
try {
202-
channel = SocketChannel.open();
203-
channel.socket().setKeepAlive(true);
204-
channel.socket().setTcpNoDelay(true);
205-
channel.socket().setSoTimeout(0);
206-
channel.socket().setReceiveBufferSize(DataConst.MAX_RECEIVE_BUFFER_SIZE);
207-
channel.configureBlocking(false);
208-
} catch(SocketException e) {
209-
Log.e(TAG, e.toString());
210-
return null;
211-
} catch (IOException e) {
212-
Log.e(TAG,"Failed to create SocketChannel: "+ e.getMessage());
213-
return null;
214-
}
182+
channel = SocketChannel.open();
183+
channel.socket().setKeepAlive(true);
184+
channel.socket().setTcpNoDelay(true);
185+
channel.socket().setSoTimeout(0);
186+
channel.socket().setReceiveBufferSize(DataConst.MAX_RECEIVE_BUFFER_SIZE);
187+
channel.configureBlocking(false);
188+
215189
String ips = PacketUtil.intToIPAddress(ip);
216190
Log.d(TAG,"created new SocketChannel for " + key);
217191

@@ -220,42 +194,20 @@ public Session createNewTCPSession(int ip, int port, int srcIp, int srcPort){
220194

221195
session.setChannel(channel);
222196

223-
//initiate connection to reduce latency
224-
// Use the real address, unless tcpPortRedirection defines a different
197+
// Initiate connection straight away, to reduce latency
198+
// We use the real address, unless tcpPortRedirection redirects us to a different
225199
// target address for traffic on this port.
226200
SocketAddress socketAddress = tcpPortRedirection.get(port) != null
227201
? tcpPortRedirection.get(port)
228202
: new InetSocketAddress(ips, port);
229203

230-
Log.d(TAG,"initiate connecting to remote tcp server: " + socketAddress.toString());
231-
boolean connected;
232-
try{
233-
connected = channel.connect(socketAddress);
234-
} catch(IOException e) {
235-
Log.e(TAG, e.toString());
236-
return null;
237-
}
238-
204+
Log.d(TAG,"Initiate connecting to remote tcp server: " + socketAddress.toString());
205+
boolean connected = channel.connect(socketAddress);
239206
session.setConnected(connected);
240207

241-
try {
242-
nioService.registerSession(session);
243-
} catch (ClosedChannelException e) {
244-
e.printStackTrace();
245-
Log.e(TAG,"Failed to register channel with selector: " + e.getMessage());
246-
return null;
247-
}
208+
nioService.registerSession(session);
209+
table.put(key, session);
248210

249-
if (table.containsKey(key)) {
250-
try {
251-
channel.close();
252-
} catch (IOException e) {
253-
e.printStackTrace();
254-
}
255-
return null;
256-
} else {
257-
table.put(key, session);
258-
}
259211
return session;
260212
}
261213

0 commit comments

Comments
 (0)