How to write multithreaded algorithm to discovery available socket - java

For now I have sequential single threaded algorithm like this:
boolean conn = false;
for (InetSocketAddress addr : addrs) {
while (!conn) {
SocketChannel ch = SocketChannel.open();
ch.configureBlocking(true);
ch.socket().setTcpNoDelay(tcpNoDelay);
ch.socket().setKeepAlive(true);
try {
ch.socket().connect(addr, connTimeout);
conn = true;
....
}
catch (...) {
log("Not available: " + addr);
}
if (conn)
break;
}
}
But in some situations I could have quite a large list of addresses and available address could be in the end of this list - this will result into slow discovery of available address because I need to check all addresses one by one.
So my question - how I could implement algorithm that will try to connect to several addresses from different threads and will exit once first available address is found?
Also this algorithm should exit if no address available.
Any ideas how to code this?

Do all the connects in non-blocking mode, and then use a Selector on OP_CONNECT to find out when and if the connections succeed.

Related

SSH Server Identification never received - Handshake Deadlock [SSHJ]

We're having some trouble trying to implement a Pool of SftpConnections for our application.
We're currently using SSHJ (Schmizz) as the transport library, and facing an issue we simply cannot simulate in our development environment (but the error keeps showing randomly in production, sometimes after three days, sometimes after just 10 minutes).
The problem is, when trying to send a file via SFTP, the thread gets locked in the init method from schmizz' TransportImpl class:
#Override
public void init(String remoteHost, int remotePort, InputStream in, OutputStream out)
throws TransportException {
connInfo = new ConnInfo(remoteHost, remotePort, in, out);
try {
if (config.isWaitForServerIdentBeforeSendingClientIdent()) {
receiveServerIdent();
sendClientIdent();
} else {
sendClientIdent();
receiveServerIdent();
}
log.info("Server identity string: {}", serverID);
} catch (IOException e) {
throw new TransportException(e);
}
reader.start();
}
isWaitForServerIdentBeforeSendingClientIdent is FALSE for us, so first of all the client (we) send our identification, as appears in logs:
"Client identity String: blabla"
Then it's turn for the receiveServerIdent:
private void receiveServerIdent() throws IOException
{
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer();
while ((serverID = readIdentification(buf)).isEmpty()) {
int b = connInfo.in.read();
if (b == -1)
throw new TransportException("Server closed connection during identification exchange");
buf.putByte((byte) b);
}
}
The thread never gets the control back, as the server never replies with its identity. Seems like the code is stuck in this While loop. No timeouts, or SSH exceptions are thrown, my client just keeps waiting forever, and the thread gets deadlocked.
This is the readIdentification method's impl:
private String readIdentification(Buffer.PlainBuffer buffer)
throws IOException {
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
if (ident.isEmpty()) {
return ident;
}
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
"Server does not support SSHv2, identified as: " + ident);
return ident;
}
Seems like ConnectionInfo's inputstream never gets data to read, as if the server closed the connection (even if, as said earlier, no exception is thrown).
I've tried to simulate this error by saturating the negotiation, closing sockets while connecting, using conntrack to kill established connections while the handshake is being made, but with no luck at all, so any help would be HIGHLY appreciated.
: )
I bet following code creates a problem:
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
if (ident.isEmpty()) {
return ident;
}
If the IdentificationStringParser.parseIdentificationString() returns empty string, it will be returned to the caller method. The caller method will keep calling the while ((serverID = readIdentification(buf)).isEmpty()) since the string is always empty. The only way to break the loop would be if call to int b = connInfo.in.read(); returns -1... but if server keeps sending the data (or resending the data) this condition is never met.
If this is the case I would add some kind of artificial way to detect this like:
private String readIdentification(Buffer.PlainBuffer buffer, AtomicInteger numberOfAttempts)
throws IOException {
String ident = new IdentificationStringParser(buffer, loggerFactory).parseIdentificationString();
numberOfAttempts.incrementAndGet();
if (ident.isEmpty() && numberOfAttempts.intValue() < 1000) { // 1000
return ident;
} else if (numberOfAttempts.intValue() >= 1000) {
throw new TransportException("To many attempts to read the server ident").
}
if (!ident.startsWith("SSH-2.0-") && !ident.startsWith("SSH-1.99-"))
throw new TransportException(DisconnectReason.PROTOCOL_VERSION_NOT_SUPPORTED,
"Server does not support SSHv2, identified as: " + ident);
return ident;
}
This way you would at least confirm that this is the case and can dig further why .parseIdentificationString() returns empty string.
Faced a similar issue where we would see:
INFO [net.schmizz.sshj.transport.TransportImpl : pool-6-thread-2] - Client identity string: blablabla
INFO [net.schmizz.sshj.transport.TransportImpl : pool-6-thread-2] - Server identity string: blablabla
But on some occasions, there were no server response.
Our service would typically wake up and transfer several files simultaneously, one file per connection / thread.
The issue was in the sshd server config, we increased maxStartups from default value 10
(we noticed the problems started shortly after batch sizes increased to above 10)
Default in /etc/ssh/sshd_config:
MaxStartups 10:30:100
Changed to:
MaxStartups 30:30:100
MaxStartups
Specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the LoginGraceTime expires for a connection. The default is 10:30:100. Alternatively, random early drop can be enabled by specifying the three colon separated values start:rate:full (e.g. "10:30:60"). sshd will refuse connection attempts with a probability of rate/100 (30%) if there are currently start (10) unauthenticated connections. The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches full (60).
If you cannot control the server, you might have to find a way to limit your concurrent connection attempts in your client code instead.

