1717package tech .httptoolkit .android .vpn ;
1818
1919import java .io .IOException ;
20+ import java .net .InetAddress ;
2021import java .nio .ByteBuffer ;
2122import java .nio .channels .SelectionKey ;
23+ import java .util .concurrent .ExecutorService ;
24+ import java .util .concurrent .Executors ;
2225
2326import tech .httptoolkit .android .vpn .network .ip .IPPacketFactory ;
2427import tech .httptoolkit .android .vpn .network .ip .IPv4Header ;
2528import tech .httptoolkit .android .vpn .socket .SocketNIODataService ;
26- import tech .httptoolkit .android .vpn .transport .tcp .PacketHeaderException ;
29+ import tech .httptoolkit .android .vpn .transport .PacketHeaderException ;
30+ import tech .httptoolkit .android .vpn .transport .icmp .ICMPPacket ;
31+ import tech .httptoolkit .android .vpn .transport .icmp .ICMPPacketFactory ;
2732import tech .httptoolkit .android .vpn .transport .tcp .TCPHeader ;
2833import tech .httptoolkit .android .vpn .transport .tcp .TCPPacketFactory ;
29- import tech .httptoolkit .android .vpn .transport .ITransportHeader ;
3034import tech .httptoolkit .android .vpn .transport .udp .UDPHeader ;
3135import tech .httptoolkit .android .vpn .transport .udp .UDPPacketFactory ;
3236import tech .httptoolkit .android .vpn .util .PacketUtil ;
@@ -48,13 +52,41 @@ public class SessionHandler {
4852 private final SocketNIODataService nioService ;
4953 private final ClientPacketWriter writer ;
5054
55+ private final ExecutorService pingThreadpool = Executors .newCachedThreadPool ();
56+
5157 public SessionHandler (SessionManager manager , SocketNIODataService nioService , ClientPacketWriter writer ) {
5258 this .manager = manager ;
5359 this .nioService = nioService ;
5460 this .writer = writer ;
5561 }
5662
57- private void handleUDPPacket (ByteBuffer clientPacketData , IPv4Header ipHeader , UDPHeader udpheader ){
63+ /**
64+ * Handle unknown raw IP packet data
65+ *
66+ * @param stream ByteBuffer to be read
67+ */
68+ public void handlePacket (@ NonNull ByteBuffer stream ) throws PacketHeaderException , IOException {
69+ final byte [] rawPacket = new byte [stream .limit ()];
70+ stream .get (rawPacket , 0 , stream .limit ());
71+ stream .rewind ();
72+
73+ final IPv4Header ipHeader = IPPacketFactory .createIPv4Header (stream );
74+
75+ if (ipHeader .getProtocol () == 6 ) {
76+ handleTCPPacket (stream , ipHeader );
77+ } else if (ipHeader .getProtocol () == 17 ) {
78+ handleUDPPacket (stream , ipHeader );
79+ } else if (ipHeader .getProtocol () == 1 ) {
80+ handleICMPPacket (stream , ipHeader );
81+ } else {
82+ Log .w (TAG , "Unsupported IP protocol: " + ipHeader .getProtocol ());
83+ return ;
84+ }
85+ }
86+
87+ private void handleUDPPacket (ByteBuffer clientPacketData , IPv4Header ipHeader ) throws PacketHeaderException {
88+ UDPHeader udpheader = UDPPacketFactory .createUDPHeader (clientPacketData );
89+
5890 Session session = manager .getSession (
5991 ipHeader .getDestinationIP (), udpheader .getDestinationPort (),
6092 ipHeader .getSourceIP (), udpheader .getSourcePort ()
@@ -77,17 +109,18 @@ private void handleUDPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader, U
77109 int len = manager .addClientData (clientPacketData , session );
78110 session .setDataForSendingReady (true );
79111
112+ Log .i (TAG , "Subscribe UDP to OP_WRITE" );
80113 // Ping the NIO thread to write this, when the session is next writable
81114 session .subscribeKey (SelectionKey .OP_WRITE );
82115 nioService .refreshSelect (session );
83-
84- Log .d (TAG ,"Added " + len + " bytes of UDP data, ready to send" );
116+ Log .d (TAG ,"added UDP data for bg worker to send: " +len );
85117 }
86118
87119 manager .keepSessionAlive (session );
88120 }
89121
90- private void handleTCPPacket (ByteBuffer clientPacketData , IPv4Header ipHeader , TCPHeader tcpheader ){
122+ private void handleTCPPacket (ByteBuffer clientPacketData , IPv4Header ipHeader ) throws PacketHeaderException {
123+ TCPHeader tcpheader = TCPPacketFactory .createTCPHeader (clientPacketData );
91124 int dataLength = clientPacketData .limit () - clientPacketData .position ();
92125 int sourceIP = ipHeader .getSourceIP ();
93126 int destinationIP = ipHeader .getDestinationIP ();
@@ -176,34 +209,6 @@ private void handleTCPPacket(ByteBuffer clientPacketData, IPv4Header ipHeader, T
176209 }
177210 }
178211
179- /**
180- * handle each packet from each vpn client
181- * @param stream ByteBuffer to be read
182- */
183- public void handlePacket (@ NonNull ByteBuffer stream ) throws PacketHeaderException {
184- final byte [] rawPacket = new byte [stream .limit ()];
185- stream .get (rawPacket , 0 , stream .limit ());
186- stream .rewind ();
187-
188- final IPv4Header ipHeader = IPPacketFactory .createIPv4Header (stream );
189-
190- final ITransportHeader transportHeader ;
191- if (ipHeader .getProtocol () == 6 ) {
192- transportHeader = TCPPacketFactory .createTCPHeader (stream );
193- } else if (ipHeader .getProtocol () == 17 ) {
194- transportHeader = UDPPacketFactory .createUDPHeader (stream );
195- } else {
196- Log .e (TAG , "******===> Unsupported protocol: " + ipHeader .getProtocol ());
197- return ;
198- }
199-
200- if (transportHeader instanceof TCPHeader ) {
201- handleTCPPacket (stream , ipHeader , (TCPHeader ) transportHeader );
202- } else if (ipHeader .getProtocol () == 17 ){
203- handleUDPPacket (stream , ipHeader , (UDPHeader ) transportHeader );
204- }
205- }
206-
207212 private void sendRstPacket (IPv4Header ip , TCPHeader tcp , int dataLength ){
208213 byte [] data = TCPPacketFactory .createRstData (ip , tcp , dataLength );
209214
@@ -384,4 +389,48 @@ private void replySynAck(IPv4Header ip, TCPHeader tcp){
384389 Log .d (TAG ,"Send SYN-ACK to client" );
385390 }
386391 }
387- }//end class
392+
393+ private void handleICMPPacket (
394+ ByteBuffer clientPacketData ,
395+ final IPv4Header ipHeader
396+ ) throws PacketHeaderException {
397+ final ICMPPacket requestPacket = ICMPPacketFactory .parseICMPPacket (clientPacketData );
398+
399+ pingThreadpool .execute (new Runnable () {
400+ @ Override
401+ public void run () {
402+ try {
403+ if (!isReachable (PacketUtil .intToIPAddress (ipHeader .getDestinationIP ()))) {
404+ Log .d (TAG , "Failed ping, ignoring" );
405+ return ;
406+ }
407+
408+ ICMPPacket response = ICMPPacketFactory .buildSuccessPacket (requestPacket );
409+
410+ // Flip the address
411+ int destination = ipHeader .getDestinationIP ();
412+ int source = ipHeader .getSourceIP ();
413+ ipHeader .setSourceIP (destination );
414+ ipHeader .setDestinationIP (source );
415+
416+ byte [] responseData = ICMPPacketFactory .packetToBuffer (ipHeader , response );
417+
418+ Log .d (TAG , "Successful ping response" );
419+ writer .write (responseData );
420+ } catch (PacketHeaderException e ) {
421+ Log .w (TAG , "Handling ICMP failed with " + e .getMessage ());
422+ return ;
423+ }
424+ }
425+
426+ private boolean isReachable (String ipAddress ) {
427+ try {
428+ InetAddress destAddr = InetAddress .getByName (ipAddress );
429+ return destAddr .isReachable (10000 );
430+ } catch (IOException e ) {
431+ return false ;
432+ }
433+ }
434+ });
435+ }
436+ }
0 commit comments