Android VpnService forward packets - java

I want to use android's VpnService to capture packets filter them based off IP address. I can get the packets from the "tun" interface just fine but after that i'm not sure how to forward them to their original destination. Based off of the comments from this answer it seems like i just need to:
Create a new socket to the destination IP address and port
Trim the IP and TCP header to send only the data
Re-attach the IP and TCP header when i get a response
Send the complete packet to the output stream
I have tried to send the data like this:
Socket socket = new Socket();
socket.bind(new InetSocketAddress(0));
if (protect(socket)){
Log.e(TAG, "Socket protected");
}else{
Log.e(TAG, "Socket NOT protected");
}
socket.connect(new InetSocketAddress(ipPacket.getDestinationIp(), ipPacket.getDstPort()));
Log.e(TAG, "Socket connected: " + socket.isConnected());
socket.getOutputStream().write(getTCPHeader(getIpHeader(packet)[1])[1].array());
The methods getTCPHeader(ByteArray packet) and getIpHeader(ByteArray packet) simply splits the packet into two ByteArray's as follows:
private ByteBuffer[] getIpHeader(ByteBuffer packet){
packet.position(0);
ByteBuffer ipHeader = ByteBuffer.allocate(20);
ByteBuffer data = ByteBuffer.allocate(packet.limit() - 20);
packet.get(ipHeader.array(), 0, 20);
packet.get(data.array(), 0, packet.limit() - 20);
return new ByteBuffer[]{ipHeader, data};
}
private ByteBuffer[] getTCPHeader(ByteBuffer packet){
packet.position(20);
ByteBuffer tcpHeader = ByteBuffer.allocate(20);
ByteBuffer data = ByteBuffer.allocate(packet.limit() - 20);
packet.get(tcpHeader.array(), 0, 20);
packet.get(data.array(), 0, packet.limit() - 40);
return new ByteBuffer[]{tcpHeader, data};
}
Now to get a response from the server, i am using the following code:
ByteBuffer responsePacket = ByteBuffer.allocate(65535);
InputStream socketInputStream = socket.getInputStream();
try{
int responseLength = socketInputStream.read(responsePacket.array());
if (responseLength > 20){
Log.e(TAG, "===Server Response===");
Log.e(TAG, "Length: " + responseLength);
ByteBuffer trimmedResponseData = ByteBuffer.allocate(responseLength);
System.arraycopy(responseData.array(), 0, trimmedResponseData.array(), 0, responseLength);
String resp = "";
for (int i = 0; i < responseLength; i++){
resp += String.valueOf(responseData.get(i) + " ");
}
Log.e(TAG, "Response data: " + resp);
ByteBuffer finalPacket = ByteBuffer.allocate(40 + responseLength);
ByteBuffer swappedIpHeader = swapSrcDstAddress(getIpHeader(packet)[0]);
ByteBuffer swappedTcpHeader = swapTCPSrcDst(getTCPHeader(getIpHeader(packet)[1])[0]);
finalPacket.put(swappedIpHeader.array());
finalPacket.put(swappedTcpHeader.array());
finalPacket.put(serverResponseData.array());
Packet finPack = debugPacket(finalPacket);
Log.e("VPN", "Final packet --> Packet size: " + finPack.getTotalLength() + " from " + finPack.getSourceIp() + " src port: " + finPack.getSrcPort() + " going to " + finPack.getDestinationIp() + " dst port: " + finPack.getDstPort());
out.write(finalPacket.array());
}
}catch (Exception e){
//Log.e(TAG, "EXCEPTION: " + e);
e.printStackTrace();
}
This code seems to work either EXTREMELY slowly, or not at all. Sometimes if i go to www.google.com it will load slowly but most of the time it doesn't. Also some times i am getting the following error on the line int responseLength = socketInputStream.read(serverResponse.array());
java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
What is causing this error, and how can i properly forward these packets to the appropriate destination? Any help is greatly appreciated!

