I have an issue with my Node.JS application.
For some reason, dns.reverse
is only working with the first nameserver specified and ignoring the second one.
I have the following piece of code:
import Resolver from '~/classes/resolver';
Resolver.addNameServer('192.168.2.1', '192.168.1.254').boot();
Resolver.hostnameFromIP('192.168.1.30').then((hostname: string) => {
console.log(hostname);
}).catch((error) => {
console.log(error); // We are going to end up here as the 192.168.2.1 is the first and only nameserver which will be queried
});
And here is Resolver class:
import q, { Deferred } from 'q';
import dns from 'dns';
import { ResolverInterface } from '~/classes/resolver/interfaces';
/**
* Resolver class
*/
class Resolver implements ResolverInterface {
/**
* List of nameservers used for hostname resolution
* @type { string[] }
*/
public nameservers: string[] = [];
/**
* Add nameserver for resolver to use
* @param { string } nameserver
* @return { Resolver }
*/
public addNameServer(...nameserver: string[]) : Resolver {
this.nameservers.push(...nameserver);
return this;
}
/**
* Initialize resolver
* @return { void }
*/
public boot() : void {
if (this.nameservers.length === 0) {
this.initializeWithDefaultNameServers();
}
dns.setServers(this.nameservers);
}
/**
* Resolve hostname from the IP address
* @param { string } ip
* @return { q.Promise<string> }
*/
public hostnameFromIP(ip: string) : q.Promise<string> {
const deferred: Deferred<any> = q.defer();
dns.reverse(ip, (error: NodeJS.ErrnoException | null, hostnames: string[]) => {
const defaultErrorMessage: string = 'Unable to resolve hostname';
if (error) {
return deferred.reject(defaultErrorMessage);
}
let hostname: string = hostnames.length === 0 ? defaultErrorMessage : hostnames[0];
hostname = hostname.replace('.localdomain', '').trim();
deferred.resolve(hostname);
});
return deferred.promise as q.Promise<string>;
}
/**
* Initialize resolver with default nameservers
* @return { void }
* @private
*/
private initializeWithDefaultNameServers() : void {
const nameservers: string[] = [
'192.168.0.1',
'192.168.1.1',
'192.168.2.1',
];
nameservers.forEach((nameserver: string) : void => {
this.nameservers.push(nameserver);
});
}
}
export default new Resolver;
Expected behavior: Application should go through all specified nameservers to resolve the hostname for specified IP address
Actual behavior:
Depending on which nameserver is first, only that nameserver will be queried for the hostname.
If 192.168.2.1
is first, i can query data for 192.168.2.10
, but i cannot do that for 192.168.1.30
.
If 192.168.1.254
is first, i can query data for 192.168.1.30
, but i cannot do that for 192.168.2.10
.
Is there a way to use all specified nameservers while doing reverse hostname lookup using dns.reverse
in Node.JS?
Thanks for help to Jorge Fuentes González, here is the version i've ended up using, at least it works for 2 nameservers.
/**
* Resolve hostname from IP address
* @param { string } ip
* @return { Promise<string> }
*/
public async hostnameFromIP(ip: string) : Promise<string> {
return await this.resolveForHostnameFromIP(ip)
.then((hostname: string): string => hostname)
.catch(async (error: string): Promise<string> => {
const indexOf: number = this.nameservers.indexOf(this.currentNameServer);
const newNameserverIndex: number = indexOf + 1;
if (newNameserverIndex <= this.nameservers.length - 1) {
this.currentNameServer = this.nameservers[indexOf + 1];
this.setCurrentNameServerValue();
return await this.hostnameFromIP(ip).then((hostname: string): string => hostname);
}
return error;
});
}
/**
* Helper method to resolve hostname from the IP address
* @param { string } ip
* @return { q.Promise<string> }
*/
private resolveForHostnameFromIP(ip: string) : q.Promise<string> {
const deferred: Deferred<any> = q.defer();
this.resolver.reverse(ip, (error: NodeJS.ErrnoException | null, hostnames: string[]) => {
const defaultErrorMessage: string = 'Unable to resolve hostname';
if (error) {
return deferred.reject(defaultErrorMessage);
} else {
let hostname: string = hostnames.length === 0 ? defaultErrorMessage : hostnames[0];
hostname = hostname.replace('.localdomain', '').trim();
deferred.resolve(hostname);
}
});
return deferred.promise as q.Promise<string>;
}
/**
* Update resolver configuration with current name server
* @return { void }
* @private
*/
private setCurrentNameServerValue() : void {
this.resolver.setServers([this.currentNameServer]);
};