/*
* Copyrigth (C) 2010 Henrik Baastrup.
*
* Licensed under the GNU Lesser General Public License version 3;
* you may not use this file except in compliance with the License.
* You should have received a copy of the license together with this
* file but can obtain a copy of the License at:
*
* http://www.gnu.org/licenses/lgpl-3.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package javax.net.stun.dns;
/**
*
* @author Henrik Baastrup
*/
public class DMessage {
public static final int MAXLENGTH = 65535;
private byte message[] = new byte[MAXLENGTH];
private int mLength = 12;
/*
* specifies whether this message is a query (false), or a response (true).
*/
private boolean useQR = false;
/*
* 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.
*/
private boolean useAA = false;
/*
* TrunCation - specifies that this message was truncated
* due to length greater than that permitted on the
* transmission channel.
*/
private boolean useTC = false;
/*
* 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 useRD = false;
/*
* 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 useRA = false;
/*
* 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:
* 0 a standard query (QUERY)
* 1 an inverse query (IQUERY)
* 2 a server status request (STATUS)
*/
private int opcode = 0;
/*
* Response code - this 4 bit field is set as part of
* responses. The values have the following
* interpretation:
* 0 No error condition
* 1 Format error - The name server was
* unable to interpret the query.
* 2 Server failure - The name server was
* unable to process this query due to a
* problem with the name server.
* 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.
* 4 Not Implemented - The name server does
* not support the requested kind of query.
* 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.
*/
private int rcode = 0;
private int qdCount = 0;
private int anCount = 0;
private int nsCount = 0;
private int asCount = 0;
DResource questions[] = new DResource[0];
DResource answers[] = new DResource[0];
DResource authorities[] = new DResource[0];
DResource additionals[] = new DResource[0];
public void resetMessage() {
for (int i=0; i<MAXLENGTH; i++) message[i] = 0;
mLength = 12;
}
public void createHeader() {
long id = System.currentTimeMillis();
createHeader((int)id);
}
public void createHeader(int id) {
for (int i= 0; i<12; i++) message[i] = 0;
message[0] = (byte) ((id & 0xff00) >> 8);
message[1] = (byte) (id & 0x00ff);
message[2] = (byte) ((opcode & 0x0f) << 3);
if (useQR) message[2] |= 0x80;
if (useAA) message[2] |= 0x04;
if (useTC) message[2] |= 0x02;
if (useRD) message[2] |= 0x01;
message[3] = (byte) (rcode & 0x0f);
if (useRA) message[3] |= 0x80;
}
public void setAA(boolean arg0) {useAA = arg0;}
public void setTC(boolean arg0) {useTC = arg0;}
public void setRA(boolean arg0) {useRA = arg0;}
public void appendQuestion(byte[] buffer) {
append(buffer);
qdCount++;
message[4] = (byte) ((qdCount & 0xff00) >> 8);
message[5] = (byte) (qdCount & 0x00ff);
}
public void appendAnswer(byte[] buffer) {
append(buffer);
anCount++;
message[6] = (byte) ((anCount & 0xff00) >> 8);
message[7] = (byte) (anCount & 0x00ff);
}
public void appendAuthority(byte[] buffer) {
append(buffer);
nsCount++;
message[8] = (byte) ((nsCount & 0xff00) >> 8);
message[9] = (byte) (nsCount & 0x00ff);
}
public void appendAdditional(byte[] buffer) {
append(buffer);
asCount++;
message[10] = (byte) ((asCount & 0xff00) >> 8);
message[11] = (byte) (asCount & 0x00ff);
}
public void append(byte[] buffer) {
for (int i=0; i<buffer.length; i++) message[mLength++] = buffer[i];
}
public byte[] toBytes() {return message;}
public int length() {return mLength;}
public DResource[] getQuestions() {return questions;}
public DResource[] getAnswers() {return answers;}
public DResource[] getAuthorities() {return authorities;}
public DResource[] getAdditionals() {return additionals;}
public static DMessage create(byte buffer[], int length) {
DMessage retVal = new DMessage();
retVal.message = new byte[length];
retVal.mLength = length;
for (int i=0; i<length; i++) retVal.message[i] = buffer[i];
//Build Header cetion
retVal.opcode = (buffer[2] >> 3) & 0x0f;
retVal.rcode = buffer[3] & 0x0f;
if ((buffer[2] & 0x80)==0x80) retVal.useQR = true;
if ((buffer[2] & 0x04)==0x04) retVal.useAA = true;
if ((buffer[2] & 0x02)==0x02) retVal.useTC = true;
if ((buffer[2] & 0x01)==0x01) retVal.useRD = true;
if ((buffer[3] & 0x80)==0x80) retVal.useRA = true;
retVal.qdCount = ((buffer[4] << 8) + (buffer[5] & 0xFF));
retVal.anCount = ((buffer[6] << 8) + (buffer[7] & 0xFF));
retVal.nsCount = ((buffer[8] << 8) + (buffer[9] & 0xFF));
retVal.asCount = ((buffer[10] << 8) + (buffer[11] & 0xFF));
//Build Question section
int idx = 12;
retVal.questions = new DResource[retVal.qdCount];
for (int i=0; i<retVal.qdCount; i++) {
DResource resource = new DResource();
idx = fillResource(resource, buffer, idx, true);
retVal.questions[i] = resource;
}
//Build Answer section
retVal.answers = new DResource[retVal.anCount];
for (int i=0; i<retVal.anCount; i++) {
DResource resource = new DResource();
idx = fillResource(resource, buffer, idx, false);
retVal.answers[i] = resource;
}
// Build Authority section
retVal.authorities = new DResource[retVal.nsCount];
for (int i=0; i<retVal.nsCount; i++) {
DResource resource = new DResource();
idx = fillResource(resource, buffer, idx, false);
retVal.authorities[i] = resource;
}
//Build Additional section
retVal.additionals = new DResource[retVal.asCount];
for (int i=0; i<retVal.asCount; i++) {
DResource resource = new DResource();
idx = fillResource(resource, buffer, idx, false);
retVal.additionals[i] = resource;
}
return retVal;
}
private static int fillResource(DResource resource, byte buffer[], int start, boolean questionRes) {
int idx = start;
int dType = 0;
int dClass = 0;
int ttl = 0;
byte rdata[] = new byte[0];
String name = "";
if ((buffer[idx] & 0xc0)==0xc0) { //Name is offseted
int iName = ((buffer[idx] & 0x3f) << 8) + (buffer[idx+1] & 0xFF);
idx += 2;
name = createName(buffer, iName);
}
else {
name = createName(buffer, idx);
while (buffer[idx]!=0) idx++;
idx++;
}
dType =((buffer[idx] << 8) + (buffer[idx+1] & 0xFF));
idx += 2;
dClass =((buffer[idx] << 8) +(buffer[idx+1] & 0xFF));
idx += 2;
if (!questionRes) {
ttl = (buffer[idx] << 24) + (buffer[idx+1] << 16) + (buffer[idx+2] << 8) + (buffer[idx+3] & 0xFF);
idx += 4;
int rdataLen = (((buffer[idx] & 0x3f) << 8) + (buffer[idx+1] & 0xFF));
rdata = createINRdata(buffer, idx, dType);
idx += rdataLen+2;
}
if (resource!=null) {
resource.setName(name);
resource.setDType(dType);
resource.setDClass(dClass);
resource.setTTL(ttl);
resource.setRData(rdata);
}
return idx;
}
public static String createName(byte buffer[], int start) {
int idx = start;
StringBuilder str = new StringBuilder();
boolean firstRun = true;
while (idx<buffer.length && buffer[idx]!=0) {
if ((buffer[idx] & 0xc0)==0xc0) {
int next = ((buffer[idx] & 0x3f) << 8) + (buffer[idx+1] & 0xFF);
idx = next;
continue;
}
int len = buffer[idx++];
char buf[] = new char[len];
for (int i=0; i<len; i++) buf[i] = (char)buffer[idx++];
if (firstRun) {
str.append(buf);
firstRun = false;
}
else {
str.append('.');
str.append(buf);
}
}
return str.toString();
}
private static byte[] createINRdata(byte buffer[], int start, int dtype) {
int idx = start;
boolean bufIsBinary = true;
switch (dtype) {
case DType.SRV:
case DType.A:
bufIsBinary = true;
break;
case DType.NS:
case DType.CNAME:
bufIsBinary = false;
break;
default:
return new byte[0];
}
if (bufIsBinary) {
int len = ((buffer[idx] << 8) + (buffer[idx+1] & 0xFF));
idx +=2;
byte retBuf[] = new byte[len];
for(int i=0; i<len; i++) retBuf[i] = buffer[idx++];
return retBuf;
}
byte buf[] = new byte[MAXLENGTH/4]; //2 firts bit in length feilds are used for description.
int bufLen = 0;
idx += 2;
while (buffer[idx]!=0) {
if ((buffer[idx] & 0xc0)==0xc0) {
int next = ((buffer[idx] & 0x3f) << 8) + (buffer[idx+1] & 0xFF);
idx = next;
continue;
}
int len = buffer[idx];
for (int i=0; i<len+1; i++) buf[bufLen++] = buffer[idx++];
}
buf[bufLen++] = buffer[idx++];
byte retBuf[] = new byte[bufLen];
for (int i=0; i<bufLen; i++) retBuf[i] = buf[i];
return retBuf;
}
}