What is causing this error?
recvfrom failed exception means that Server has closed the client socket but client was still reading input data (in your case serverResponse.array(). For details, see this.
how can i properly forward these packets to the appropriate
destination?
There is a sample-code from google-sources here that forwards available packets. Please go through the code and the relevant comments. According to google-sources:
This application consists of an Android client and a sample
implementation of a server. It performs IP over UDP and is capable of
doing seamless handover between different networks as long as it
receives the same VPN parameters.It shows how to build a VPN client
using the VpnService class introduced in API level 14.
The sample code of the server-side implementation is Linux-specific
and is available in the server directory. To run the server or port
it to another platform, please see comments in the code for the
details.
one more helpful app link here

Related

Java UDP packet loss. 50%

I am making an online java game. Right now I am using UDP packets. I have about a 50% packet drop rate right now at updating 60 15 times a second. This both happen when it's being hosted on my local machine and hosted on my server. I have tried sending fewer packets and sending them slower neither have helped. Thanks in advance.
Sending packets:
while(true) {
try {
socket = new DatagramSocket();
if(id >= 0) {
data = (PacketID.POS.toString() + tempobj.x + "/" + tempobj.y + "/" + tempobj.getHelth() + "/" + id + "/").getBytes();
}else {
data = (PacketID.POS.toString() + tempobj.x + "/" + tempobj.y + "/" + tempobj.getHelth() + "/" + tempobj.isSheild + "/" + tempobj.name + "/" + tempobj.size + "/" + (int)(tempobj.getVelx()) + "/" + (int)(tempobj.getVely())).getBytes();
}
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
if(tempobj.x != lx || tempobj.y != ly || tempobj.helth != lh) {
packetssent++;
System.out.println(packetssent);
lx = tempobj.x;
ly = tempobj.y;
lh = tempobj.helth;
socket.send(packet);
}
Thread.sleep(66);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
receiving packets:
byte[] arr = new byte[40];
DatagramPacket data = new DatagramPacket(arr, arr.length);
while(true) {
socket.receive(data);
String s = new String(data.getData(),0, data.getLength());
if(s.substring(0, 3).equalsIgnoreCase(PacketID.POS.name())) {
String[] split = s.substring(3).split("/");
System.out.println(reciver);
for(int i = 0; i < players.size(); i++) {
if((players.get(i).id - 1) == Integer.parseInt(split[3].trim())) {
players.get(i).x = Integer.parseInt(split[0].trim());
players.get(i).y = Integer.parseInt(split[1].trim());
players.get(i).helth = Integer.parseInt(split[2].trim());
}
}
}
s = null;
}
There is probably nothing you can do about UDP packet loss that you haven't already tried. Except ... maybe ... try and get a better end-to-end network connection.
Packet loss typically happens as a result of network congestion on the most congested links. Unless you own and manage those links, there is nothing you can do about it.
Maybe you should be using TCP instead. Or changing your application so that it can cope with 50% packet loss ...
There is one problem I noticed with your code. Your sending code is creating a brand new DatagramSocket to send each UDP message, and then not closing the socket. That's inefficient, and could lead to resource leak problems. (It shouldn't cause packet loss though.)
If this is a linux host you may try & check if you're not exceeding any buffers. I've been bitten by similar problem in the past. Check the output of netstat -s and checkout the udp section for any drops.

Performing a UPNP scan is not returning the Philips Hue Bridge

I am trying to implement my own UpNP scan, it is mostly working, and to prove that it's not me I have a windows program that allows you to send packets and see what response comes back.
I am sending a packet to 239.255.255.250 on port 1900 and I am sending the following data:
M-SEARCH * HTTP/1.1
Host: 239.255.255.250:1900
Man: "ssdp:discover"
MX: 10
ST: ssdp:all
Just for further info, in my Java code (Android) I have the following but I get the same response as the packet tester application:
try
{
byte[] sendData = new byte[1024];
//byte[] receiveData = new byte[1024];
byte[] receiveData;
String mSearch = "M-SEARCH * HTTP/1.1\r\nHost: 239.255.255.250:1900\r\nMan: \"ssdp:discover\"\r\nMX: 10\r\nST: ssdp:all\r\n\r\n";
sendData = mSearch.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.250"), 1900);
DatagramSocket clientSocket = new DatagramSocket();
clientSocket.send(sendPacket);
while (keepGoing)
{
receiveData = new byte[1024];
receivePacket = new DatagramPacket(receiveData, receiveData.length);
clientSocket.receive(receivePacket);
String response = new String(receivePacket.getData());
if (response == null || response.length() == 0)
{
keepGoing = false;
}
else
{
iupnpScan.updateText(response);
}
}
iupnpScan.complete(true);
return true;
}
catch (UnknownHostException ex)
{
Log.e("MainActivity", "Unknown Host Exception: " + ex.toString());
}
catch (SocketException ex)
{
Log.e("MainActivity", "Socket Exception: " + ex.toString());
}
catch (IOException ex)
{
Log.e("MainActivity", "IO Exception: " + ex.toString());
}
iupnpScan.complete(false);
return false;
I am getting some devices come back, such as my smart TV, router and NAS but the Philips Hue bridge is never returned in the reply.
Does the Philips Hue Bridge implement UpNP differently? All I can see is what response they send back now anything about what is needed to find it.
Although Philips site notes it supports UPnP, I do not know if it is true or not.
I would try scanning the entire network and testing IP by IP. Yes, I know, this is not which the standard says, but reality is insane sometimes.
This discovery is already implemented out there this way.
I programmed a network search in the past (looking for a Raspberry PI) and the best method I can use was matching MAC addresses with my known address start. Luckily, Philips publish their MAC addresses range.
I was also struggling with this behavior. After some trial and error, I realized that the Hue Bridge does not seem to understand the " around the ssdp:discover value. These quotation marks are also not present in the IETF draft: https://datatracker.ietf.org/doc/html/draft-cai-ssdp-v1-03
Following request was successful for me:
M-SEARCH * HTTP/1.1
ST: ssdp:all
MX: 3
MAN: ssdp:discover
HOST: 239.255.255.250:1900
This is the response I got:
HTTP/1.1 200 OK
HOST: 239.255.255.250:1900
EXT:CACHE-CONTROL: max-age=100
LOCATION: http://192.168.xxx.xxx:80/description.xml
SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.16.0
hue-bridgeid: 001788FFFE29D301
ST: urn:schemas-upnp-org:device:basic:1
USN: uuid:2f402f80-da50-11e1-9b23-00178829d301

Socket communicating between java and c# applications

I have two applications, one written in Java and the other in C#. I am trying to send a string from the Java app to C# app.
My java code for sending the string is as follows:
String response;
try {
DataOutputStream outToServer =
new DataOutputStream(outGoingSocket.getOutputStream());
BufferedReader inFromServer =
new BufferedReader(new InputStreamReader(outGoingSocket.getInputStream()));
outToServer.writeBytes(message + '\n');
outToServer.flush();
response = inFromServer.readLine();
System.out.println("Received: " + response);
} catch (Exception ex) {
System.err.println("Exception in incoming socket: " + ex.getMessage());
}
My C# code for receiving the data is as follows:
Byte[] bytes = new Byte[1000];
String data = null;
try {
Console.Write("Waiting for a connection... ");
TcpClient client = incomingSocket.AcceptTcpClient();
Console.WriteLine("Connected!");
data = null;
NetworkStream stream = client.GetStream();
int i;
while (true) {
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0) {
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received:", data);
processReceivedMessage(data);
ackSocket(stream, "OK");
}
}
} catch (Exception ex) {
Console.WriteLine("Exception: ", ex);
}
I have a problem with receiving the data in the C# application. When I send the string "Data" in the Java app, and try to print the data received by the C# application using Console.WriteLine("Received: {0}", data), the output is:
Received: D
Received: ata
If I use Console.WriteLine("Received: ", data), the output is:
Received:
Received:
I want my C# application to receive the full string that is sent by the Java application. I tried to increase the buffer byte array size to 1000 but it doesn't help. I don't have experience using sockets, can someone show me what I did wrong?
So, as you can see, the receiving end picks up a response in chunks that might be considerably smaller than the total message.
You shouldn't be seeking to change this behaviour... it's a fact of network programming. It's your job to glue them back together again.
"I want my c# application receive the full string"
So, how is your receiving app meant to know that it has received the full string? Did you send a length field to indicate how much data is coming? Perhaps you expect \n to indicate the end of message? A zero byte?
If your terminator is indeed a newline, you might want to consider passing your NetworkStream to a StreamReader and calling ReadLine on it. Now, the StreamReader will keep reading from the stream until it hits a newline, then hand you the line.

PingIDs (Data) is different on different sides

I am working on a server software for Minecraft: Pocket Edition. Right now I am working on the MOTD for the server, and it works Just fine. When sending a response to a ping packet (0x01) with a 0x1c. It shows up in the world list with the name just fine. But, for some reason, If I send the same data from another program, the Ping ID and ServerID will show different in the consoles. Why is this?
Ping response code:
public PingResponse(DatagramPacket Packet, long ServerID) throws IOException {
// Data from Ping
ByteBuffer ReceivedPing = ByteBuffer.wrap(Packet.getData());
// Set variables
this.ServerID = ServerID;
this.ServerName = ServerPropertiesHandler.getMOTD();
this.PingID = ReceivedPing.getLong();
// Server Name
String Identifier = "MCCPP;MINECON;" + ServerPropertiesHandler.getMOTD();
ByteBuffer PingResponseBuffer = ByteBuffer.allocate(35 + (short) Identifier.length());
// Put Packet ID
PingResponseBuffer.put(PacketIDList.ID_UNCONNECTED_PING_OPEN_CONNECTIONS);
// Ping ID
PingResponseBuffer.putLong(this.PingID);
System.out.println("Ping ID: " + this.PingID);
// Server ID
PingResponseBuffer.putLong(this.ServerID);
System.out.println("Server ID: " + this.ServerID);
// Sugar Spice and everything nice
PingResponseBuffer.put(PacketIDList.MAGIC);
// Server Name
PingResponseBuffer.putShort((short) Identifier.length());
PingResponseBuffer.put(Identifier.getBytes());
// Send
PacketHandler.Socket.send(new DatagramPacket(PingResponseBuffer.array(), PingResponseBuffer.array().length), Packet.getAddress(), Packet.getPort());
}
Client Example:
public static void main(String[] args) {
try {
// SEND
final long PacketID = new Random().nextLong();
DatagramSocket ClientSocket = new DatagramSocket();
ByteBuffer PingBuffer = ByteBuffer.allocate(25);
PingBuffer.put(PacketIDList.ID_CONNECTED_PING_OPEN_CONNECTIONS);
PingBuffer.putLong(PacketID);
PingBuffer.put(PacketIDList.MAGIC);
ClientSocket.send(new DatagramPacket(PingBuffer.array(), PingBuffer.array().length, InetAddress.getByName("localhost"), 19132));
// RECEIVE
byte[] buffer = new byte[1535];
DatagramPacket PongPacket = new DatagramPacket(buffer, buffer.length);
ClientSocket.receive(PongPacket);
byte[] PongPacketData = PongPacket.getData();
ByteBuffer PongBuffer = ByteBuffer.wrap(PongPacketData);
if(PongPacketData[0] == (byte) 0x1c) {
System.out.println("PingID From Server: " + PongBuffer.getLong());
System.out.println("ServerID From Server: " + PongBuffer.getLong());
System.out.println("MAGIC From Server: " + PongBuffer.get());
System.out.println("MOTD From Server: " + PongBuffer.get());
}
else {
System.out.println("UNKNOWN PACKET");
}
ClientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
3 months and no reply. pingID and serverID are identifiers of time used on respective systems to determine the time since start. These provide a double functionality. Firstly, they allow a server to determine any latency in the communications. Secondly, they help provide a packet order to commands. Rebooting a server would generate a new serverID starting from 0, while rejoining a server would provide a new pingID starting at 0.
I would be quite interested to see the progress you've made, as I've begun working on my own.

How can I get the IP address from a multicast socket that is listening to broadcasts?

My server creates a multicast socket which listens for UDP packets. It is receiving packets sent to the broadcast address of the network but I can't get the ip address of the sender:
multisocket.getInetAddress().getHostAddress();
returns
"::"
(I guess its because of null getInetAddress).
How can I get the IP address of the sender?
TIPS: I guess it has to do with the socket not being bound and basically the whole broadcasting because those packets arent sent exclusively to me but to the whole network, but shouldnt they hold the IP address of the sender? Enlighten me please.
Here is the code:
public void run() {
try {
Thread.sleep(5000);
Log.i("SERVERUDP", "Connecting...");
MulticastSocket multisocket = new MulticastSocket(SERVERPORT);
multisocket.setBroadcast(true);
Log.i("SERVERUDP","Server's IP is: " + multisocket.getLocalAddress().getHostAddress());
getLocalIpAddress();
while(true){
byte[] b = new byte[65535];
ByteArrayInputStream b_in = new ByteArrayInputStream(b);
DatagramPacket dgram = new DatagramPacket(b, b.length);
multisocket.receive(dgram); // blocks
ObjectInputStream o_in = new ObjectInputStream(b_in);
Object o = o_in.readObject();
dgram.setLength(b.length);
b_in.reset();
if(o.getClass().getSimpleName().equalsIgnoreCase("Request")){
Request request = (Request)o;
String inetaddress = multisocket.getInetAddress().getHostAddress();
Log.i("SERVERUDP-if", "Sending request to IP: " + inetaddress);
new Thread(new ClientTCP(inetaddress, createRequestFromBroadcast(request))).start();
}else if(o.getClass().getSimpleName().equalsIgnoreCase("String")){
Log.e("SERVERUDP-elseif-string", "WTF received a string: " + (String)o);
}else{
Log.e("SERVERUDP-else", "Unrecognized object of type: " + o.getClass().getSimpleName());
}
o_in.close();
//iteration done only once for testing!
break;
}
multisocket.close();
} catch (Exception e) {
Log.e("SERVERUDP", "Error", e);
}
}
Each packet that you receive might have a different source address. So I'm not sure why you're trying to look at multisocket to figure out the source address.
I have to admit I haven't tried this, but does dgram.getSocketAddress () give you what you want after the multisocket.receive call returns?

Categories