I am using JNDI in Java to perform DNS lookups in my application to resolve A records - running under Java 8 on Windows 7. However, I am having trouble resolving records unless I specify the complete host entry including domain name.
Java appears to be ignoring the DNS search list which is configured on the PC. I don't have a problem including the domain name, if that is what Java requires, but I can't find a public method to obtain the domains in the search list.
The following SSCCE uses the private method sun.net.dns.ResolverConfiguration to obtain the DNS search list, but I shouldn't use it as it is an internal proprietary API and may be removed in a future release.
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
public class SSCCE {
public static void main(String[] args) {
String[] hostsToLookup = new String[] { "testhost", "testhost.mydomain.com" };
try {
System.out.println("DNS Search List:");
for (Object o: sun.net.dns.ResolverConfiguration.open().searchlist()) {
System.out.println(" " + o);
}
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
InitialDirContext idc = new InitialDirContext(p);
for (String h : hostsToLookup) {
System.out.println("Host: " + h);
try {
Attributes attrs = idc.getAttributes(h, new String[] { "A" });
Attribute attr = attrs.get("A");
if (attr != null) {
for (int i = 0; i < attr.size(); i++) {
System.out.println(" " + attr.get(i));
}
}
}
catch (NameNotFoundException e) {
System.out.println(" undefined");
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
When I run this using just the host part it doesn't resolve, but when I manually add the domain from the search list then it does:
DNS Search List:
mydomain.com
Host: testhost
undefined
Host: testhost.mydomain.com
192.0.2.1
Is it possible to either make Java honour the DNS search list using JNDI or is there a public method to obtain the DNS search list?
Related
I have written an application that retrieves Active Directory groups and flattens them, i.e. includes recursively members of subgroup to the top parent group.
It works fine for small groups, but with larger groups I am facing a problem.
If number of members does not exceed 1500, they are listed in the member attribute. If there are more - then this attribute is empty and attribute with name member;range:0-1499 appears, containing first 1500 members.
My problem that I don't know how to get the rest of member set over 1500.
We have groups with 8-12 thousand members. Do I need to run another query?
On the Microsoft site I have seen C# code snippet on the similar matter, but couldn't make much sense of it, as they were showing how to specify a range, but not how to plug it into query. If someone knows how to do it in Java, I'd appreciate a tip.
This will obviously give you the next ones:
String[] returnedAtts = { "member;range=1500-2999" };
You need to fetch the users chunk by chunk (1500 chunks) Just make a counter and update you search and retrieve the next ones until you have all of them.
With your help I have a full working code
// Initialize
LdapContext ldapContext = null;
NamingEnumeration<SearchResult> results = null;
NamingEnumeration<?> members = null;
try {
// Initialize properties
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
properties.put(Context.PROVIDER_URL, "ldap://" + ldapUrl);
properties.put(Context.SECURITY_PRINCIPAL, adminLoginADOnPremise);
properties.put(Context.SECURITY_CREDENTIALS, adminPasswordADOnPremise);
// Initialize ldap context
ldapContext = new InitialLdapContext(properties, null);
int range = 0;
boolean finish = false;
while (finish != true) {
// Set search controls
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchCtls.setReturningAttributes(generateRangeArray(range));
// Get results
results = ldapContext.search(ldapBaseDn, String.format("(samAccountName=%s)", groupName), searchCtls);
if (results.hasMoreElements() == true) {
SearchResult result = results.next();
try {
members = result.getAttributes().get(generateRangeString(range)).getAll();
while (members.hasMore()) {
String distinguishedName = (String) members.next();
logger.debug(distinguishedName);
}
range++;
} catch (Exception e) {
// Fails means there is no more result
finish = true;
}
}
}
} catch (NamingException e) {
logger.error(e.getMessage());
throw new Exception(e.getMessage());
} finally {
if (ldapContext != null) {
ldapContext.close();
}
if (results != null) {
results.close();
}
}
Two functions missing from the working code example by #Nicolas, I guess they would be something like:
public static String[] generateRangeArray(int i) {
String range = "member;range=" + i * 1500 + "-" + ((i + 1) * 1500 - 1);
String[] returnedAtts = { range };
return returnedAtts;
}
public static String generateRangeString(int i) {
String range = "member;range=" + i * 1500 + "-" + ((i + 1) * 1500 - 1);
return range;
}
The code does not handle the case if the AD group is not so large that the member attribute actually needs to be "chunked", that is if the "member" attribute exists instead.
I am using an example of recently released Google's People API from here. I have extended a sample a bit to display additional information about the contact such as an email address and a phone number. The code that should do the job is presented below.
public class PeopleQuickstart {
...
public static void getPersonInfo(Person person){
// Get names
List<Name> names = person.getNames();
if(names != null && names.size() > 0) {
for(Name personName: names) {
System.out.println("Name: " + personName.getDisplayName());
}
}
// Get email addresses
List<EmailAddress> emails = person.getEmailAddresses();
if(emails != null && emails.size() > 0) {
for(EmailAddress personEmail: emails) {
System.out.println("Email: " + personEmail.getValue());
}
}
// Get phone numbers
List<PhoneNumber> phones = person.getPhoneNumbers();
if(phones != null && phones.size() > 0) {
for(PhoneNumber personPhone: phones){
System.out.println("Phone number: " + personPhone.getValue());
}
}
}
public static void main(String [] args) throws IOException {
People service = getPeopleService();
// Request 120 connections.
ListConnectionsResponse response = service.people().connections()
.list("people/me")
.setPageSize(120)
.execute();
// Display information about your connections.
List<Person> connections = response.getConnections();
if (connections != null && connections.size() > 0) {
for (Person person: connections){
getPersonInfo(person);
}
} else {
System.out.println("No connections found.");
}
}
}
I am testing this program with my contact list and I can successfully obtain a list of people along with the name fields. However, I cannot get values for email addresses and phone numbers (lists are always null), although I do have these values set in my contact list (verified through Gmail->Contacts). What am I missing?
Ok, problem solved. It looks like Google's documentation is a bit misleading (well, it has just been released;)). When I try to fetch my contacts using people.connections.list (see here) there are several query parameters that can be set. However, for the requestMask parameter it is stated that "Omitting this field will include all fields" which is not the case (at least did not work for me). Therefore, one has to explicitly specify which fields to be returned in the response. The modified code is given below. I wish Google people would clarify this point a bit.
public class PeopleQuickstart {
...
public static void main(String [] args) throws IOException {
People service = getPeopleService();
// Request 120 connections.
ListConnectionsResponse response = service.people().connections()
.list("people/me")
.setPageSize(120)
// specify fields to be returned
.setRequestMaskIncludeField("person.names,person.emailAddresses,person.phoneNumbers")
.execute();
// Display information about a person.
List<Person> connections = response.getConnections();
if (connections != null && connections.size() > 0) {
for (Person person: connections){
getPersonInfo(person);
}
} else {
System.out.println("No connections found.");
}
}
}
I'm rewriting my C# program to Java and became very curios about the fact that C# application can extract tens of thousands of users with this trick:
DirectorySearcher search = new DirectorySearcher(entry);
search.SizeLimit = 99000;
search.PageSize = 98000;
but my Java programs firmly say
LDAPSearchException(resultCode=4 (size limit exceeded), numEntries=1000, numReferences=0, errorMessage='size limit exceeded')
I tried both unboundid and standard libraries. Found million discussions about this problem and everywhere is said - the limitation is on server, you can do nothing.
But my C# application does work! How can this happen? Secret techniques from Microsoft, that cannot be repeated by other vendors?
Just in case, my code is:
SearchRequest searchRequest = new SearchRequest(path, SearchScope.SUB, filter, "SamAccountName");
searchRequest.setSizeLimit(99000);
searchRequest.setTimeLimitSeconds(999);
SearchResult result = connection.search(searchRequest);
for (SearchResultEntry sre : result.getSearchEntries()) {
System.out.println(count++ + ": " + sre.toString());
}
for unboundid
p.s. I do not want to use workaround with searching for a*, b*, c*
et c. Especially, considering that usernames might be not only in English.
Further reading showed, that unboundid does support paged mode, so problem is solved.
public static void main(String[] args) {
try {
int count = 0;
LDAPConnection connection = new LDAPConnection("hostname", 389, "user#domain", "password");
final String path = "OU=Users,DC=org,DC=com";
String[] attributes = {"SamAccountName","name"};
SearchRequest searchRequest = new SearchRequest(path, SearchScope.SUB, Filter.createEqualityFilter("objectClass", "person"), attributes);
ASN1OctetString resumeCookie = null;
while (true)
{
searchRequest.setControls(
new SimplePagedResultsControl(100, resumeCookie));
SearchResult searchResult = connection.search(searchRequest);
for (SearchResultEntry e : searchResult.getSearchEntries())
{
if (e.hasAttribute("SamAccountName"))
System.out.print(count++ + ": " + e.getAttributeValue("SamAccountName"));
if (e.hasAttribute("name"))
System.out.println("->" + e.getAttributeValue("name"));
}
LDAPTestUtils.assertHasControl(searchResult,
SimplePagedResultsControl.PAGED_RESULTS_OID);
SimplePagedResultsControl responseControl =
SimplePagedResultsControl.get(searchResult);
if (responseControl.moreResultsToReturn())
{
resumeCookie = responseControl.getCookie();
}
else
{
break;
}
}
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
I'm building a java application that gets the mac addresses of a user and compare it with the correspondent value in the database(security feature). but the problem happens on mac os when i discovered that the list of mac addresses has common values(ex: on my mac the list of mac addresses are: 001C42000009,001C42000008,E0F8474267B6(wifi),70CD60F1A5C1(ethernet))
Is there a way to know all these common values that will result when getting the Mac address on Mac os.
Thank you.
At http://standards.ieee.org/develop/regauth/oui/public.html you can lookup a vendor using first 3 bytes of the MAC address, 00-1C-42 points to "Parallels, Inc." (http://www.parallels.com). Are you using some of their virtualization software? Try what java.net.NetworkInterface.isVirtual() returns for this address, if that is not useful then some ugly filter may require (e.g. based on address pattern)
import java.net.NetworkInterface;
import java.util.Enumeration;
public class NetworkInterfaceTest {
public static void main(String args[]) {
try {
Enumeration<NetworkInterface> ie = NetworkInterface.getNetworkInterfaces();
while (ie.hasMoreElements()) {
NetworkInterface i = ie.nextElement();
System.out.println(i.getDisplayName() + " [" + i.getName() + "]: " + formatAddress(i.getHardwareAddress()) + "; isVirtual=" + i.isVirtual());
}
} catch (Exception e){
e.printStackTrace();
}
}
private static String formatAddress(byte[] address) {
if (address == null) {
return null;
}
StringBuilder ret = new StringBuilder(address.length * 2);
for (byte b : address) {
if (ret.length() > 0) {
ret.append('-');
}
String bs = Integer.toHexString(b & 0x000000FF).toUpperCase();
if (bs.length() < 2) {
ret.append('0');
}
ret.append(bs);
}
return ret.toString();
}
}
i believe something like this will do the work for you
try {
InetAddress []addresses = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName());
/*
* Get NetworkInterfaces for current host and read hardware addresses.
*/
for(int j=0; j< addresses.length; i++) {
System.out.format("%02X%s", mac[i], (i < addresses.length – 1) ? "-" : "");
}
System.out.println();
}
}
i need to extract the top domain of an url and i got his http://publicsuffix.org/index.html
and the java implementation is in http://guava-libraries.googlecode.com and i could not find
any example to extract domain name
say example..
example.google.com
returns google.com
and bing.bing.bing.com
returns bing.com
can any one tell me how can i implement using this library with an example....
It looks to me like InternetDomainName.topPrivateDomain() does exactly what you want. Guava maintains a list of public suffixes (based on Mozilla's list at publicsuffix.org) that it uses to determine what the public suffix part of the host is... the top private domain is the public suffix plus its first child.
Here's a quick example:
public class Test {
public static void main(String[] args) throws URISyntaxException {
ImmutableList<String> urls = ImmutableList.of(
"http://example.google.com", "http://google.com",
"http://bing.bing.bing.com", "http://www.amazon.co.jp/");
for (String url : urls) {
System.out.println(url + " -> " + getTopPrivateDomain(url));
}
}
private static String getTopPrivateDomain(String url) throws URISyntaxException {
String host = new URI(url).getHost();
InternetDomainName domainName = InternetDomainName.from(host);
return domainName.topPrivateDomain().name();
}
}
Running this code prints:
http://example.google.com -> google.com
http://google.com -> google.com
http://bing.bing.bing.com -> bing.com
http://www.amazon.co.jp/ -> amazon.co.jp
I recently implemented a Public Suffix List API:
PublicSuffixList suffixList = new PublicSuffixListFactory().build();
assertEquals(
"google.com", suffixList.getRegistrableDomain("example.google.com"));
assertEquals(
"bing.com", suffixList.getRegistrableDomain("bing.bing.bing.com"));
assertEquals(
"amazon.co.jp", suffixList.getRegistrableDomain("www.amazon.co.jp"));
EDIT: Sorry I've been a little too fast. I didn't think of co.jp. co.uk, and so on. You will need to get a list of possible TLDs from somewhere. You could also take a look at http://commons.apache.org/validator/ to validate a TLD.
I think something like this should work: But maybe there exists some Java-Standard Function.
String url = "http://www.foobar.com/someFolder/index.html";
if (url.contains("://")) {
url = url.split("://")[1];
}
if (url.contains("/")) {
url = url.split("/")[0];
}
// You need to get your TLDs from somewhere...
List<String> magicListofTLD = getTLDsFromSomewhere();
int positionOfTLD = -1;
String usedTLD = null;
for (String tld : magicListofTLD) {
positionOfTLD = url.indexOf(tld);
if (positionOfTLD > 0) {
usedTLD = tld;
break;
}
}
if (positionOfTLD > 0) {
url = url.substring(0, positionOfTLD);
} else {
return;
}
String[] strings = url.split("\\.");
String foo = strings[strings.length - 1] + "." + usedTLD;
System.out.println(foo);