Socket creation in VpnService freezes thread - java

I'm trying to implement no root firewall for android. I've gone through all related questions on SO, but haven't find an answer:
Here I am configuring my vpn.
private void configure() {
// If the old interface has exactly the same parameters, use it!
if (mInterface != null) {
Log.i(TAG, "Using the previous interface");
return;
}
// Configure a builder while parsing the parameters.
Builder builder = new Builder();
builder.setMtu(1500);
builder.addAddress("192.168.178.90", 24);
//builder.addAddress("10.0.2.0", 32);
//builder.addDnsServer("8.8.8.8");
builder.addRoute("0.0.0.0", 0); // to intercept packets
try {
mInterface.close();
} catch (Exception e) {
// ignore
}
mInterface = builder.establish();
}
And then I am trying to send data to destination address
byte[] data= new byte[pdata.data.capacity()];
pdata.data.get(data);
Socket s = new Socket(pdata.destAddr,pdata.destPort);
if(shouldBeBlocked(pdata.destAddr)) {
sendResult("blocked: "+ pdata.destAddr.toString()+":"+pdata.destPort);
} else {
sendResult(pdata.destAddr.toString()+":"+pdata.destPort);
if (protect(s)) {
...
But thread is freezing on constructor of Socket and then rasinig IOException like this:
java.net.ConnectException: failed to connect to /173.194.71.100 (port 443): connect failed: ETIMEDOUT (Connection timed out)

Maybe try builder.setMtu(1492); to be on a safer side. See http://en.wikipedia.org/wiki/Maximum_transmission_unit

Related

Java socket write error when client disconnects from server

I am working on a chat app in Java and so far everything works all right except that when a client disconnects and a message is send by other client this error pops out:
java.net.SocketException: Software caused connection abort: socket write error
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/java.io.DataOutputStream.write(DataOutputStream.java:107)
at java.base/java.io.DataOutputStream.writeUTF(DataOutputStream.java:401)
at java.base/java.io.DataOutputStream.writeUTF(DataOutputStream.java:323)
at com.terkea/com.terkea.system.server.ClientThread.run(ClientThread.java:65)
at java.base/java.lang.Thread.run(Thread.java:835)
This is my server Thread:
#Override
public void run() {
try {
DataInputStream in = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while (!socket.isClosed()) {
try {
String input = in.readUTF();
if (Message.fromJSON(input).getUserName().equals("REGISTER")) {
Message specialMessage = Message.fromJSON(input);
specialMessage.setUserName("SERVER");
Client test = Client.fromJSON(specialMessage.getMessage());
test.setIp(socket.getInetAddress().toString());
test.setListening_port(String.valueOf(socket.getPort()));
specialMessage.setMessage(Client.toJSON(test));
input = Message.toJSON(specialMessage);
}
for (ClientThread thatClient : server.getClients()) {
DataOutputStream outputParticularClient = new DataOutputStream(thatClient.getSocket().getOutputStream());
outputParticularClient.writeUTF(input);
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
and the Client:
public void createClient() {
try {
socket = new Socket(getHost(), portNumber);
DataInputStream in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
Message registerclient = new Message("REGISTER", Client.toJSON(getClient()));
out.writeUTF(Message.toJSON(registerclient));
new Thread(() -> {
while (!socket.isClosed()) {
try {
if (in.available() > 0) {
String input = in.readUTF();
Message inputMessage = Message.fromJSON(input);
if (inputMessage.getUserName().equals("SERVER")) {
System.err.println(Client.fromJSON(inputMessage.getMessage()));
allClientsConnected.add(Client.fromJSON(inputMessage.getMessage()));
} else {
chat.add(inputMessage);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
The error corresponds to outputParticularClient.writeUTF(input);
My goal is to get rid of this error and also if possible could anybody tell me a way to check when a client disconnects? I've found some similar questions over here and their solution was to check if (socket.getInputStream().read()!=-1)
but when I do that the whole program freezes and the GUI stops working.
You may want to look into expanding upon your special message functionality, and instead of using the username to pass "REGISTER" use something like messageType in order to do so. This way you can configure handlers based on type to do a number of things. For example things like:
MessageType { REGISTER, UNREGISTER, READ_RECEIPT, ... }
You can then have things like:
RegisterHandler {}
UnregisterHandler{}
and eventually expand them to have some features like facebook/whatsapp (/ICQ haha):
TypingHandler {} // Other user gets a message saying that I am typing to them
From here, you can implement the UNREGISTER to do what you want. Like the first comment says, you should catch the SocketException and manually unregister that client so it doesn't happen anymore. But you should also try to pre-emptively send an
{
messageType: UNREGISTER,
from: Client1
to: server|null,
data: {}
}
so that your server can remove it before the exception occurs. This would also let you handle Offline messages, if that's something you're interested in.

Moving from IO to NIO - Networking, IllegalBlockingModeException

I'm attempting to move my networking over from standard IO to NIO and following the few tutorials that there are to attempt to figure it out, while me, myself, and I decided it'd be a great idea to spend my first week rewriting the core for all of the applications logic handling, I never would have imagined that I wouldn't be able to set up the basic networking.
Currently the networking is at a very basic stage, everything is tossed inside of a while-loop and I can't honestly say I've put any attempt into it to make it look nice, considering I haven't a clue what I'm doing my goal was to figure out how to do it first, then go back and give it a makeover.
Here's the code I use to initialize my server:
// Initializes the TCP Server and all of its components.
private void initTcpServer(int port) {
try {
// Create a new selector
Selector socketSelector = SelectorProvider.provider()
.openSelector();
// Create a new non-blocking server socket channel;
this.serverSocketChannel = ServerSocketChannel.open();
this.serverSocketChannel.configureBlocking(false);
// Bind the server socket to the specified address and port
this.serverSocketChannel.socket().bind(
new InetSocketAddress("127.0.0.1", port));
// Register the server socket channel, indicating an interest in
// accepting new connections
this.serverSocketChannel.register(socketSelector,
SelectionKey.OP_ACCEPT);
// Set the selector for the server instance.
this.selector = socketSelector;
} catch (IOException e) {
e.printStackTrace();
}
}
Then this class implements the Runnable interface, and a new thread is started directly after this method completes, in this thread we contain the following code:
public void run() {
while (isRunning) {
try {
selector.selectNow();
} catch (IOException io) {
return;
}
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (!key.isValid()) {
it.remove();
continue;
}
try {
if (key.isAcceptable()) {
this.handleConnection(key);
} else if (key.isReadable()) {
Connection connection = (Connection) key.attachment();
if (connection != null) {
try {
connection.getMasterProtocol()
.decode(connection,
connection.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}
} finally {
it.remove();
}
}
}
}
This, from my understanding is what allows us to handle our connections and data based off of a SelectionKey.. and is what all of the NIO based networking runs from, you'll see that I'm calling two different methods to make this not so much of a mess, the first one is #handleConnection and the other one is a decode function.
The handle connection method creates a new instance of my Connection class and attatches it to the SelectionKey, like so:
public Connection(SelectionKey key) {
try {
// For an accept to be pending the channel must be a server socket channel.
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
// Accept the connection and make it non-blocking.
this.socketChannel = serverSocketChannel.accept();
this.socketChannel.configureBlocking(false);
// Set up other user data.
this.inputStream = new DataInputStream(socketChannel.socket().getInputStream());
this.masterProtocol = new MasterProtocol();
// Register the new SocketChannel with our Selector, indicating
// we'd like to be notified when there's data waiting to be read.
key = this.socketChannel.register(OGServer.getInstance().getSelector(), SelectionKey.OP_READ);
key.attach(this);
// Add the current <SelectorKey, Connection> to the current connections collection.
connections.put(key, this);
Log.debug(getClass(), "Connection constructed successfully.");
} catch(IOException e) {
e.printStackTrace();
}
}
The error is called when I attempt to call the MasterProtocol#decode method, which looks like this:
public Object decode(Connection connection, DataInputStream dataInputStream) throws IOException {
if(connection.getState() == ConnectionState.CONNECTED) {
byte[] bytes = ByteStreams.toByteArray(dataInputStream);
if(bytes.length < 4) {
System.out.println("Not enough bytes read.");
return null;
}
int bufferSize = dataInputStream.readInt();
System.out.println("Buffer Size: " + bufferSize);
while(bytes.length < bufferSize) {
return null;
}
int test = dataInputStream.readInt();
System.out.println("Test: " + test);
return null;
}
return null;
}
The error seems to be called when the DataInputStream tries to read from the network, more specifically on this line of code:
byte[] bytes = ByteStreams.toByteArray(dataInputStream);
The error:
Exception in thread "Thread-0" java.nio.channels.IllegalBlockingModeException
at sun.nio.ch.SocketAdaptor$SocketInputStream.read(SocketAdaptor.java:190)
at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:103)
at java.io.DataInputStream.read(DataInputStream.java:100)
at com.google.common.io.ByteStreams.copy(ByteStreams.java:70)
at com.google.common.io.ByteStreams.toByteArray(ByteStreams.java:115)
at net.ogserver.framework.net.protocol.MasterProtocol.decode(MasterProtocol.java:29)
at net.ogserver.framework.net.OGServer.run(OGServer.java:146)
at java.lang.Thread.run(Thread.java:745)
The 'IllegalBlockingModeException' exception is what's throwing me off, as all of the information I've found was for setting up a Non-blocking server, but the DataInputStream implementation was my own, so I must have done something wrong somewhere. NIO is a completely different world from IO, but learning is learning, eh?
EDIT: I guess it'd help to know how I'm sending the data from the client, it's just a very basic test application that does this:
socket = new Socket("127.0.0.1", 5055);
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeBoolean(false);
If you're moving to NIO in non-blocking you can't keep using streams. If you want to use streams there is no advantage to using NIO at all. I would just stop the migration project now.

Java Apache FTPClient API: Why isn't logout() in finally clause?

http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html
I noticed the example disconnects() in the finally clause, but doesn't do the same for logout()
FTPClient ftp = new FTPClient();
FTPClientConfig config = new FTPClientConfig();
config.setXXX(YYY); // change required options
ftp.configure(config );
boolean error = false;
try {
int reply;
ftp.connect("ftp.foobar.com");
System.out.println("Connected to " + server + ".");
System.out.print(ftp.getReplyString());
// After connection attempt, you should check the reply code to verify
// success.
reply = ftp.getReplyCode();
if(!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
System.err.println("FTP server refused connection.");
System.exit(1);
}
... // transfer files
ftp.logout();
} catch(IOException e) {
error = true;
e.printStackTrace();
} finally {
if(ftp.isConnected()) {
try {
ftp.disconnect();
} catch(IOException ioe) {
// do nothing
}
}
System.exit(error ? 1 : 0);
}
Anyone know why we don't need to logout() when we catch an exception?
Anyone know why we don't need to logout() when we catch an exception?
The inside code of ftp.logout() function is as follows:
public boolean logout() throws IOException
{
return FTPReply.isPositiveCompletion(quit());
}
The quit() function send a command using sendCommand(FTPCommand.QUIT) to the FTP Server. If a connection exception happens, we are likely not being able to connect with FTP Server. calling logout() will try to write to FTP server again and create resources with additional throwing exception. In addition, although disconnect() function will also throw an exception, it closes the input, output, socket and releases resources which logout() function doesn't: as it is evident from the following source code of disconnect() function:
public void disconnect() throws IOException
{
if (_socket_ != null) _socket_.close();
if (_input_ != null) _input_.close();
if (_output_ != null) _output_.close();
if (_socket_ != null) _socket_ = null;
_input_ = null;
_output_ = null;
_controlInput_ = null;
_controlOutput_ = null;
_newReplyString = false;
_replyString = null;
}
I dont know much about the FTPClient library but I believe it's safe to assume disconnecting from the server implies logging out as part of the process if applicable, considering the explanations given in the docs:
disconnect() : Closes the connection to the FTP server and restores connection parameters to the default values.
logout() : Logout of the FTP server by sending the QUIT command.
http://commons.apache.org/proper/commons-net/apidocs/org/apache/commons/net/ftp/FTPClient.html

Java Socket on Android, connection through 3G internet global ip

I have problems using Sockets on Android. I'm new to this topic so I try to achieve a simple chat, Android phone to another Android phone as a training.
I can use my code to create a ServerSocket and connect to it with another Socket from the same device ( An 'echo' example with only one client on one device works fine) I've done that also using two IP adresses on the same wifi (192.168...) but any tentative to connect a distant client using internet ip address using 3G connection fails with a
java.net.SocketException: recvfrom failed: ETIMEDOUT (Connection timed out)
What I do is, creating the ServerSocket (ServerConnect.java) :
private ServerSocket _mainServer = null;
private void initServer() {
try {
//port I use here is arbitrary: 8081
_mainServer = new ServerSocket(CONNECT_SOCKET_PORT);
} catch (IOException e) {
Log.w("ServerSocket", e.toString());
e.printStackTrace();
}
}
in the same class, in a separate thread I do this to wait for client connections :
while (running) {
GuestConnect ssc = new GuestConnect(_mainServer.accept());
ssc.setListener(this);
ssc.startConnection();
_clientSockets.add(ssc);
performGuestAdded("bra!");
}
it makes the server waiting for multiple client connection, so it can host more than two poeple in the 'chat room'.
The comunication server side is made from the local client callback and dispatch messages to all my guests (I'm still working on this part, its not really done yet but I don't think its relevant to my problem here) :
public void onMessageReceived(TBTGuestConnect sender, String message) {
for(TBTGuestConnect guestConnect : _clientSockets)
if(guestConnect != sender)
guestConnect.sendMessage(message);
}
Clients are store as 'GuestConnect' objects here is how I set them (GuestConnect.java):
public class GuestConnect {
private StringBuilder _currentMessage;
private BufferedReader _is;
private PrintStream _os;
private Socket _clientSocket = null;
private String _hostname;
private boolean _running = false;
public GuestConnect(String hostname) {
_hostname = hostname;
_currentMessage = new StringBuilder();
}
clientSocket initialisation (still GuestConnect.java):
private void initSocket() {
if(_clientSocket==null)
{
// Try to open a server socket on given port
// Here is the fail when I called it from another device
try {
_clientSocket = new Socket(_hostname, ServerConnect.CONNECT_SOCKET_PORT);
} catch (IOException e) {
Log.w("GuestSocket", e.toString());
}
}
// Create a socket object from the ServerSocket to listen and accept
// connections.
// Open input and output streams
try {
_is = new BufferedReader(new InputStreamReader(_clientSocket.getInputStream()));
_os = new PrintStream(_clientSocket.getOutputStream());
} catch (IOException e) {
Log.w("GuestSocket", e.toString());
}
}
again, in the same class there is the comunication part :
initSocket();
_running = true;
performConnectionStarted();
try {
while (_running) {
String received = _is.readLine();
_currentMessage.append(received);
if (received.contains(ServerConnect.CONNECT_SOCKET_MESSAGE_END)) {
String finalMsg = _currentMessage.toString().substring(0, _currentMessage.lastIndexOf(ServerConnect.CONNECT_SOCKET_MESSAGE_END));
performMessageReceived(finalMsg);
_currentMessage.setLength(0);
}
}
} catch (IOException e) {
Log.w("GuestSocket", e.toString());
performError(e);
} finally {
try {
_os.close();
_is.close();
_clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
performError(e);
}
_clientSocket = null;
}
I can then send a message from this method :
public void sendMessage(String toSend) {
if (_running)
_os.println(toSend+ServerConnect.CONNECT_SOCKET_MESSAGE_END);
else
throw new IllegalStateException("Send message : Connection isn't started !");
}
So the question is, what should I do to make this works using global internet ip addresses ?
And more generally, what it the technical difference between same wifi local ip address and internet ip adress ?
i'll start from the end because it's easier - local ip address is private, it's not valid (and not visible) out of your local network, someone out of your local network can't access it directly but you can access hosts with public ip addresses. because they are not universally visible private addresses are not unique, public ip addresses are unique and (normally) visible from any point on the internet. there's more on the subject http://en.wikipedia.org/wiki/Network_address_translation
when it comes to 3g networks take a look here Could I connect to my iphone using 3g ip? and here ServerSocket accept() not accepting connections on Android . so most probably it can't be done
Don't know if this would be of any importance to you but in our country most 3G accounts are blocked by the ISP from incoming connections. You have to apply to unblock the ports. Some ISP's won't unblock them and some will.
Found that out when I wanted to connect my DVR with a 3G modem.

Java Multicast Sending Data, Not Receiving

I am writing a class in Java which is used to greatly simplify the process of multicasting. However, I am having two big problems with it:
The class sends data (I can verify this with my net monitor, Wireshark) but the data is not received by any others in the same group.
On some machines, the sending packet TTL is exceeded in transit (again, according to Wireshark).
Could anyone please help me? I've been trying and searching for answers for hours, and it appears that my code follows all of the basic procedures for connecting to, joining, sending, and receiving data from a multicast host.
Here is a snippet of the relevant portions of the class:
Multicaster class:
public class Multicaster {
public int port = 5540;
protected String IPAddress;
private MulticastSocket msConn;
private InetAddress netAddr;
public Multicaster(String IPAddress) {
this.IPAddress = IPAddress;
}
public String recieveData() {
byte[] buf = new byte[1000];
DatagramPacket pack = new DatagramPacket(buf, buf.length);
try {
this.msConn.receive(pack);
new Message(pack);
String out = new String(pack.getData());
return out.substring(0, pack.getLength());
} catch (IOException e) {
return new String("");
}
}
public void joinGroup() {
try {
this.msConn.joinGroup(this.netAddr);
} catch (IOException e) {
//This error shouldn't occur since it would caught by the connect() method during initial connection to the host
}
}
public void connect() throws MulticasterInitException {
//Try to create a multicast connection on the given IP address and port
try {
try {
//Create a multicast connection on a given port, throws UnknownHostException
this.msConn = new MulticastSocket(this.port);
//If all goes well, then create a connection to a given IP address using the above port number, throws IOException and SecurityException
this.netAddr = InetAddress.getByName(this.IPAddress);
}
}
/**
* Here all of the possible exceptions that are thrown above
* are caught and handled here. This works just fine.
*/
}
public void sendData(String data) throws MulticasterSendException {
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), this.netAddr, this.port);
try {
this.msConn.send(packet);
} catch (IOException e) {
throw new MulticasterSendException("Java could not communicate with the server. Please check your network connections.", e);
}
}
}
Sample usage to send data:
Multicaster multicast = new Multicaster("239.0.0.0");
try {
multicast.connect();
} catch (MulticasterInitException e) {
//Handle exception...
}
multicast.joinGroup();
try {
multicast.sendData("Hi");
} catch (MulticasterSendException e) {
//Handle exception...
}
Sample usage to receive data:
Multicaster multicast = new Multicaster("239.0.0.0");
try {
multicast.connect();
} catch (MulticasterInitException e) {
//Handle exception...
}
multicast.joinGroup();
System.out.print(multicast.recieveData());
I've run into similar problems before and had to ensure that the NetworkInterface was specified on the receiving side.
SocketAddress socketAddress = new InetSocketAddress(groupIp, groupPort);
NetworkInterface networkInterface = NetworkInterface.getByName(interfaceName);
socket.joinGroup(socketAddress, networkInterface);
Where interfaceName is one of the interface names shown when running ifconfig on Linux or Mac.

Categories