Get the name and IP of devices on a Wifi network

I know this question has been asked here but it didn't get answered.
I'm writing a simple Java Swing application in which I want to show the name and IP address of each and every device that is connected to my wireless network.
I want to show this list in a JFrame. I searched a lot on the web but couldn't find a way to achieve this. Please help me out Java masters!
Thanks in advance!
I found this code after looking a little bit. It works, but it is slow, and probably not the best way to do it, but it works.
import java.io.IOException;
import java.net.InetAddress;
public class NetworkPing {
/**
* JavaProgrammingForums.com
*/
public static void main(String[] args) throws IOException {
InetAddress localhost = InetAddress.getLocalHost();
// this code assumes IPv4 is used
byte[] ip = localhost.getAddress();
for (int i = 1; i <= 254; i++)
{
ip[3] = (byte)i;
InetAddress address = InetAddress.getByAddress(ip);
if (address.isReachable(1000))
{
System.out.println(address + " machine is turned on and can be pinged");
}
else if (!address.getHostAddress().equals(address.getHostName()))
{
System.out.println(address + " machine is known in a DNS lookup");
}
else
{
System.out.println(address + " the host address and host name are equal, meaning the host name could not be resolved");
}
}
}
}
Couple things to note, address.getHostAddress() returns the 192.168.0.xxx
and address.getHostName() returns the name of the device like "Kevins-PC"
It's a pretty simple piece of code, but I'll walk through it real fast.
It starts off by getting your localhost IP address (which on a normal household network would be 192.168.0.xxx) and it stores that in a byte[] so it looks something like {192, 168, 0, xxx}.
Then it creates a for loop starting at 1 and going to 254 (because this code assumes a /24 subnet mask (255.255.255.0) but if its running a different subnet mask then it might not be 1-254).
Then in the for loop it sets the third index of the ip to i.
It then creates an InetAddress from that address.
Then it tries to reach it in 1000 milliseconds (1 second), and if it succeeds then it prints the address and says its reachable.
Else if the machine host address (the 192.168.0.xxx) does not equal the host name (like the name of your computer like Kevins-PC), then it says that the machine is known in a DNS lookup meaning it is found in a DNS lookup but it wasnt reachable (so its probably off or not connected, but it has been before), DNS is Domain Name Service. The DNS basically stores the information (your router probably does this).
Finally, else it just says it couldn't be resolved which means it wasnt reachable nor was it found looking in the DNS.
I found this code here and here
UPDATE
So if you run this and you just keep getting something like "192.168.0.5/192.168.0.5 the host address and host name are equal, meaning the host name could not be resolved"
That means that your router (your local DNS) just isn't storing the information OR those devices just choose not to submit their host name to the router, and that is why you will continually get that message. As far as I am aware, there isn't a way around this because those device names literally aren't stored
Try this :)
import java.io.IOException;
import java.net.*;
import java.util.Vector;
public class search {
public static void main(String args[]) throws UnknownHostException{
Vector<String> Available_Devices=new Vector<>();
String myip=InetAddress.getLocalHost().getHostAddress();
String mynetworkips=new String();
for(int i=myip.length();i>0;--i) {
if(myip.charAt(i-1)=='.'){ mynetworkips=myip.substring(0,i); break; }
}
System.out.println("My Device IP: " + myip+"\n");
System.out.println("Search log:");
for(int i=1;i<=254;++i){
try {
InetAddress addr=InetAddress.getByName(mynetworkips + new Integer(i).toString());
if (addr.isReachable(1000)){
System.out.println("Available: " + addr.getHostAddress());
Available_Devices.add(addr.getHostAddress());
}
else System.out.println("Not available: "+ addr.getHostAddress());
}catch (IOException ioex){}
}
System.out.println("\nAll Connected devices(" + Available_Devices.size() +"):");
for(int i=0;i<Available_Devices.size();++i) System.out.println(Available_Devices.get(i));
}
}

