/*
** This file is part of Filius, a network construction and simulation software.
**
** Originally created at the University of Siegen, Institute "Didactics of
** Informatics and E-Learning" by a students' project group:
** members (2006-2007):
** André Asschoff, Johannes Bade, Carsten Dittich, Thomas Gerding,
** Nadja Haßler, Ernst Johannes Klebert, Michell Weyer
** supervisors:
** Stefan Freischlad (maintainer until 2009), Peer Stechert
** Project is maintained since 2010 by Christian Eibl <filius@c.fameibl.de>
** and Stefan Freischlad
** Filius is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) version 3.
**
** Filius is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied
** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
** PURPOSE. See the GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Filius. If not, see <http://www.gnu.org/licenses/>.
*/
package filius.software.dns;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.StringTokenizer;
import filius.Main;
/**
* Das Format einer DNS-Nachricht wird in RFC 1035 beschrieben. <br />
* The header contains the following fields: <br />
* <ol>
* <li> ID </li>
* <li> QR </li>
* <li> OPCODE </li>
* <li> AA </li>
* <li> TC </li>
* <li> RD </li>
* <li> RA </li>
* <li> Z </li>
* <li> RCODE </li>
* <li> QDCOUNT </li>
* <li> ANCOUNT </li>
* <li> NSCOUNT </li>
* <li> ARCOUNT </li>
* </ol>
*
* <b> Resource record format </b> <br />
* The answer, authority, and additional sections all share the same format
*/
public class DNSNachricht {
/**
* Konstanten fuer das Bit im Kopfteil (Header), das indiziert, ob es sich
* um eine Anfrage (QUERY) oder eine Antwort (RESPONSE) handelt.
*/
public static final int QUERY = 0, RESPONSE = 1;
/**
* Konstanten fuer den Response-Code: The values have the following
* interpretation:
* <ul>
* <li> 0 - No error condition </li>
* <li> 1 - Format error - The name server was unable to interpret the
* query. </li>
* <li> 2 - Server failure - The name server was unable to process this
* query due to a problem with the name server. </li>
* <li> 3 - Name Error - Meaningful only for responses from an authoritative
* name server, this code signifies that the domain name referenced in the
* query does not exist. </li>
* <li> 4 - Not Implemented - The name server does not support the requested
* kind of query. </li>
* <li> 5 - Refused - The name server refuses to perform the specified
* operation for policy reasons. For example, a name server may not wish to
* provide the information to the particular requester, or a name server may
* not wish to perform a particular operation (e.g., zone transfer) for
* particular data. </li>
* </ul>
*/
public static final int NO_ERROR = 0, FORMAT_ERROR = 1, SERVER_FAILURE = 2,
NAME_ERROR = 3, NOT_IMPLEMENTED = 4, REFUSED = 5;
/**
* ID A 16 bit identifier assigned by the program that generates any kind of
* query. This identifier is copied the corresponding reply and can be used
* by the requester to match up replies to outstanding queries.
*/
private int id = (int) (Math.random() * 65536);
/**
* QR A one bit field that specifies whether this message is a query (0), or
* a response (1).
*/
private int queryResponse = QUERY;
/**
* OPCODE A four bit field that specifies kind of query in this message.
* This value is set by the originator of a query and copied into the
* response. The values are:
* <ul>
* <li> 0 - a standard query (QUERY) </li>
* <li> 1 - an inverse query (IQUERY) </li>
* <li> 2 - a server status request (STATUS) </li>
* </ul>
*/
private int opcode = 0;
/**
* AA Authoritative Answer - this bit is valid in responses, and specifies
* that the responding name server is an authority for the domain name in
* question section. Note that the contents of the answer section may have
* multiple owner names because of aliases. The AA bit corresponds to the
* name which matches the query name, or the first owner name in the answer
* section.
*/
private boolean authoritativeAnswer = false;
/**
* TC TrunCation - specifies that this message was truncated due to length
* greater than that permitted on the transmission channel.
*/
private boolean truncated = false;
/**
* RD Recursion Desired - this bit may be set in a query and is copied into
* the response. If RD is set, it directs the name server to pursue the
* query recursively. Recursive query support is optional.
*/
private boolean recursionDesired = true;
/**
* RA Recursion Available - this be is set or cleared in a response, and
* denotes whether recursive query support is available in the name server.
*/
private boolean recursionAvailable = true;
/**
* RCODE Response code - this 4 bit field is set as part of responses.
* <br />
* Dazu werden in dieser Klasse Konstanten zur Verfuegung gestellt.
*/
private int responseCode = NO_ERROR;
/**
* QDCOUNT an unsigned 16 bit integer specifying the number of entries in
* the question section.
*/
private int queryCount = 0;
/**
* ANCOUNT an unsigned 16 bit integer specifying the number of resource
* records in the answer section.
*/
private int answerCount = 0;
/**
* NSCOUNT an unsigned 16 bit integer specifying the number of name server
* resource records in the authority records section.
*/
private int nameServerCount = 0;
/** ARCOUNT Anzahl der Resource Records in der 'additional section' */
private int additionalCount = 0;
/**
* Anfragen (query) in der Nachricht
*/
private LinkedList<Query> queries = new LinkedList<Query>();
/** Resource Records in der 'answer section' */
private LinkedList<ResourceRecord> answerRecords = new LinkedList<ResourceRecord>();
/** Resource Records in der 'authority section' */
private LinkedList<ResourceRecord> authoratativeRecords = new LinkedList<ResourceRecord>();
/** Resource Records in der 'additional section' */
private LinkedList<ResourceRecord> additionalRecords = new LinkedList<ResourceRecord>();
/**
* Konstruktor zur Erzeugung einer DNS-Nachricht mit Standardwerten.
* Lediglich ob es sich um eine Anfrage oder eine Antwort handelt, muss als
* Parameter uebergeben werden. Dazu werden die Konstanten QUERY und
* RESPONSE verwendet.
*/
public DNSNachricht(int queryResponse) {
Main.debug.println("INVOKED ("+this.hashCode()+") "+getClass()+", DNSNachricht("+queryResponse+")");
this.queryResponse = queryResponse;
}
/**
* Ein Konstruktor, der aus einem String, der durch den Aufruf der Methode
* toString() erzeugt wurde, wieder eine DNSNachricht erstellt.
*
* @param nachricht
* ein String, der durch die Methode toString() erstellt wurde
*/
public DNSNachricht(String nachricht) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (DNSNachricht), constr: DNSNachricht("+nachricht+")");
StringTokenizer lineTokenizer, tokenizer;
String line, token;
if (nachricht != null) {
lineTokenizer = new StringTokenizer(nachricht, "\n");
line = lineTokenizer.nextToken();
tokenizer = new StringTokenizer(line, " ");
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken().trim();
if (token.startsWith("ID")) {
id = Integer.parseInt(token.substring(3));
}
else if (token.startsWith("QR")) {
queryResponse = Integer.parseInt(token.substring(3));
}
else if (token.startsWith("RCODE")) {
responseCode = Integer.parseInt(token.substring(6));
}
else if (token.startsWith("QDCOUNT")) {
queryCount = Integer.parseInt(token.substring(8));
}
else if (token.startsWith("ANCOUNT")) {
answerCount = Integer.parseInt(token.substring(8));
}
else if (token.startsWith("NSCOUNT")) {
nameServerCount = Integer.parseInt(token.substring(8));
}
else if (token.startsWith("ARCOUNT")) {
additionalCount = Integer.parseInt(token.substring(8));
}
}
for (int i = 0; i < queryCount && lineTokenizer.hasMoreTokens(); i++) {
line = lineTokenizer.nextToken();
queries.add(new Query(line));
}
for (int i = 0; i < answerCount && lineTokenizer.hasMoreTokens(); i++) {
line = lineTokenizer.nextToken();
answerRecords.add(new ResourceRecord(line));
}
for (int i = 0; i < nameServerCount
&& lineTokenizer.hasMoreTokens(); i++) {
line = lineTokenizer.nextToken();
authoratativeRecords.add(new ResourceRecord(line));
}
for (int i = 0; i < additionalCount
&& lineTokenizer.hasMoreTokens(); i++) {
line = lineTokenizer.nextToken();
additionalRecords.add(new ResourceRecord(line));
}
}
}
/**
* Diese Methode liefert die Nachricht mit ausgewaehlten Attributen des
* Kopfteils in der Form <Attribut>=<Wert> (z. B.
* "ID=42"), die durch Leerzeichen getrennt sind, und den
* Datenteil, der durch einen Zeilenumbruch vom Kopfteil abgetrennt ist.
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
ListIterator<?> it;
buffer.append("ID=" + id + " ");
buffer.append("QR=" + queryResponse + " ");
buffer.append("RCODE=" + responseCode + " ");
buffer.append("QDCOUNT=" + queryCount + " ");
buffer.append("ANCOUNT=" + answerCount + " ");
buffer.append("NSCOUNT=" + nameServerCount + " ");
buffer.append("ARCOUNT=" + additionalCount + " ");
buffer.append("\n");
it = queries.listIterator();
while (it.hasNext()) {
buffer.append(it.next().toString() + "\n");
}
it = answerRecords.listIterator();
while (it.hasNext()) {
buffer.append(it.next().toString() + "\n");
}
it = authoratativeRecords.listIterator();
while (it.hasNext()) {
buffer.append(it.next().toString() + "\n");
}
it = additionalRecords.listIterator();
while (it.hasNext()) {
buffer.append(it.next().toString() + "\n");
}
return buffer.toString();
}
/**
* Methode zum hinzufuegen einer Anfrage (query). Das Format muss
* folgendermassen sein: NAME TYPE CLASS (Bsp.: web.de. A IN)
*/
public void hinzuQuery(String anfrage) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (DNSNachricht), hinzuQuery("+anfrage+")");
queries.add(new Query(anfrage));
queryCount++;
}
/** Methode fuer den Zugriff auf die Anfragen (queries) */
public LinkedList<Query> holeQueries() {
return queries;
}
/**
* Methode fuer den Zugriff auf die Resource-Records in der 'answer
* section'.
*/
public LinkedList<ResourceRecord> holeAntwortResourceRecords() {
return answerRecords;
}
/**
* Methode fuer den Zugriff auf die Resource-Records in der 'authority
* section'.
*/
public LinkedList<ResourceRecord> holeAuthoritativeResourceRecords() {
return authoratativeRecords;
}
/**
* Methode fuer den Zugriff auf die Resource-Records in der 'additional
* section'.
*/
public LinkedList<ResourceRecord> holeZusatzResourceRecords() {
return additionalRecords;
}
/**
* Diese Methode fuegt der DNS-Nachricht einen Resource Record hinzu. Das
* Format muss folgendermassen aussehen: NAME TYPE CLASS TTL RDATA (Bsp.
* web.de. A IN 3600 217.72.195.42)
*/
public void hinzuAntwortResourceRecord(String record) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (DNSNachricht), hinzuAntwortResourceRecord("+record+")");
answerRecords.add(new ResourceRecord(record));
answerCount++;
}
/**
* Diese Methode fuegt der DNS-Nachricht einen Resource Record hinzu. Das
* Format muss folgendermassen aussehen: NAME TYPE CLASS TTL RDATA (Bsp.
* web.de. A IN 3600 217.72.195.42)
*/
public void hinzuAuthoritativeResourceRecord(String record) {
Main.debug.println("INVOKED ("+this.hashCode()+", T"+this.getId()+") "+getClass()+" (DNSNachricht), hinzuAuthoritativeResourceRecord("+record+")");
Main.debug.println("INVOKED: "+getClass()+", hinzuAuthoritativeResourceRecord("+record+")");
authoratativeRecords.add(new ResourceRecord(record));
nameServerCount++;
}
/**
* @return the authoritativeAnswer
*/
public boolean isAuthoritativeAnswer() {
return authoritativeAnswer;
}
/**
* @param authoritativeAnswer
* the authoritativeAnswer to set
*/
public void setAuthoritativeAnswer(boolean authoritativeAnswer) {
this.authoritativeAnswer = authoritativeAnswer;
}
/**
* @return the id
*/
public int getId() {
return id;
}
/**
* @param id
* the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* @return the opcode
*/
public int getOpcode() {
return opcode;
}
/**
* @param opcode
* the opcode to set
*/
public void setOpcode(int opcode) {
this.opcode = opcode;
}
/**
* @return the queryResponse
*/
public int getQueryResponse() {
return queryResponse;
}
/**
* @param queryResponse
* the queryResponse to set
*/
public void setQueryResponse(int queryResponse) {
this.queryResponse = queryResponse;
}
/**
* @return the recursionAvailable
*/
public boolean isRecursionAvailable() {
return recursionAvailable;
}
/**
* @param recursionAvailable
* the recursionAvailable to set
*/
public void setRecursionAvailable(boolean recursionAvailable) {
this.recursionAvailable = recursionAvailable;
}
/**
* @return the recursionDesired
*/
public boolean isRecursionDesired() {
return recursionDesired;
}
/**
* @param recursionDesired
* the recursionDesired to set
*/
public void setRecursionDesired(boolean recursionDesired) {
this.recursionDesired = recursionDesired;
}
/**
* @return the responseCode
*/
public int getResponseCode() {
return responseCode;
}
/**
* @param responseCode
* the responseCode to set
*/
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
/**
* @return the truncated
*/
public boolean isTruncated() {
return truncated;
}
/**
* @param truncated
* the truncated to set
*/
public void setTruncated(boolean truncated) {
this.truncated = truncated;
}
}