Is there a way to listen to DNS changes using Java? I'm looking to update an external system with DNS changes as they happen.
Existing solution is to listen to bind.log for changes.
Monitoring a DNS server for changes over the internet requires that you are granted DNS zone transfer access (AXFR/IXFR). Once you have zone transfer access, you can pull a copy of the DNS data held by the server, but without such permission your options are rather limited. For instance, you can poll the server for RR changes for known names, but you can't possibly detect new names using just public DNS access.
Another alternative is to use a passive DNS data provider, but the coverage these provide may be limited and the service might be expensive. At least one of the major providers of passive DNS data works by tapping into DNS traffic, so they see new names and changes as they appear in DNS traffic.
This is similar to this question: Resolving ip-address of a hostname
To resolve a host in Java:
InetAddress address = InetAddress.getByName("www.example.com");
Now you could run this on a separate Thread and listen for the change:
public void launchThread()
{
Thread thread = new Thread(new Runnable()
{
InetAddress start = InetAddress.getByName("www.example.com");
while(start.equals(InetAddress.getByName("www.example.com")))
{
try
{
Thread.sleep(1000);
}catch(Exception e){e.printStackTrace();}
}
System.out.println("Domain resolution has changed.");
})
}
Related
Im using an MQTTSN Gateway Version 1.2 from;
https://github.com/simon622/mqtt-sn
I notice that a when a client connection goes to sleep and then subsequently wakes up, its IP address may well have changed on the network. When the next PINGREQ message is sent by a client, the gateway is unable to establish its identity from the network address and so the session simply times out.
Is there anyway of updating the network address in this scenario to tie it back to the original session without having the overhead of a new CONNECT?
I tried issuing a new PINGREQ, but the gateway was unable to link the new network address to an existing gateway session.
You're correct in stating that a client may well recycle their IP address during a network event (ie. a network stack power down, or roaming between cells on a cellular network). These events typically require a completely new CONNECT to be issued in order to re-authenticate with a gateway, since typically in SN UDP implementations, the network address is used as part of the identification mechanism. You can CONNECT(clean=false) to maintain session state.
Allowing a client to re-establish (or bind to an existing) session using a PINGREQ alone (with the presence of a clientId) would be very insecure and would present an easy attack vector for session hijacking.
Hope this helps clarify things.
I need to restrict specific user's roles to use the application while are accessing it from a specific place lets say "Users with role employee can only access the application from the office or its branches"
IP checking? it is changeable
How about if I follow private/public key? the disadvantage of that is that if I put the key in the cookie they can read it or may clear their cookies.
Based on mac address ? it is changeable
You can't trust IPs and MAC addresses are even more useless, your only friend here is cryptography. Assuming your user will authenticate using his credentials you need to somehow authenticate the machine as well. This is done by placing a different certificate on each machine and having the client prove his "identity" to the server by using his certificate.
If your client-server communication is based on SSL, you can require client authentication - have a look at Java HTTPS client certificate authentication, http://rap.ucar.edu/staff/paddy/cacerts/ or http://docs.oracle.com/cd/E11035_01/wls100/security/SSL_client.html.
If your communication is not based on SSL, or you wish to authenticate on the application level - you can still use the certificate. Load it from the truststore and prove your identity by proving you have access to the private key (usually the server sends a challenge, something random encrypted with the public key, you answer by decrypting it with the private key and sending it back. That way you prove you have the private key without having to show it).
If you do not wish to store certificates, you can just place a different encrypted file on each machine. The client will be able to decrypt it (using a hard coded key) and send something akin to a password to the server.
How do you protect these certificates? Read-only permissions for the users on the file...
Several notes -
You can't really EVER trust a client machine. A resourceful hostile user will break anything. The more resources you "enemies" have the more effort you need to put into your defence.
You didn't specify details regarding your environment. I'm sure there are system level solutions which I'm not aware of. For example - your server may connect to the Active Directory and monitor user logins on specific machines.
Sometimes the best solution may not come from the software level. For example, if your server uses a designated port for your communication. You could allow\block this traffic on your firewall\router\personal firewall - in places more adequate to resolve this issue than your server. If you have application control enforcement, you can allow the client itself to run on only specific machines.
You can also look for ways to create some unique PC fingerprint (motherboard Id, cpu id, SID in Active Directory, HDD id, MAC address...) - your server could then store a list of allowed fingerprints and your client will send the currently calculated fingerprint. This still comes back to - how well do you trust your clients?
Restricting by IP only works if people are coming from places that have static IPs. Anywhere like at home where you have dynamic it doesn't work.
If you can't use static and still want to restrict by IP you could use a service like http://dyn.com/dns/ to assign a FQDN to your IP. Then you could do lookup by FQDN to see if it returns an IP that matches the one in the request. This lookup could be cached so you're only doing every few hours. The tricky part to this is that each location would have to setup of a dynamic DNS client. Some routers now have this built in.
You can't get the MAC address through the HttpServlet class. And if could you'd get the MAC address from the device that is talking to your server which most likely would be something like a router, load balance, switch. MAC address aren't not route'able.
Re: Keys, you can use x509 certs - http://static.springsource.org/spring-security/site/docs/3.0.x/reference/x509.html
I just want to address this part of your Question:
I am trying to implement the IP method but it runs into following error.
java.lang.IncompatibleClassChangeError: com.project.Default and
com.project.Default$IpCheckService disagree on InnerClasses attribute
An IncompatibleClassChangeError means that there is a conflict between what the types were at compile time and what they are at runtime. In this case, it seems to be that you have (had) a nested IpCheckService class that has changed from static to non-static (or vice-versa!), and somehow you've managed to load an old version of one of the classes.
This is a build or deployment problem. If you can figure out what is going wrong here there is a good chance that your code will work. (At least, you won't get this exception any more.)
UPDATE:
The only way to restrict users in a certain place is the following:
You must define Fixed IP's in the Office!
Or, at least, a subnet mask for the office and it's branches.
In your application, check the subnet mask from the request and compare it to fixed preconfigured office subnet mask.
Thus, either you put those fixed IP's into webconf.xml or the subnet mask for the IPs;
In any event, the solution will always be connected to a network solution.
You could try something like this to check a fixed IP:
public class TestFilter implements Filter{
public void destroy() {}
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter) throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24");
try {
if(!matcher.matches(req.getHeader("X-Forwarded-For"))){
res.sendRedirect("AnotherPage.jsp");
}
} catch (UnsupportedOperationException e) {
//Handle IT
}
}
}
Also, you may need check the following, to catch every situation:
request.getHeader("Proxy-Client-IP");
request.getHeader("WL-Proxy-Client-IP");
request.getHeader("HTTP_CLIENT_IP");
request.getHeader("HTTP_X_FORWARDED_FOR");
request.getRemoteAddr();
I've found the following code from here, you can identify them based on their mac address.
This one is also helpful.
package com.mkyong;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
public class App{
public static void main(String[] args){
InetAddress ip;
try {
ip = InetAddress.getLocalHost();
System.out.println("Current IP address : " + ip.getHostAddress());
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
System.out.print("Current MAC address : ");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
System.out.println(sb.toString());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e){
e.printStackTrace();
}
}
}
To set up such kind of authorization rules, the first thing need to be defined is:
What is the definition of "office or its branches" in terms of computer identification?
Usually it is computers from some subnetwork as written in answers above - in that case the solution is obvious.
Second possibility - some guy came to the office with his personal computer or laptop or touchpad, etc. If this is allowed by security policy, the only entity we can authenticate is user. We might still want to differ access from the office (as physical location, e.g. building)/from home. In that case I'd recommend to look at one-time-password generation devices that should be available to user only in the office.
"Users with role employee can only access the application from the office or its branches"
Use a site-to site VPN. This effectively transforms the problem into an intranet login problem, which is trivial to solve.
By having a site-to-site VPN, you can be assured of the identity of the remote sites, since connection setup and authentication is generally performed by routers on site, the configuration of which users at the site do not need to know (or have on their computers, so cannot take away).
Once transformed into an intranet problem, just bind the application to an intranet address and secure it as you would any other intranet resource.
IPv6 usage is slowly starting nowadays, so I'm currently in the process of fixing and updating all applications to be prepared for IPv6.
One of the applications is the Java editor JOSM (http://josm.openstreetmap.de/). Java does not really use IPv6 in the default configuration even if the OS uses IPv6.
According to
http://docs.oracle.com/javase/1.5.0/docs/guide/net/ipv6_guide/#using
I set java.net.preferIPv6Addresses to true to let it use IPv6. Result have been user bug reports about broken internet connection.
It seems Java only switches to use IPv6 address instead of IPv4, but does nothing else. All C/C++ based software I maintain has been changed to check and try all available IP addresses, so broken IPv6 (or IPv4) addresses are skipped as long as one of the addresses works. For me it looks like Java only tries once, which does not work in real world.
Also usually the OS prefers IPv4 over IPv6, when IPv6 is tunneled. It seems like Java does ignore this settings as well.
So my question is: Are there any good ways to get a Java application to use IPV6 by default when available without breaking the application for IPv4 users.
User-bug reports: http://josm.openstreetmap.de/ticket/8562, http://josm.openstreetmap.de/ticket/8627.
It seems that topic is interesting for others as well, so I describe my current solution.
The software does an detection whether IPv6 works or not and remembers the state -> This is done by doing a TCP connect to a known IPv6 address (Ping of isReachable() is not reliable, see this bug report: https://josm.openstreetmap.de/ticket/11452).
Based on the remembered state the software starts with "java.net.preferIPv6Addresses" set to "true".
This means for a switch from IPv4 to a IPv6 network it will use IPv4 until next restart which is ok.
For a switch from an IPv6 enabled to an IPv4 only network it will not work at all which is solved by restarting the software.
In case of doubt we assume IPv6 does not work.
It is not possible to change "java.net.preferIPv6Addresses" after doing the detection, as that values seems to be read only before the first network connection. If there is a way to reset that state during runtime I'd like to know about it.
This solution seems to work, we have about 4% IPv6 connections in our logs ATM, but is is not really a satisfying solution.
/**
* Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation,
* disabling or enabling IPV6 may only be done with next start.
*/
private static void checkIPv6() {
if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) {
new Thread(new Runnable() { /* this may take some time (DNS, Connect) */
public void run() {
boolean hasv6 = false;
boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false);
try {
/* Use the check result from last run of the software, as after the test, value
changes have no effect anymore */
if (wasv6) {
Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
}
for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) {
if (a instanceof Inet6Address) {
if (a.isReachable(1000)) {
/* be sure it REALLY works */
Socket s = new Socket();
s.connect(new InetSocketAddress(a, 80), 1000);
s.close();
Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
if (!wasv6) {
Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart."));
} else {
Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4."));
}
hasv6 = true;
}
break; /* we're done */
}
}
} catch (IOException | SecurityException e) {
if (Main.isDebugEnabled()) {
Main.debug("Exception while checking IPv6 connectivity: "+e);
}
}
if (wasv6 && !hasv6) {
Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart."));
Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart!
new RestartAction().actionPerformed(null);
}
Main.pref.put("validated.ipv6", hasv6);
}
}, "IPv6-checker").start();
}
}
So you have two problems here:
Operating system vendors ship OSes with broken default IPv6 configurations, and/or users enable broken IPv6 configurations.
When it doesn't work, they mistakenly blame you.
There are two things you can do here:
Advise users on how to disable unnecessary and broken IPv6 transition mechanisms such as Teredo, ISATAP and 6to4. Instructions for these are widely available on the Internet.
It would also be nice if certain OS vendors would not enable this crap by default, but that's probably asking too much.
Implement Happy Eyeballs (RFC 6555) in your application. This is how modern web browsers solve this problem.
Happy Eyeballs specifies an algorithm whereby an application tries to connect via IPv6 and IPv4 at (almost) the same time, and if IPv6 isn't working within a short amount of time, to fall back to the IPv4 connection. The results of this trial are also cached for a few minutes.
Unfortunately I'm not familiar enough with Java to give you specific code to bypass all the interesting stuff Oracle is hiding from you by default, but it should be doable.
I have a Netty TCP server, and I want to reject/refuse incoming connection attempts selectively (based on their remote address). I guess I have to use ServerBootstrap.setParentHandler(ChannelHandler), but what do I do in the ChannelHandler? What event am I handling? How do I refuse the connection?
As Norman said, there is no way to refuse the connection, but you can close it immediately by adding a Netty's IpFilterHandler to server pipeline as the first handler. It will also stop propagating the upstream channel state events for filtered connection too.
#ChannelHandler.Sharable
public class MyFilterHandler extends IpFilteringHandlerImpl {
private final Set<InetSocketAddress> deniedRemoteAddress;
public MyFilterHandler(Set<InetSocketAddress> deniedRemoteAddress) {
this.deniedRemoteAddress = deniedRemoteAddress;
}
#Override
protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) throws Exception {
return !deniedRemoteAddress.contains(inetSocketAddress);
}
}
if you have list of patterns of IP address to block, you can use IpFilterRuleHandler,
//Example: allow only localhost:
new IPFilterRuleHandler().addAll(new IpFilterRuleList("+n:localhost, -n:*"))
If you have several network interfaces and you want to accept connections from one interface only you just need to set the local address in ServerBootstrap. This may be enough if your server is running in a machine that's connected to several networks and you want to serve only one of them. In this case any connection attempts from the other networks would be refused by the OS.
Once you have a connection in the application layer it's too late to refuse it. The best you can do is close it immediately.
This is enough if for example you want the server available only on localhost and invisible to the outside world: the loopback network 127.0.0.0/8 is served by a separate interface.
After having looked at org.jboss.netty.channel.socket.nio.NioServerSocketPipelineSink in the Netty sources, I am fairly certain that Netty accepts all incoming connections, and there is no way to refuse them (but, of course, they can be closed after being accepted).
If I make a connection using new Socket("unit.domain.com", 100) and the unit.domain.com DNS record has multiple IP addresses in the A record.. In the event of a failed connection, Does Java automatically connect to one of the other addresses in the list like the browser does? or does that have to be implemented manually?
No!
Creating a socket via new Socket(String, int) results in a resolving like that
addr = InetAddress.getByName(hostname);
which is a shortcut for
return InetAddress.getAllByName(host)[0];
The address resolution is performed in the Socket c-tor.
If you have to reconnect (failover) use the result returned by InetAddress.getAllByName(host), randomize (or use round-robin) and connect to the necessary addresses.
Edit: also if you are going to need to connect with some likely failure, you'd be better off using connect method of the Socket class with a timeout. Also make sure you close even failed sockets (and esp. channels) since they may leak a FD on *Nix.