Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions src/main/java/org/xbill/DNS/Record.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
import org.xbill.DNS.utils.base16;

/**
Expand All @@ -16,9 +21,11 @@
*
* @author Brian Wellington
*/
public abstract class Record implements Cloneable, Comparable<Record> {
@Slf4j
public abstract class Record implements Cloneable, Comparable<Record>, Serializable {
protected Name name;
protected int type, dclass;
protected int type;
protected int dclass;
protected long ttl;

private static final DecimalFormat byteFormat = new DecimalFormat();
Expand All @@ -27,6 +34,25 @@ public abstract class Record implements Cloneable, Comparable<Record> {
byteFormat.setMinimumIntegerDigits(3);
}

private static class RecordSerializationProxy implements Serializable {
private static final long serialVersionUID = 1434159920070152561L;
private final byte[] wireData;
private final boolean isEmpty;

RecordSerializationProxy(Record r) {
this.isEmpty = r instanceof EmptyRecord;
wireData = r.toWire(isEmpty ? Section.QUESTION : Section.ANSWER);
}

protected Object readResolve() throws ObjectStreamException {
try {
return Record.fromWire(wireData, isEmpty ? Section.QUESTION : Section.ANSWER);
} catch (IOException e) {
throw new InvalidObjectException(e.getMessage());
}
}
}

protected Record() {}

/** @since 3.1 */
Expand All @@ -43,6 +69,15 @@ protected Record(Name name, int type, int dclass, long ttl) {
this.ttl = ttl;
}

Object writeReplace() {
log.trace("Creating proxy object for serialization");
return new RecordSerializationProxy(this);
}

private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException("Use RecordSerializationProxy");
}

private static Record getEmptyRecord(Name name, int type, int dclass, long ttl, boolean hasData) {
Record rec;
if (hasData) {
Expand Down Expand Up @@ -169,7 +204,8 @@ public static Record newRecord(Name name, int type, int dclass) {
}

static Record fromWire(DNSInput in, int section, boolean isUpdate) throws IOException {
int type, dclass;
int type;
int dclass;
long ttl;
int length;
Name name;
Expand Down Expand Up @@ -404,12 +440,7 @@ protected static String byteArrayToString(byte[] array, boolean quote) {

/** Converts a byte array into the unknown RR format. */
protected static String unknownToString(byte[] data) {
StringBuilder sb = new StringBuilder();
sb.append("\\# ");
sb.append(data.length);
sb.append(" ");
sb.append(base16.toString(data));
return sb.toString();
return "\\# " + data.length + " " + base16.toString(data);
}

/**
Expand Down Expand Up @@ -453,8 +484,7 @@ public static Record fromString(
rec.rdataFromString(st, origin);
t = st.get();
if (t.type != Tokenizer.EOL && t.type != Tokenizer.EOF) {
throw st.exception(
"unexpected tokens at end of record (wanted EOL/EOF, got " + t.toString() + ")");
throw st.exception("unexpected tokens at end of record (wanted EOL/EOF, got " + t + ")");
}
return rec;
}
Expand Down
19 changes: 19 additions & 0 deletions src/test/java/org/xbill/DNS/ARecordTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -135,4 +139,19 @@ void rrToWire() {
ar.rrToWire(dout, null, false);
assertArrayEquals(m_addr_bytes, dout.toByteArray());
}

@Test
void testSerializable() throws IOException, ClassNotFoundException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
ARecord expected = new ARecord(Name.root, DClass.IN, 60, m_addr);
oos.writeObject(expected);
try (ObjectInputStream ois =
new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
Record actual = (Record) ois.readObject();
assertEquals(expected, actual);
}
}
}
}
}
43 changes: 34 additions & 9 deletions src/test/java/org/xbill/DNS/RecordTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Instant;
Expand Down Expand Up @@ -552,7 +556,6 @@ void fromString_invalid() throws IOException {
int t = Type.A;
int d = DClass.IN;
int ttl = 0xABE99;
InetAddress addr = InetAddress.getByName("191.234.43.10");

assertThrows(
RelativeNameException.class,
Expand Down Expand Up @@ -863,14 +866,36 @@ void testAllTypesHaveNoArgConstructor() {
assertNotNull(proto.get());
} catch (Exception e) {
fail(
"Record type "
+ Type.string(i)
+ " ("
+ i
+ ", "
+ proto.getClass().getSimpleName()
+ ")"
+ " seems to have no or invalid 0arg ctor");
String.format(
"Record type %s, (%d, %s) seems to have no or invalid 0arg ctor",
Type.string(i), i, proto.getClass().getSimpleName()));
}
}
}
}

@Test
void testSerializable() throws IOException {
for (int i = 1; i < 65535; i++) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
if (Type.getFactory(i) != null) {
Record expected = Record.newRecord(Name.root, i, DClass.IN);
try {
oos.writeObject(expected);
try (ObjectInputStream ois =
new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
Record actual = (Record) ois.readObject();
assertEquals(expected, actual);
}
} catch (Exception e) {
fail(
String.format(
"Record type %s (%d, %s) failed to (de)serialize",
Type.string(i), i, expected.getClass().getSimpleName()),
e);
}
}
}
}
}
Expand Down