Java NIO - non-blocking connect

In a non-blocking connect on the client side, it might be the case that the server is not up and the connection cannot be established. I use selector to wait for OP_CONNECT to figure out if the connection can be established in the following way:
connection = SocketChannel.open();
connection.configureBlocking(false);
// Kick off connection establishment
connection.connect(hostAddress);
connection.register(selector, SelectionKey.OP_CONNECT);
this.selector.select(2000);
// Iterate over the set of keys for which events are available
Iterator<SelectionKey> selectedKeys = this.selector.selectedKeys().iterator();
if (!selectedKeys.hasNext()) {
throw new IllegalStateException("\"Could not connect to \" + hostAddress");
}
SelectionKey key = selectedKeys.next();
boolean valid = key.isValid();
if (!key.isConnectable()) {
throw new IllegalStateException("\"Could not connect to \" + hostAddress");
}
finishConnection(key);
However, even if I do not start the server, the key.isConnectable() returns true... I don;t understand why that is the case and how to make sure that I only call selector.select() again when I am connected

How to ping and keep statistics in Java

I have a task to make a simple console pinger in Java.
I tried the following code and I have 2 main issues.
First of all even if I am connected to the internet (I can ping from console any site), when I run the code returns false.
Second, is it possible to track the time of response of the ping?
Here is the code:
try {
InetAddress address = InetAddress.getByName(the_link);
System.out.println(the_link);
// Try to reach the specified address within the timeout
// periode. If during this periode the address cannot be
// reach then the method returns false.
boolean reachable = address.isReachable(5000);
System.out.println("Is host reachable? " + reachable);
} catch (Exception e) {
e.printStackTrace();
}
This is not a good one to use for most external ips.
Instead following can be used
boolean reachable = (java.lang.Runtime.getRuntime().exec("ping -c 1 www.google.lk").waitFor()==0);

Java regex for accepting a valid hostname, IPv4, or IPv6 address

Does anyone have a good (preferably tested) regex for accepting only a valid DNS hostname, IPv4, or IPv6 address?
I understand that you may be forced to use a regex. However, if possible it is better to avoid using regexes for this task and use a Java library class to do the validation instead.
If you want to do validation and DNS lookup together, then InetAddress.getByName(String) is a good choice. This will cope with DNS, IPv4 and IPv6 in one go, and it returns you a neatly wrapped InetAddress instance that contains both the DNS name (if provided) and the IPv4 or IPv6 address.
If you just want to do a syntactic validation, then Apache commons has a couple of classes that should do the job: DomainValidator and InetAddressValidator.
Guava has a new class HostSpecifier. It will even validate that the host name (if it is a host name) ends in a valid "public suffix" (e.g., ".com", ".co.uk", etc.), based on the latest mozilla public suffix list. That's something you would NOT want to attempt with a hand-crafted regex!
As others have said, doing this with a regex is quite a challenge and not advisable. But it is easy to do with the IPAddress Java library which can parse host names, IPv4 and IPv6 addresses, without triggering DNS lookup. Disclaimer: I am the project manager of that library.
Sample code:
check("1.2.3.4");
check("::1");
check("a.b.com");
static void check(String hostStr) {
HostName host = new HostName(hostStr);
try {
host.validate(); // triggers exception for invalid
if(host.isAddress()) {
IPAddress address = host.asAddress();
System.out.println(address.getIPVersion() + " address: " + address);
} else {
System.out.println("host name: " + host);
}
} catch(HostNameException e) {
System.out.println(e.getMessage());
}
}
Output:
IPv4 address: 1.2.3.4
IPv6 address: ::1
host name: a.b.com
Inspired by the code I found in this post, I created the following validator method that seems to suit simple validation needs quite nicely. By reading the JavaDoc of URI I removed some false positives such as "host:80" and "hostname/page", but I cannot guarantee there are some false positives left.
public static boolean isValidHostNameSyntax(String candidateHost) {
if (candidateHost.contains("/")) {
return false;
}
try {
// WORKAROUND: add any scheme and port to make the resulting URI valid
return new URI("my://userinfo#" + candidateHost + ":80").getHost() != null;
} catch (URISyntaxException e) {
return false;
}
}
You can also do this. Let's say:
public boolean isHostnameValid(String hostname) {
try {
InetAddress.getAllByName(hostname); // throws an error when the hostnme could not be found, if so, then return false
return true;
} catch(Exception exc) {
return false;
}
}

Categories