Skip to content

Commit

Permalink
Giant Refactor (#120)
Browse files Browse the repository at this point in the history
* Marshalling/Unmarshalling conformance
  * `PacketInput.readString`, `PacketOutput.writeString` and related methods have been removed. `WChar` has been used in its place.
  * `PacketInput.readStringBuffer`, `PacketOutput.writeStringBuffer` and related methods have been removed. `RPCUnicodeString` has been used in its place
  * `PacketInput.align()` and `PacketOutput.align()` have been removed. `Alignment` must always be considered by the caller by using `align(Alignment alignment)`.
* A significant change to `RequestResponse` (all request responses throughout the repository) has been made which performs the necessary alignment before reading the `NTCODE` as an int. Additionally, the code then checks that all bytes have been read off the wire, otherwise an `UnmarshalException` is thrown; this is important, as otherwise can not trust that we have read the `NTCODE` properly.
* All services have been refactored so that they have a clear distinction between RPC level objects, and DTOs. This includes adding DTOs where they were missing
* Adds lots of missing documentation
* Adds lots of missing unit test coverage
* Code styling conformance
  • Loading branch information
tosmun-r7 authored Dec 15, 2017
1 parent e5f693c commit 64e38bd
Show file tree
Hide file tree
Showing 121 changed files with 2,780 additions and 1,459 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*
*/

package com.rapid7.client.dcerpc.objects;
package com.rapid7.client.dcerpc.dto;

import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -51,7 +51,7 @@
* SidTypeComputer: This member is not used.
* SidTypeLabel: This member is not used.</pre></blockquote>
*/
public enum SIDNameUse {
public enum SIDUse {
SID_TYPE_USER((short) 1),
SID_TYPE_GROUP((short) 2),
SID_TYPE_DOMAIN((short) 3),
Expand All @@ -65,22 +65,22 @@ public enum SIDNameUse {

private final short value;

SIDNameUse(final short value) {
SIDUse(final short value) {
this.value = value;
}

public short getValue() {
return value;
}

private static final Map<Short, SIDNameUse> VALUE_MAP = new HashMap<>();
private static final Map<Short, SIDUse> VALUE_MAP = new HashMap<>();
static {
for (SIDNameUse sidNameUse : SIDNameUse.values()) {
for (SIDUse sidNameUse : SIDUse.values()) {
VALUE_MAP.put(sidNameUse.getValue(), sidNameUse);
}
}

public static SIDNameUse fromValue(final short value) {
public static SIDUse fromValue(final short value) {
return VALUE_MAP.get(value);
}
}
142 changes: 50 additions & 92 deletions src/main/java/com/rapid7/client/dcerpc/io/PacketInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,128 +18,86 @@
*/
package com.rapid7.client.dcerpc.io;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.UnmarshalException;
import com.rapid7.client.dcerpc.io.ndr.Unmarshallable;

public class PacketInput extends PrimitiveInput {
public PacketInput(final InputStream inputStream) {
super(inputStream);
}

/**
* Read a non-null object which implements {@link Unmarshallable}.
* This object *must* be considered a top level object; if it is not, consider calling
* {@link Unmarshallable#unmarshalPreamble(PacketInput)}, {@link Unmarshallable#unmarshalEntity(PacketInput)},
* and {@link Unmarshallable#unmarshalDeferrals(PacketInput)} separately at the appropriate locations.
* @param unmarshallable A non-null {@link Unmarshallable} object.
* @param <T> The class of the provided unmarshallable object.
* @return The same input parameter. Useful for chaining.
* @throws IOException On read failure.
*/
public <T extends Unmarshallable> T readUnmarshallable(T unmarshallable) throws IOException {
unmarshallable.unmarshalPreamble(this);
unmarshallable.unmarshalEntity(this);
unmarshallable.unmarshalDeferrals(this);
return unmarshallable;
}

public Integer readIntRef() throws IOException {
return 0 != readReferentID() ? readInt() : null;
}

public Long readLongRef() throws IOException {
return 0 != readReferentID() ? readLong() : null;
}

/**
* Read a referent ID unique to this instance of {@link PacketInput}.
* @return A referent ID unique to this instance of {@link PacketInput}.
* @throws IOException On read failure.
*/
public int readReferentID() throws IOException {
// Currently only supports NDR20
return readInt();
}

public byte[] readByteArray() throws IOException {
readInt();
final int initialOffset = readInt();
final int actualCount = readInt();
final byte[] result = new byte[initialOffset + actualCount];

for (int index = initialOffset; index < result.length; index++) {
result[index] = readByte();
}

return result;
}

public byte[] readByteArrayRef() throws IOException {
final byte[] result;
if (0 != readReferentID()) {
result = readByteArray();
align();
} else {
result = null;
}

return result;
}

public byte[] readRawBytes(int length) throws IOException {
/**
* Read and return length number of bytes.
* @param length The number of bytes to read.
* @return A byte[] populated with length bytes.
* @throws EOFException If not enough bytes are available.
* @throws IOException On read failure.
*/
public byte[] readRawBytes(final int length) throws IOException {
byte[] bytes = new byte[length];
readRawBytes(bytes);

return bytes;
}

public void readRawBytes(byte[] buf) throws IOException {
/**
* Read all bytes into the given buffer.
* @param buf The buffer to read into.
* @throws EOFException If not enough bytes are available to fill the buffer.
* @throws IOException On read failure.
*/
public void readRawBytes(final byte[] buf) throws IOException {
readFully(buf, 0, buf.length);
}

public String readString(final boolean nullTerminated) throws IOException {
final StringBuffer result;

readInt();
final int initialOffset = readInt();
final int currentChars = readInt();

result = new StringBuffer(currentChars);
result.setLength(initialOffset);

int currentOffset = 0;
while (currentOffset++ < currentChars) {
final char currentChar = (char) readShort();
if (nullTerminated && currentChar == 0) {
break;
}
result.append(currentChar);
}

while (currentOffset++ < currentChars) {
readShort();
}

align();

return result.toString();
}

public String readStringRef(final boolean nullTerminated) throws IOException {
final String result;

if (0 != readReferentID()) {
result = readString(nullTerminated);
align();
} else {
result = null;
}

return result != null ? result.toString() : null;
}

public String readStringBuf(final boolean nullTerminated) throws IOException {
readShort(); // Current byte length
readShort(); // Maximum byte length

return readStringRef(nullTerminated);
}

public String readStringBufRef(final boolean nullTerminated) throws IOException {
final String result;
if (0 != readReferentID()) {
result = readStringBuf(nullTerminated);
align();
} else {
result = null;
/**
* Read an unsigned integer which is to be used as an array size or offset.
*
* Due to the limitations of Java, we must used a signed integer, and array lengths can not
* exceed the maximum value of an unsigned integer. Therefore, if the read unsigned integer
* is greater than {@link Integer#MAX_VALUE}, this will throw an {@link UnmarshalException}.
*
* @param name The name of the entity being read. Used in the potential {@link UnmarshalException}.
* @return The unsigned integer which is guaranteed to be valid as an array size or offset.
* @throws UnmarshalException When the value exceeds {@link Integer#MAX_VALUE}.
* @throws IOException On read failure.
*/
public int readIndex(final String name) throws IOException {
final long ret = readUnsignedInt();
// Don't allow array length or index values bigger than signed int
if (ret > Integer.MAX_VALUE) {
throw new UnmarshalException(String.format("%s %d > %d", name, ret, Integer.MAX_VALUE));
}

return result;
return (int) ret;
}
}
Loading

0 comments on commit 64e38bd

Please sign in to comment.