use of org.xbill.DNS.Message in project GNS by MobilityFirst.
the class NameResolution method forwardToDnsServer.
/**
* Sends the query to the DNS server.
*
* @param dnsServer
* @param query
* @return A message with either a good response or an error.
*/
public static Message forwardToDnsServer(SimpleResolver dnsServer, Message query) {
try {
Message dnsResponse = dnsServer.send(query);
NameResolution.getLogger().log(Level.FINE, "DNS response {0} with {1} answer, {2} authoritative and {3} additional records", new Object[] { Rcode.string(dnsResponse.getHeader().getRcode()), dnsResponse.getSectionArray(Section.ANSWER).length, dnsResponse.getSectionArray(Section.AUTHORITY).length, dnsResponse.getSectionArray(Section.ADDITIONAL).length });
if (isReasonableResponse(dnsResponse)) {
NameResolution.getLogger().log(Level.FINE, "Outgoing response from DNS: {0}", dnsResponse.toString());
return dnsResponse;
}
} catch (IOException e) {
NameResolution.getLogger().log(Level.WARNING, "DNS resolution failed for {0}: {1}", new Object[] { query, e });
}
return errorMessage(query, Rcode.NXDOMAIN);
}
use of org.xbill.DNS.Message in project GNS by MobilityFirst.
the class NameResolution method lookupGnsServer.
/**
* Lookup the query in the GNS server.
* @param addr
* @param query
* @param handler
* @return A message with either a good response or an error.
*/
public static Message lookupGnsServer(InetAddress addr, Message query, ClientRequestHandlerInterface handler) {
// check for queries we can't handle
int type = query.getQuestion().getType();
// Was the query legitimate or implemented?
if (!Type.isRR(type) && type != Type.ANY) {
return errorMessage(query, Rcode.NOTIMP);
}
// extract the domain (guid) and field from the query
final int fieldName = query.getQuestion().getType();
final Name requestedName = query.getQuestion().getName();
final byte[] rawName = requestedName.toWire();
final String domainName = querytoStringForGNS(rawName);
// The domain name must be an absolute name, i.e., ended with a dot
assert (domainName.endsWith(".")) : "The domain name " + domainName + "to resolve is not an absolute name!";
/**
* The query type or domain name can't be null, otherwise return an error message
*/
if (Type.string(fieldName) == null || domainName == null) {
return errorMessage(query, Rcode.NXDOMAIN);
}
NameResolution.getLogger().log(Level.FINE, "Trying GNS lookup for domain {0}, type {1}", new Object[] { domainName, Type.string(fieldName) });
/**
* Create a response message, build the header first.
* The response is constructed later after GNS query.
*/
Message response = new Message(query.getHeader().getID());
response.getHeader().setFlag(Flags.QR);
if (query.getHeader().getFlag(Flags.RD)) {
response.getHeader().setFlag(Flags.RA);
}
response.addRecord(query.getQuestion(), Section.QUESTION);
response.getHeader().setFlag(Flags.AA);
/**
* Request DNS fields of an alias and prepare a DNS response message
*/
ArrayList<String> fields = new ArrayList<>(Arrays.asList("A", "NS", "CNAME", "SOA", "PTR", "MX"));
/**
* <p>
* RFC 1034: the additional section "carries RRs(Resource Records) which may be helpful in
* using the RRs in the other section"
* RFC 2181: data you put in the additional section can never be promoted into real answers.
*
* <p>When a DNS client needs to look up a name used in a program, it queries DNS servers to resolve the name.
* Each query message the client sends contains three pieces of information, specifying a question for the server to answer:
* 1. A specified DNS domain name, stated as a fully qualified domain name (FQDN).
* 2. A specified query type, which can either specify a resource record (RR) by type or a specialized type of query operation.
* 3. A specified class for the DNS domain name. For DNS servers running the Windows operating system, this should always be specified as the Internet (IN) class.
*
* <p>The information is retrieved from GNS based on the queried domain.
* <p>The response is constructed based on the query type,
* 1. A: return A records in ANSWER section, NS records in AUTHORITY section, A records of name servers in ADDITIONAL section
* 2. NS: return NS records in ANSWER section, A records of name servers in ADDITIONAL section
* 3. MX: return MX records in ANSWER section, NS records in AUTHORITY section, A record of name servers in ADDITIONAL section
* 4. CNAME: return CNAME records in in ANSWER section, NS records in AUTHORITY section, A record of name servers in ADDITIONAL section
*
* Records in ADDITIONAL section is not required, we do a best-effort resolution for the names in ADDITIONAL section.
*/
long resolveStart = System.currentTimeMillis();
JSONObject fieldResponseJson = lookupGuidField(addr.getHostAddress().toString(), query.getHeader().getID(), domainName, null, fields, handler);
if (fieldResponseJson == null) {
NameResolution.getLogger().log(Level.FINE, "GNS lookup for domain {0} failed.", domainName);
return errorMessage(query, Rcode.NXDOMAIN);
}
NameResolution.getLogger().log(Level.FINE, "fieldResponse all fields (NS, MX, CNAME, A): {0}", fieldResponseJson.toString());
switch(fieldName) {
case Type.NS:
{
JSONObject obj = getNSRecordsFromNSField(fieldResponseJson, domainName);
if (obj != null) {
try {
JSONArray nsList = obj.getJSONArray("NS");
JSONArray aList = obj.getJSONArray("A");
for (int i = 0; i < nsList.length(); i++) {
response.addRecord((Record) nsList.get(i), Section.ANSWER);
}
for (int i = 0; i < aList.length(); i++) {
response.addRecord((Record) aList.get(i), Section.ADDITIONAL);
}
} catch (JSONException e) {
// do nothing, this happens only because some record is corrupted
}
} else {
// I don't have the requested A record, you must ask a wrong guy
return errorMessage(query, Rcode.NXDOMAIN);
}
}
break;
case Type.A:
{
// Get A records from retrieved GNS record
JSONArray aList = getARecordsFromAField(fieldResponseJson, domainName);
if (aList != null) {
for (int i = 0; i < aList.length(); i++) {
try {
response.addRecord((Record) aList.get(i), Section.ANSWER);
} catch (JSONException e) {
// trash the record
}
}
} else {
// I don't have the requested A record, you must ask a wrong guy
return errorMessage(query, Rcode.NXDOMAIN);
}
//Get NS record if we can
JSONObject obj = getNSRecordsFromNSField(fieldResponseJson, domainName);
if (obj != null) {
try {
JSONArray nsList = obj.getJSONArray("NS");
JSONArray aNSList = obj.getJSONArray("A");
for (int i = 0; i < nsList.length(); i++) {
response.addRecord((Record) nsList.get(i), Section.AUTHORITY);
}
for (int i = 0; i < aNSList.length(); i++) {
response.addRecord((Record) aNSList.get(i), Section.ADDITIONAL);
}
} catch (JSONException e) {
// do nothing, this happens only because some record is corrupted
}
}
}
break;
case Type.MX:
{
JSONObject obj = getMXRecordsFromMXField(fieldResponseJson, domainName);
NameResolution.getLogger().log(Level.FINE, "MX record for domain {0} is {1}", new Object[] { domainName, obj });
if (obj != null) {
try {
JSONArray mxList = obj.getJSONArray("MX");
JSONArray aList = obj.getJSONArray("A");
for (int i = 0; i < mxList.length(); i++) {
response.addRecord((Record) mxList.get(i), Section.ANSWER);
}
for (int i = 0; i < aList.length(); i++) {
response.addRecord((Record) aList.get(i), Section.ADDITIONAL);
}
} catch (JSONException e) {
// do nothing, this happens only because some record is corrupted
}
} else {
// I don't have the requested MX record, you must ask a wrong guy
return errorMessage(query, Rcode.NXDOMAIN);
}
//Get NS record if we can
obj = getNSRecordsFromNSField(fieldResponseJson, domainName);
if (obj != null) {
try {
JSONArray nsList = obj.getJSONArray("NS");
JSONArray aNSList = obj.getJSONArray("A");
for (int i = 0; i < nsList.length(); i++) {
response.addRecord((Record) nsList.get(i), Section.AUTHORITY);
}
for (int i = 0; i < aNSList.length(); i++) {
response.addRecord((Record) aNSList.get(i), Section.ADDITIONAL);
}
} catch (JSONException e) {
// do nothing, this happens only because some record is corrupted
}
}
}
break;
case Type.CNAME:
{
if (fieldResponseJson.has("CNAME")) {
// get CNAME alias, no need to resolve it to an IP address
try {
String cname = fieldResponseJson.getString("CNAME");
// The cname must be an absolute name, i.e., ended with a dot
if (!cname.endsWith(".")) {
cname = cname + ".";
}
CNAMERecord cnameRecord = new CNAMERecord(new Name(domainName), DClass.IN, 60, new Name(cname));
response.addRecord(cnameRecord, Section.ANSWER);
} catch (JSONException | TextParseException e) {
}
} else {
// I don't have the requested CNAME record, you must ask a wrong guy
return errorMessage(query, Rcode.NXDOMAIN);
}
}
break;
default:
// we haven't implemented yet
return errorMessage(query, Rcode.NOTIMPL);
}
DelayProfiler.updateDelay("ResolveName", resolveStart);
NameResolution.getLogger().log(Level.FINER, "Outgoing response from GNS: {0}", response.toString());
return response;
}
use of org.xbill.DNS.Message in project GNS by MobilityFirst.
the class NameResolution method buildErrorMessage.
/**
* Creates an error message from a header, response code and a record.
*
* @param header
* @param rcode
* @param question
* @return the error message
*/
private static Message buildErrorMessage(Header header, int rcode, Record question) {
Message response = new Message();
response.setHeader(header);
for (int i = 0; i < 4; i++) {
response.removeAllRecords(i);
}
if (question != null) {
response.addRecord(question, Section.QUESTION);
}
response.getHeader().setRcode(rcode);
return response;
}
use of org.xbill.DNS.Message in project opennms by OpenNMS.
the class DNSServer method doAXFR.
byte[] doAXFR(final Name name, final Message query, final TSIG tsig, TSIGRecord qtsig, final Socket s) {
final Zone zone = m_znames.get(name);
boolean first = true;
if (zone == null)
return errorMessage(query, Rcode.REFUSED);
@SuppressWarnings("unchecked") final Iterator<RRset> it = zone.AXFR();
try {
final DataOutputStream dataOut = new DataOutputStream(s.getOutputStream());
int id = query.getHeader().getID();
while (it.hasNext()) {
final RRset rrset = it.next();
final Message response = new Message(id);
final Header header = response.getHeader();
header.setFlag(Flags.QR);
header.setFlag(Flags.AA);
addRRset(rrset.getName(), response, rrset, Section.ANSWER, FLAG_DNSSECOK);
if (tsig != null) {
tsig.applyStream(response, qtsig, first);
qtsig = response.getTSIG();
}
first = false;
final byte[] out = response.toWire();
dataOut.writeShort(out.length);
dataOut.write(out);
}
} catch (final IOException ex) {
LOG.warn("AXFR failed", ex);
}
try {
s.close();
} catch (final IOException ex) {
LOG.warn("error closing socket", ex);
}
return null;
}
use of org.xbill.DNS.Message in project opennms by OpenNMS.
the class DNSServer method generateReply.
/*
* Note: a null return value means that the caller doesn't need to do
* anything. Currently this only happens if this is an AXFR request over
* TCP.
*/
byte[] generateReply(final Message query, final byte[] in, final int length, final Socket s) throws IOException {
final Header header = query.getHeader();
int maxLength;
int flags = 0;
if (header.getFlag(Flags.QR))
return null;
if (header.getRcode() != Rcode.NOERROR)
return errorMessage(query, Rcode.FORMERR);
if (header.getOpcode() != Opcode.QUERY)
return errorMessage(query, Rcode.NOTIMP);
final Record queryRecord = query.getQuestion();
final TSIGRecord queryTSIG = query.getTSIG();
TSIG tsig = null;
if (queryTSIG != null) {
tsig = m_TSIGs.get(queryTSIG.getName());
if (tsig == null || tsig.verify(query, in, length, null) != Rcode.NOERROR)
return formerrMessage(in);
}
final OPTRecord queryOPT = query.getOPT();
if (s != null)
maxLength = 65535;
else if (queryOPT != null)
maxLength = Math.max(queryOPT.getPayloadSize(), 512);
else
maxLength = 512;
if (queryOPT != null && (queryOPT.getFlags() & ExtendedFlags.DO) != 0)
flags = FLAG_DNSSECOK;
final Message response = new Message(query.getHeader().getID());
response.getHeader().setFlag(Flags.QR);
if (query.getHeader().getFlag(Flags.RD)) {
response.getHeader().setFlag(Flags.RD);
}
response.addRecord(queryRecord, Section.QUESTION);
final Name name = queryRecord.getName();
final int type = queryRecord.getType();
final int dclass = queryRecord.getDClass();
if ((type == Type.AXFR || type == Type.IXFR) && s != null)
return doAXFR(name, query, tsig, queryTSIG, s);
if (!Type.isRR(type) && type != Type.ANY)
return errorMessage(query, Rcode.NOTIMP);
final byte rcode = addAnswer(response, name, type, dclass, 0, flags);
if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN)
return errorMessage(query, rcode);
addAdditional(response, flags);
if (queryOPT != null) {
final int optflags = (flags == FLAG_DNSSECOK) ? ExtendedFlags.DO : 0;
final OPTRecord opt = new OPTRecord((short) 4096, rcode, (byte) 0, optflags);
response.addRecord(opt, Section.ADDITIONAL);
}
response.setTSIG(tsig, Rcode.NOERROR, queryTSIG);
return response.toWire(maxLength);
}
Aggregations