diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a80e30c6..8102335d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,18 @@ - fix: use `StandardCharsets.UTF_8` explicitly when converting byte arrays to strings to ensure consistent behavior across different platforms. - refactor: use static initialization for `GsonSingleton` to ensure thread safety. - fix: use `commons-codec` for hex encoding/decoding in `Util` class to properly validate input and throw clear exceptions for invalid hex strings. +- fix: improve XDR decoding security and correctness. + - Add decoding depth limit to prevent stack overflow (default: 200) + - Add input length tracking to prevent DoS via oversized allocations + - Validate variable-length array/opaque/string sizes before allocation + - Validate variable-length types don't exceed declared max size + - Validate fixed-length opaque/array sizes match declared size + - Fix short read handling for opaque/string with proper padding + - Remove incorrect auto-padding from read(byte[], int, int) + - Reject unknown union discriminant values when no default arm + - Validate boolean/optional flags are strictly 0 or 1 per RFC 4506 + - Fix EOF handling in single-byte read + - Deprecate unsafe readIntArray/readFloatArray/readDoubleArray methods ## 2.2.1 diff --git a/Makefile b/Makefile index c1dd39c1c..50857a5e9 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ xdr/Stellar-contract-config-setting.x \ xdr/Stellar-exporter.x # xdrgen commit to use, see https://github.com/stellar/xdrgen -XDRGEN_COMMIT=6b98787dcbb2b6407880adec5f74c6d88975ab0f +XDRGEN_COMMIT=621d042b824f67ac65cc53d0e5e381e24aed4583 # stellar-xdr commit to use, see https://github.com/stellar/stellar-xdr XDR_COMMIT=4b7a2ef7931ab2ca2499be68d849f38190b443ca diff --git a/src/main/java/org/stellar/sdk/StrKey.java b/src/main/java/org/stellar/sdk/StrKey.java index 191e0f94c..25e2c8799 100644 --- a/src/main/java/org/stellar/sdk/StrKey.java +++ b/src/main/java/org/stellar/sdk/StrKey.java @@ -1,6 +1,5 @@ package org.stellar.sdk; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; @@ -18,7 +17,6 @@ import org.stellar.sdk.xdr.PublicKeyType; import org.stellar.sdk.xdr.Uint256; import org.stellar.sdk.xdr.Uint64; -import org.stellar.sdk.xdr.XdrDataInputStream; import org.stellar.sdk.xdr.XdrUnsignedHyperInteger; /** @@ -457,15 +455,12 @@ public static MuxedAccount encodeToXDRMuxedAccount(String data) { } break; case MED25519_PUBLIC_KEY: - XdrDataInputStream input = - new XdrDataInputStream( - new ByteArrayInputStream( - decodeCheck(VersionByte.MED25519_PUBLIC_KEY, data.toCharArray()))); + byte[] input = decodeCheck(VersionByte.MED25519_PUBLIC_KEY, data.toCharArray()); muxed.setDiscriminant(CryptoKeyType.KEY_TYPE_MUXED_ED25519); MuxedAccount.MuxedAccountMed25519 med = new MuxedAccount.MuxedAccountMed25519(); try { - med.setEd25519(Uint256.decode(input)); - med.setId(new Uint64(XdrUnsignedHyperInteger.decode(input))); + med.setEd25519(Uint256.fromXdrByteArray(Arrays.copyOfRange(input, 0, 32))); + med.setId(Uint64.fromXdrByteArray(Arrays.copyOfRange(input, 32, 40))); } catch (IOException e) { throw new IllegalArgumentException("invalid address: " + data, e); } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountEntry.java b/src/main/java/org/stellar/sdk/xdr/AccountEntry.java index 8e4dd3193..0c3b83121 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountEntry.java @@ -76,6 +76,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { homeDomain.encode(stream); thresholds.encode(stream); int signersSize = getSigners().length; + if (signersSize > 20) { + throw new IOException("signers size " + signersSize + " exceeds max size 20"); + } stream.writeInt(signersSize); for (int i = 0; i < signersSize; i++) { signers[i].encode(stream); @@ -83,28 +86,50 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static AccountEntry decode(XdrDataInputStream stream) throws IOException { + public static AccountEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntry decodedAccountEntry = new AccountEntry(); - decodedAccountEntry.accountID = AccountID.decode(stream); - decodedAccountEntry.balance = Int64.decode(stream); - decodedAccountEntry.seqNum = SequenceNumber.decode(stream); - decodedAccountEntry.numSubEntries = Uint32.decode(stream); - int inflationDestPresent = stream.readInt(); - if (inflationDestPresent != 0) { - decodedAccountEntry.inflationDest = AccountID.decode(stream); + decodedAccountEntry.accountID = AccountID.decode(stream, maxDepth); + decodedAccountEntry.balance = Int64.decode(stream, maxDepth); + decodedAccountEntry.seqNum = SequenceNumber.decode(stream, maxDepth); + decodedAccountEntry.numSubEntries = Uint32.decode(stream, maxDepth); + boolean inflationDestPresent = stream.readXdrBoolean(); + if (inflationDestPresent) { + decodedAccountEntry.inflationDest = AccountID.decode(stream, maxDepth); } - decodedAccountEntry.flags = Uint32.decode(stream); - decodedAccountEntry.homeDomain = String32.decode(stream); - decodedAccountEntry.thresholds = Thresholds.decode(stream); + decodedAccountEntry.flags = Uint32.decode(stream, maxDepth); + decodedAccountEntry.homeDomain = String32.decode(stream, maxDepth); + decodedAccountEntry.thresholds = Thresholds.decode(stream, maxDepth); int signersSize = stream.readInt(); + if (signersSize < 0) { + throw new IOException("signers size " + signersSize + " is negative"); + } + if (signersSize > 20) { + throw new IOException("signers size " + signersSize + " exceeds max size 20"); + } + int signersRemainingInputLen = stream.getRemainingInputLen(); + if (signersRemainingInputLen >= 0 && signersRemainingInputLen < signersSize) { + throw new IOException( + "signers size " + + signersSize + + " exceeds remaining input length " + + signersRemainingInputLen); + } decodedAccountEntry.signers = new Signer[signersSize]; for (int i = 0; i < signersSize; i++) { - decodedAccountEntry.signers[i] = Signer.decode(stream); + decodedAccountEntry.signers[i] = Signer.decode(stream, maxDepth); } - decodedAccountEntry.ext = AccountEntryExt.decode(stream); + decodedAccountEntry.ext = AccountEntryExt.decode(stream, maxDepth); return decodedAccountEntry; } + public static AccountEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -113,6 +138,7 @@ public static AccountEntry fromXdrBase64(String xdr) throws IOException { public static AccountEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -148,7 +174,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AccountEntryExt decode(XdrDataInputStream stream) throws IOException { + public static AccountEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntryExt decodedAccountEntryExt = new AccountEntryExt(); Integer discriminant = stream.readInt(); decodedAccountEntryExt.setDiscriminant(discriminant); @@ -156,12 +187,18 @@ public static AccountEntryExt decode(XdrDataInputStream stream) throws IOExcepti case 0: break; case 1: - decodedAccountEntryExt.v1 = AccountEntryExtensionV1.decode(stream); + decodedAccountEntryExt.v1 = AccountEntryExtensionV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAccountEntryExt; } + public static AccountEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -170,6 +207,7 @@ public static AccountEntryExt fromXdrBase64(String xdr) throws IOException { public static AccountEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV1.java b/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV1.java index f177ae4f9..207dd5763 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV1.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV1.java @@ -43,13 +43,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static AccountEntryExtensionV1 decode(XdrDataInputStream stream) throws IOException { + public static AccountEntryExtensionV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntryExtensionV1 decodedAccountEntryExtensionV1 = new AccountEntryExtensionV1(); - decodedAccountEntryExtensionV1.liabilities = Liabilities.decode(stream); - decodedAccountEntryExtensionV1.ext = AccountEntryExtensionV1Ext.decode(stream); + decodedAccountEntryExtensionV1.liabilities = Liabilities.decode(stream, maxDepth); + decodedAccountEntryExtensionV1.ext = AccountEntryExtensionV1Ext.decode(stream, maxDepth); return decodedAccountEntryExtensionV1; } + public static AccountEntryExtensionV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntryExtensionV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +67,7 @@ public static AccountEntryExtensionV1 fromXdrBase64(String xdr) throws IOExcepti public static AccountEntryExtensionV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -93,7 +103,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AccountEntryExtensionV1Ext decode(XdrDataInputStream stream) throws IOException { + public static AccountEntryExtensionV1Ext decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntryExtensionV1Ext decodedAccountEntryExtensionV1Ext = new AccountEntryExtensionV1Ext(); Integer discriminant = stream.readInt(); @@ -102,12 +117,18 @@ public static AccountEntryExtensionV1Ext decode(XdrDataInputStream stream) throw case 0: break; case 2: - decodedAccountEntryExtensionV1Ext.v2 = AccountEntryExtensionV2.decode(stream); + decodedAccountEntryExtensionV1Ext.v2 = AccountEntryExtensionV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAccountEntryExtensionV1Ext; } + public static AccountEntryExtensionV1Ext decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntryExtensionV1Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -116,6 +137,7 @@ public static AccountEntryExtensionV1Ext fromXdrBase64(String xdr) throws IOExce public static AccountEntryExtensionV1Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV2.java b/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV2.java index 93dc1349a..950ed3142 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV2.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV2.java @@ -46,6 +46,10 @@ public void encode(XdrDataOutputStream stream) throws IOException { numSponsored.encode(stream); numSponsoring.encode(stream); int signerSponsoringIDsSize = getSignerSponsoringIDs().length; + if (signerSponsoringIDsSize > 20) { + throw new IOException( + "signerSponsoringIDs size " + signerSponsoringIDsSize + " exceeds max size 20"); + } stream.writeInt(signerSponsoringIDsSize); for (int i = 0; i < signerSponsoringIDsSize; i++) { signerSponsoringIDs[i].encode(stream); @@ -53,20 +57,46 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static AccountEntryExtensionV2 decode(XdrDataInputStream stream) throws IOException { + public static AccountEntryExtensionV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntryExtensionV2 decodedAccountEntryExtensionV2 = new AccountEntryExtensionV2(); - decodedAccountEntryExtensionV2.numSponsored = Uint32.decode(stream); - decodedAccountEntryExtensionV2.numSponsoring = Uint32.decode(stream); + decodedAccountEntryExtensionV2.numSponsored = Uint32.decode(stream, maxDepth); + decodedAccountEntryExtensionV2.numSponsoring = Uint32.decode(stream, maxDepth); int signerSponsoringIDsSize = stream.readInt(); + if (signerSponsoringIDsSize < 0) { + throw new IOException("signerSponsoringIDs size " + signerSponsoringIDsSize + " is negative"); + } + if (signerSponsoringIDsSize > 20) { + throw new IOException( + "signerSponsoringIDs size " + signerSponsoringIDsSize + " exceeds max size 20"); + } + int signerSponsoringIDsRemainingInputLen = stream.getRemainingInputLen(); + if (signerSponsoringIDsRemainingInputLen >= 0 + && signerSponsoringIDsRemainingInputLen < signerSponsoringIDsSize) { + throw new IOException( + "signerSponsoringIDs size " + + signerSponsoringIDsSize + + " exceeds remaining input length " + + signerSponsoringIDsRemainingInputLen); + } decodedAccountEntryExtensionV2.signerSponsoringIDs = new SponsorshipDescriptor[signerSponsoringIDsSize]; for (int i = 0; i < signerSponsoringIDsSize; i++) { - decodedAccountEntryExtensionV2.signerSponsoringIDs[i] = SponsorshipDescriptor.decode(stream); + decodedAccountEntryExtensionV2.signerSponsoringIDs[i] = + SponsorshipDescriptor.decode(stream, maxDepth); } - decodedAccountEntryExtensionV2.ext = AccountEntryExtensionV2Ext.decode(stream); + decodedAccountEntryExtensionV2.ext = AccountEntryExtensionV2Ext.decode(stream, maxDepth); return decodedAccountEntryExtensionV2; } + public static AccountEntryExtensionV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntryExtensionV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -75,6 +105,7 @@ public static AccountEntryExtensionV2 fromXdrBase64(String xdr) throws IOExcepti public static AccountEntryExtensionV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -110,7 +141,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AccountEntryExtensionV2Ext decode(XdrDataInputStream stream) throws IOException { + public static AccountEntryExtensionV2Ext decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntryExtensionV2Ext decodedAccountEntryExtensionV2Ext = new AccountEntryExtensionV2Ext(); Integer discriminant = stream.readInt(); @@ -119,12 +155,18 @@ public static AccountEntryExtensionV2Ext decode(XdrDataInputStream stream) throw case 0: break; case 3: - decodedAccountEntryExtensionV2Ext.v3 = AccountEntryExtensionV3.decode(stream); + decodedAccountEntryExtensionV2Ext.v3 = AccountEntryExtensionV3.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAccountEntryExtensionV2Ext; } + public static AccountEntryExtensionV2Ext decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntryExtensionV2Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -133,6 +175,7 @@ public static AccountEntryExtensionV2Ext fromXdrBase64(String xdr) throws IOExce public static AccountEntryExtensionV2Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV3.java b/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV3.java index 25028f1dc..6aebd67ca 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV3.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountEntryExtensionV3.java @@ -44,14 +44,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { seqTime.encode(stream); } - public static AccountEntryExtensionV3 decode(XdrDataInputStream stream) throws IOException { + public static AccountEntryExtensionV3 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountEntryExtensionV3 decodedAccountEntryExtensionV3 = new AccountEntryExtensionV3(); - decodedAccountEntryExtensionV3.ext = ExtensionPoint.decode(stream); - decodedAccountEntryExtensionV3.seqLedger = Uint32.decode(stream); - decodedAccountEntryExtensionV3.seqTime = TimePoint.decode(stream); + decodedAccountEntryExtensionV3.ext = ExtensionPoint.decode(stream, maxDepth); + decodedAccountEntryExtensionV3.seqLedger = Uint32.decode(stream, maxDepth); + decodedAccountEntryExtensionV3.seqTime = TimePoint.decode(stream, maxDepth); return decodedAccountEntryExtensionV3; } + public static AccountEntryExtensionV3 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountEntryExtensionV3 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +69,7 @@ public static AccountEntryExtensionV3 fromXdrBase64(String xdr) throws IOExcepti public static AccountEntryExtensionV3 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountFlags.java b/src/main/java/org/stellar/sdk/xdr/AccountFlags.java index ad910866b..af0facfcd 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountFlags.java @@ -46,7 +46,8 @@ public int getValue() { return value; } - public static AccountFlags decode(XdrDataInputStream stream) throws IOException { + public static AccountFlags decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -62,6 +63,10 @@ public static AccountFlags decode(XdrDataInputStream stream) throws IOException } } + public static AccountFlags decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -74,6 +79,7 @@ public static AccountFlags fromXdrBase64(String xdr) throws IOException { public static AccountFlags fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountID.java b/src/main/java/org/stellar/sdk/xdr/AccountID.java index 76f5c51dd..785cb8468 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountID.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountID.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { AccountID.encode(stream); } - public static AccountID decode(XdrDataInputStream stream) throws IOException { + public static AccountID decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountID decodedAccountID = new AccountID(); - decodedAccountID.AccountID = PublicKey.decode(stream); + decodedAccountID.AccountID = PublicKey.decode(stream, maxDepth); return decodedAccountID; } + public static AccountID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static AccountID fromXdrBase64(String xdr) throws IOException { public static AccountID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountMergeResult.java b/src/main/java/org/stellar/sdk/xdr/AccountMergeResult.java index a3db43f3d..deab5c7ea 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountMergeResult.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountMergeResult.java @@ -55,13 +55,18 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AccountMergeResult decode(XdrDataInputStream stream) throws IOException { + public static AccountMergeResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AccountMergeResult decodedAccountMergeResult = new AccountMergeResult(); - AccountMergeResultCode discriminant = AccountMergeResultCode.decode(stream); + AccountMergeResultCode discriminant = AccountMergeResultCode.decode(stream, maxDepth); decodedAccountMergeResult.setDiscriminant(discriminant); switch (decodedAccountMergeResult.getDiscriminant()) { case ACCOUNT_MERGE_SUCCESS: - decodedAccountMergeResult.sourceAccountBalance = Int64.decode(stream); + decodedAccountMergeResult.sourceAccountBalance = Int64.decode(stream, maxDepth); break; case ACCOUNT_MERGE_MALFORMED: case ACCOUNT_MERGE_NO_ACCOUNT: @@ -71,10 +76,16 @@ public static AccountMergeResult decode(XdrDataInputStream stream) throws IOExce case ACCOUNT_MERGE_DEST_FULL: case ACCOUNT_MERGE_IS_SPONSOR: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAccountMergeResult; } + public static AccountMergeResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AccountMergeResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -83,6 +94,7 @@ public static AccountMergeResult fromXdrBase64(String xdr) throws IOException { public static AccountMergeResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AccountMergeResultCode.java b/src/main/java/org/stellar/sdk/xdr/AccountMergeResultCode.java index f4f1ed0e8..dc0b660bd 100644 --- a/src/main/java/org/stellar/sdk/xdr/AccountMergeResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/AccountMergeResultCode.java @@ -47,7 +47,9 @@ public int getValue() { return value; } - public static AccountMergeResultCode decode(XdrDataInputStream stream) throws IOException { + public static AccountMergeResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -71,6 +73,10 @@ public static AccountMergeResultCode decode(XdrDataInputStream stream) throws IO } } + public static AccountMergeResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -83,6 +89,7 @@ public static AccountMergeResultCode fromXdrBase64(String xdr) throws IOExceptio public static AccountMergeResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java b/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java index 8decdba8b..0365f1e21 100644 --- a/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java +++ b/src/main/java/org/stellar/sdk/xdr/AllowTrustOp.java @@ -40,14 +40,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { authorize.encode(stream); } - public static AllowTrustOp decode(XdrDataInputStream stream) throws IOException { + public static AllowTrustOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AllowTrustOp decodedAllowTrustOp = new AllowTrustOp(); - decodedAllowTrustOp.trustor = AccountID.decode(stream); - decodedAllowTrustOp.asset = AssetCode.decode(stream); - decodedAllowTrustOp.authorize = Uint32.decode(stream); + decodedAllowTrustOp.trustor = AccountID.decode(stream, maxDepth); + decodedAllowTrustOp.asset = AssetCode.decode(stream, maxDepth); + decodedAllowTrustOp.authorize = Uint32.decode(stream, maxDepth); return decodedAllowTrustOp; } + public static AllowTrustOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AllowTrustOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -56,6 +64,7 @@ public static AllowTrustOp fromXdrBase64(String xdr) throws IOException { public static AllowTrustOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AllowTrustResult.java b/src/main/java/org/stellar/sdk/xdr/AllowTrustResult.java index 2e9f5332f..c0000e910 100644 --- a/src/main/java/org/stellar/sdk/xdr/AllowTrustResult.java +++ b/src/main/java/org/stellar/sdk/xdr/AllowTrustResult.java @@ -51,9 +51,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AllowTrustResult decode(XdrDataInputStream stream) throws IOException { + public static AllowTrustResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AllowTrustResult decodedAllowTrustResult = new AllowTrustResult(); - AllowTrustResultCode discriminant = AllowTrustResultCode.decode(stream); + AllowTrustResultCode discriminant = AllowTrustResultCode.decode(stream, maxDepth); decodedAllowTrustResult.setDiscriminant(discriminant); switch (decodedAllowTrustResult.getDiscriminant()) { case ALLOW_TRUST_SUCCESS: @@ -65,10 +70,16 @@ public static AllowTrustResult decode(XdrDataInputStream stream) throws IOExcept case ALLOW_TRUST_SELF_NOT_ALLOWED: case ALLOW_TRUST_LOW_RESERVE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAllowTrustResult; } + public static AllowTrustResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AllowTrustResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -77,6 +88,7 @@ public static AllowTrustResult fromXdrBase64(String xdr) throws IOException { public static AllowTrustResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AllowTrustResultCode.java b/src/main/java/org/stellar/sdk/xdr/AllowTrustResultCode.java index 1311d82e9..1bc704f4e 100644 --- a/src/main/java/org/stellar/sdk/xdr/AllowTrustResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/AllowTrustResultCode.java @@ -46,7 +46,9 @@ public int getValue() { return value; } - public static AllowTrustResultCode decode(XdrDataInputStream stream) throws IOException { + public static AllowTrustResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -68,6 +70,10 @@ public static AllowTrustResultCode decode(XdrDataInputStream stream) throws IOEx } } + public static AllowTrustResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -80,6 +86,7 @@ public static AllowTrustResultCode fromXdrBase64(String xdr) throws IOException public static AllowTrustResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AlphaNum12.java b/src/main/java/org/stellar/sdk/xdr/AlphaNum12.java index c8580bc46..8e817368c 100644 --- a/src/main/java/org/stellar/sdk/xdr/AlphaNum12.java +++ b/src/main/java/org/stellar/sdk/xdr/AlphaNum12.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { issuer.encode(stream); } - public static AlphaNum12 decode(XdrDataInputStream stream) throws IOException { + public static AlphaNum12 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AlphaNum12 decodedAlphaNum12 = new AlphaNum12(); - decodedAlphaNum12.assetCode = AssetCode12.decode(stream); - decodedAlphaNum12.issuer = AccountID.decode(stream); + decodedAlphaNum12.assetCode = AssetCode12.decode(stream, maxDepth); + decodedAlphaNum12.issuer = AccountID.decode(stream, maxDepth); return decodedAlphaNum12; } + public static AlphaNum12 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AlphaNum12 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static AlphaNum12 fromXdrBase64(String xdr) throws IOException { public static AlphaNum12 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AlphaNum4.java b/src/main/java/org/stellar/sdk/xdr/AlphaNum4.java index 3d91e4af6..2b33f363c 100644 --- a/src/main/java/org/stellar/sdk/xdr/AlphaNum4.java +++ b/src/main/java/org/stellar/sdk/xdr/AlphaNum4.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { issuer.encode(stream); } - public static AlphaNum4 decode(XdrDataInputStream stream) throws IOException { + public static AlphaNum4 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AlphaNum4 decodedAlphaNum4 = new AlphaNum4(); - decodedAlphaNum4.assetCode = AssetCode4.decode(stream); - decodedAlphaNum4.issuer = AccountID.decode(stream); + decodedAlphaNum4.assetCode = AssetCode4.decode(stream, maxDepth); + decodedAlphaNum4.issuer = AccountID.decode(stream, maxDepth); return decodedAlphaNum4; } + public static AlphaNum4 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AlphaNum4 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static AlphaNum4 fromXdrBase64(String xdr) throws IOException { public static AlphaNum4 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Asset.java b/src/main/java/org/stellar/sdk/xdr/Asset.java index 0d4ba8d86..de8c057e6 100644 --- a/src/main/java/org/stellar/sdk/xdr/Asset.java +++ b/src/main/java/org/stellar/sdk/xdr/Asset.java @@ -53,23 +53,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static Asset decode(XdrDataInputStream stream) throws IOException { + public static Asset decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Asset decodedAsset = new Asset(); - AssetType discriminant = AssetType.decode(stream); + AssetType discriminant = AssetType.decode(stream, maxDepth); decodedAsset.setDiscriminant(discriminant); switch (decodedAsset.getDiscriminant()) { case ASSET_TYPE_NATIVE: break; case ASSET_TYPE_CREDIT_ALPHANUM4: - decodedAsset.alphaNum4 = AlphaNum4.decode(stream); + decodedAsset.alphaNum4 = AlphaNum4.decode(stream, maxDepth); break; case ASSET_TYPE_CREDIT_ALPHANUM12: - decodedAsset.alphaNum12 = AlphaNum12.decode(stream); + decodedAsset.alphaNum12 = AlphaNum12.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAsset; } + public static Asset decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Asset fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -78,6 +88,7 @@ public static Asset fromXdrBase64(String xdr) throws IOException { public static Asset fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AssetCode.java b/src/main/java/org/stellar/sdk/xdr/AssetCode.java index c259ff5cf..aed4d2478 100644 --- a/src/main/java/org/stellar/sdk/xdr/AssetCode.java +++ b/src/main/java/org/stellar/sdk/xdr/AssetCode.java @@ -48,21 +48,31 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AssetCode decode(XdrDataInputStream stream) throws IOException { + public static AssetCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AssetCode decodedAssetCode = new AssetCode(); - AssetType discriminant = AssetType.decode(stream); + AssetType discriminant = AssetType.decode(stream, maxDepth); decodedAssetCode.setDiscriminant(discriminant); switch (decodedAssetCode.getDiscriminant()) { case ASSET_TYPE_CREDIT_ALPHANUM4: - decodedAssetCode.assetCode4 = AssetCode4.decode(stream); + decodedAssetCode.assetCode4 = AssetCode4.decode(stream, maxDepth); break; case ASSET_TYPE_CREDIT_ALPHANUM12: - decodedAssetCode.assetCode12 = AssetCode12.decode(stream); + decodedAssetCode.assetCode12 = AssetCode12.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAssetCode; } + public static AssetCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AssetCode fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -71,6 +81,7 @@ public static AssetCode fromXdrBase64(String xdr) throws IOException { public static AssetCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AssetCode12.java b/src/main/java/org/stellar/sdk/xdr/AssetCode12.java index f0859b51e..38c5279b0 100644 --- a/src/main/java/org/stellar/sdk/xdr/AssetCode12.java +++ b/src/main/java/org/stellar/sdk/xdr/AssetCode12.java @@ -25,17 +25,29 @@ public class AssetCode12 implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int AssetCode12Size = AssetCode12.length; + if (AssetCode12Size != 12) { + throw new IOException( + "AssetCode12 size " + AssetCode12Size + " does not match fixed size 12"); + } stream.write(getAssetCode12(), 0, AssetCode12Size); } - public static AssetCode12 decode(XdrDataInputStream stream) throws IOException { + public static AssetCode12 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AssetCode12 decodedAssetCode12 = new AssetCode12(); int AssetCode12Size = 12; decodedAssetCode12.AssetCode12 = new byte[AssetCode12Size]; - stream.read(decodedAssetCode12.AssetCode12, 0, AssetCode12Size); + stream.readPaddedData(decodedAssetCode12.AssetCode12, 0, AssetCode12Size); return decodedAssetCode12; } + public static AssetCode12 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AssetCode12 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -44,6 +56,7 @@ public static AssetCode12 fromXdrBase64(String xdr) throws IOException { public static AssetCode12 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AssetCode4.java b/src/main/java/org/stellar/sdk/xdr/AssetCode4.java index 72df36b04..50238206c 100644 --- a/src/main/java/org/stellar/sdk/xdr/AssetCode4.java +++ b/src/main/java/org/stellar/sdk/xdr/AssetCode4.java @@ -25,17 +25,28 @@ public class AssetCode4 implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int AssetCode4Size = AssetCode4.length; + if (AssetCode4Size != 4) { + throw new IOException("AssetCode4 size " + AssetCode4Size + " does not match fixed size 4"); + } stream.write(getAssetCode4(), 0, AssetCode4Size); } - public static AssetCode4 decode(XdrDataInputStream stream) throws IOException { + public static AssetCode4 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AssetCode4 decodedAssetCode4 = new AssetCode4(); int AssetCode4Size = 4; decodedAssetCode4.AssetCode4 = new byte[AssetCode4Size]; - stream.read(decodedAssetCode4.AssetCode4, 0, AssetCode4Size); + stream.readPaddedData(decodedAssetCode4.AssetCode4, 0, AssetCode4Size); return decodedAssetCode4; } + public static AssetCode4 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AssetCode4 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -44,6 +55,7 @@ public static AssetCode4 fromXdrBase64(String xdr) throws IOException { public static AssetCode4 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AssetType.java b/src/main/java/org/stellar/sdk/xdr/AssetType.java index 1eae419ed..8c6667209 100644 --- a/src/main/java/org/stellar/sdk/xdr/AssetType.java +++ b/src/main/java/org/stellar/sdk/xdr/AssetType.java @@ -36,7 +36,8 @@ public int getValue() { return value; } - public static AssetType decode(XdrDataInputStream stream) throws IOException { + public static AssetType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -52,6 +53,10 @@ public static AssetType decode(XdrDataInputStream stream) throws IOException { } } + public static AssetType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -64,6 +69,7 @@ public static AssetType fromXdrBase64(String xdr) throws IOException { public static AssetType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Auth.java b/src/main/java/org/stellar/sdk/xdr/Auth.java index f03ea31d7..ca6ad06dc 100644 --- a/src/main/java/org/stellar/sdk/xdr/Auth.java +++ b/src/main/java/org/stellar/sdk/xdr/Auth.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(flags); } - public static Auth decode(XdrDataInputStream stream) throws IOException { + public static Auth decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Auth decodedAuth = new Auth(); decodedAuth.flags = stream.readInt(); return decodedAuth; } + public static Auth decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Auth fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static Auth fromXdrBase64(String xdr) throws IOException { public static Auth fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AuthCert.java b/src/main/java/org/stellar/sdk/xdr/AuthCert.java index 438ea5ba1..7e6a3964c 100644 --- a/src/main/java/org/stellar/sdk/xdr/AuthCert.java +++ b/src/main/java/org/stellar/sdk/xdr/AuthCert.java @@ -38,14 +38,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { sig.encode(stream); } - public static AuthCert decode(XdrDataInputStream stream) throws IOException { + public static AuthCert decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AuthCert decodedAuthCert = new AuthCert(); - decodedAuthCert.pubkey = Curve25519Public.decode(stream); - decodedAuthCert.expiration = Uint64.decode(stream); - decodedAuthCert.sig = Signature.decode(stream); + decodedAuthCert.pubkey = Curve25519Public.decode(stream, maxDepth); + decodedAuthCert.expiration = Uint64.decode(stream, maxDepth); + decodedAuthCert.sig = Signature.decode(stream, maxDepth); return decodedAuthCert; } + public static AuthCert decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AuthCert fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +62,7 @@ public static AuthCert fromXdrBase64(String xdr) throws IOException { public static AuthCert fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java b/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java index 89cb81070..ff6284866 100644 --- a/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/AuthenticatedMessage.java @@ -44,18 +44,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static AuthenticatedMessage decode(XdrDataInputStream stream) throws IOException { + public static AuthenticatedMessage decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AuthenticatedMessage decodedAuthenticatedMessage = new AuthenticatedMessage(); - Uint32 discriminant = Uint32.decode(stream); + Uint32 discriminant = Uint32.decode(stream, maxDepth); decodedAuthenticatedMessage.setDiscriminant(discriminant); switch (decodedAuthenticatedMessage.getDiscriminant().getUint32().getNumber().intValue()) { case 0: - decodedAuthenticatedMessage.v0 = AuthenticatedMessageV0.decode(stream); + decodedAuthenticatedMessage.v0 = AuthenticatedMessageV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedAuthenticatedMessage; } + public static AuthenticatedMessage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AuthenticatedMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -64,6 +75,7 @@ public static AuthenticatedMessage fromXdrBase64(String xdr) throws IOException public static AuthenticatedMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -94,14 +106,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { mac.encode(stream); } - public static AuthenticatedMessageV0 decode(XdrDataInputStream stream) throws IOException { + public static AuthenticatedMessageV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; AuthenticatedMessageV0 decodedAuthenticatedMessageV0 = new AuthenticatedMessageV0(); - decodedAuthenticatedMessageV0.sequence = Uint64.decode(stream); - decodedAuthenticatedMessageV0.message = StellarMessage.decode(stream); - decodedAuthenticatedMessageV0.mac = HmacSha256Mac.decode(stream); + decodedAuthenticatedMessageV0.sequence = Uint64.decode(stream, maxDepth); + decodedAuthenticatedMessageV0.message = StellarMessage.decode(stream, maxDepth); + decodedAuthenticatedMessageV0.mac = HmacSha256Mac.decode(stream, maxDepth); return decodedAuthenticatedMessageV0; } + public static AuthenticatedMessageV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static AuthenticatedMessageV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -110,6 +131,7 @@ public static AuthenticatedMessageV0 fromXdrBase64(String xdr) throws IOExceptio public static AuthenticatedMessageV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesOp.java b/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesOp.java index 3551cab40..49c96130c 100644 --- a/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesOp.java +++ b/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesOp.java @@ -32,14 +32,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { sponsoredID.encode(stream); } - public static BeginSponsoringFutureReservesOp decode(XdrDataInputStream stream) + public static BeginSponsoringFutureReservesOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BeginSponsoringFutureReservesOp decodedBeginSponsoringFutureReservesOp = new BeginSponsoringFutureReservesOp(); - decodedBeginSponsoringFutureReservesOp.sponsoredID = AccountID.decode(stream); + decodedBeginSponsoringFutureReservesOp.sponsoredID = AccountID.decode(stream, maxDepth); return decodedBeginSponsoringFutureReservesOp; } + public static BeginSponsoringFutureReservesOp decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BeginSponsoringFutureReservesOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -48,6 +57,7 @@ public static BeginSponsoringFutureReservesOp fromXdrBase64(String xdr) throws I public static BeginSponsoringFutureReservesOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResult.java b/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResult.java index 70ec28305..92629b625 100644 --- a/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResult.java +++ b/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResult.java @@ -46,12 +46,16 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static BeginSponsoringFutureReservesResult decode(XdrDataInputStream stream) + public static BeginSponsoringFutureReservesResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BeginSponsoringFutureReservesResult decodedBeginSponsoringFutureReservesResult = new BeginSponsoringFutureReservesResult(); BeginSponsoringFutureReservesResultCode discriminant = - BeginSponsoringFutureReservesResultCode.decode(stream); + BeginSponsoringFutureReservesResultCode.decode(stream, maxDepth); decodedBeginSponsoringFutureReservesResult.setDiscriminant(discriminant); switch (decodedBeginSponsoringFutureReservesResult.getDiscriminant()) { case BEGIN_SPONSORING_FUTURE_RESERVES_SUCCESS: @@ -60,10 +64,17 @@ public static BeginSponsoringFutureReservesResult decode(XdrDataInputStream stre case BEGIN_SPONSORING_FUTURE_RESERVES_ALREADY_SPONSORED: case BEGIN_SPONSORING_FUTURE_RESERVES_RECURSIVE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedBeginSponsoringFutureReservesResult; } + public static BeginSponsoringFutureReservesResult decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BeginSponsoringFutureReservesResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -73,6 +84,7 @@ public static BeginSponsoringFutureReservesResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResultCode.java b/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResultCode.java index 1f62527d7..73e0167ca 100644 --- a/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/BeginSponsoringFutureReservesResultCode.java @@ -39,8 +39,9 @@ public int getValue() { return value; } - public static BeginSponsoringFutureReservesResultCode decode(XdrDataInputStream stream) - throws IOException { + public static BeginSponsoringFutureReservesResultCode decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -56,6 +57,11 @@ public static BeginSponsoringFutureReservesResultCode decode(XdrDataInputStream } } + public static BeginSponsoringFutureReservesResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -70,6 +76,7 @@ public static BeginSponsoringFutureReservesResultCode fromXdrByteArray(byte[] xd throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BinaryFuseFilterType.java b/src/main/java/org/stellar/sdk/xdr/BinaryFuseFilterType.java index 4f2e58d04..8edec4b33 100644 --- a/src/main/java/org/stellar/sdk/xdr/BinaryFuseFilterType.java +++ b/src/main/java/org/stellar/sdk/xdr/BinaryFuseFilterType.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static BinaryFuseFilterType decode(XdrDataInputStream stream) throws IOException { + public static BinaryFuseFilterType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +50,10 @@ public static BinaryFuseFilterType decode(XdrDataInputStream stream) throws IOEx } } + public static BinaryFuseFilterType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static BinaryFuseFilterType fromXdrBase64(String xdr) throws IOException public static BinaryFuseFilterType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BucketEntry.java b/src/main/java/org/stellar/sdk/xdr/BucketEntry.java index d7755d90d..8cbf0ed7b 100644 --- a/src/main/java/org/stellar/sdk/xdr/BucketEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/BucketEntry.java @@ -54,25 +54,35 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static BucketEntry decode(XdrDataInputStream stream) throws IOException { + public static BucketEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BucketEntry decodedBucketEntry = new BucketEntry(); - BucketEntryType discriminant = BucketEntryType.decode(stream); + BucketEntryType discriminant = BucketEntryType.decode(stream, maxDepth); decodedBucketEntry.setDiscriminant(discriminant); switch (decodedBucketEntry.getDiscriminant()) { case LIVEENTRY: case INITENTRY: - decodedBucketEntry.liveEntry = LedgerEntry.decode(stream); + decodedBucketEntry.liveEntry = LedgerEntry.decode(stream, maxDepth); break; case DEADENTRY: - decodedBucketEntry.deadEntry = LedgerKey.decode(stream); + decodedBucketEntry.deadEntry = LedgerKey.decode(stream, maxDepth); break; case METAENTRY: - decodedBucketEntry.metaEntry = BucketMetadata.decode(stream); + decodedBucketEntry.metaEntry = BucketMetadata.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedBucketEntry; } + public static BucketEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BucketEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -81,6 +91,7 @@ public static BucketEntry fromXdrBase64(String xdr) throws IOException { public static BucketEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BucketEntryType.java b/src/main/java/org/stellar/sdk/xdr/BucketEntryType.java index 0ab7c9418..1e749f92b 100644 --- a/src/main/java/org/stellar/sdk/xdr/BucketEntryType.java +++ b/src/main/java/org/stellar/sdk/xdr/BucketEntryType.java @@ -38,7 +38,8 @@ public int getValue() { return value; } - public static BucketEntryType decode(XdrDataInputStream stream) throws IOException { + public static BucketEntryType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case -1: @@ -54,6 +55,10 @@ public static BucketEntryType decode(XdrDataInputStream stream) throws IOExcepti } } + public static BucketEntryType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -66,6 +71,7 @@ public static BucketEntryType fromXdrBase64(String xdr) throws IOException { public static BucketEntryType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BucketListType.java b/src/main/java/org/stellar/sdk/xdr/BucketListType.java index d955afdaf..188221ddd 100644 --- a/src/main/java/org/stellar/sdk/xdr/BucketListType.java +++ b/src/main/java/org/stellar/sdk/xdr/BucketListType.java @@ -32,7 +32,8 @@ public int getValue() { return value; } - public static BucketListType decode(XdrDataInputStream stream) throws IOException { + public static BucketListType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +45,10 @@ public static BucketListType decode(XdrDataInputStream stream) throws IOExceptio } } + public static BucketListType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +61,7 @@ public static BucketListType fromXdrBase64(String xdr) throws IOException { public static BucketListType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BucketMetadata.java b/src/main/java/org/stellar/sdk/xdr/BucketMetadata.java index e51bc5828..09ed99c22 100644 --- a/src/main/java/org/stellar/sdk/xdr/BucketMetadata.java +++ b/src/main/java/org/stellar/sdk/xdr/BucketMetadata.java @@ -45,13 +45,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static BucketMetadata decode(XdrDataInputStream stream) throws IOException { + public static BucketMetadata decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BucketMetadata decodedBucketMetadata = new BucketMetadata(); - decodedBucketMetadata.ledgerVersion = Uint32.decode(stream); - decodedBucketMetadata.ext = BucketMetadataExt.decode(stream); + decodedBucketMetadata.ledgerVersion = Uint32.decode(stream, maxDepth); + decodedBucketMetadata.ext = BucketMetadataExt.decode(stream, maxDepth); return decodedBucketMetadata; } + public static BucketMetadata decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BucketMetadata fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +68,7 @@ public static BucketMetadata fromXdrBase64(String xdr) throws IOException { public static BucketMetadata fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -95,7 +104,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static BucketMetadataExt decode(XdrDataInputStream stream) throws IOException { + public static BucketMetadataExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BucketMetadataExt decodedBucketMetadataExt = new BucketMetadataExt(); Integer discriminant = stream.readInt(); decodedBucketMetadataExt.setDiscriminant(discriminant); @@ -103,12 +117,18 @@ public static BucketMetadataExt decode(XdrDataInputStream stream) throws IOExcep case 0: break; case 1: - decodedBucketMetadataExt.bucketListType = BucketListType.decode(stream); + decodedBucketMetadataExt.bucketListType = BucketListType.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedBucketMetadataExt; } + public static BucketMetadataExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BucketMetadataExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -117,6 +137,7 @@ public static BucketMetadataExt fromXdrBase64(String xdr) throws IOException { public static BucketMetadataExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BumpSequenceOp.java b/src/main/java/org/stellar/sdk/xdr/BumpSequenceOp.java index f4f04a772..8b0ee532c 100644 --- a/src/main/java/org/stellar/sdk/xdr/BumpSequenceOp.java +++ b/src/main/java/org/stellar/sdk/xdr/BumpSequenceOp.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { bumpTo.encode(stream); } - public static BumpSequenceOp decode(XdrDataInputStream stream) throws IOException { + public static BumpSequenceOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BumpSequenceOp decodedBumpSequenceOp = new BumpSequenceOp(); - decodedBumpSequenceOp.bumpTo = SequenceNumber.decode(stream); + decodedBumpSequenceOp.bumpTo = SequenceNumber.decode(stream, maxDepth); return decodedBumpSequenceOp; } + public static BumpSequenceOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BumpSequenceOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static BumpSequenceOp fromXdrBase64(String xdr) throws IOException { public static BumpSequenceOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BumpSequenceResult.java b/src/main/java/org/stellar/sdk/xdr/BumpSequenceResult.java index 927e00428..3392df1ed 100644 --- a/src/main/java/org/stellar/sdk/xdr/BumpSequenceResult.java +++ b/src/main/java/org/stellar/sdk/xdr/BumpSequenceResult.java @@ -41,19 +41,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static BumpSequenceResult decode(XdrDataInputStream stream) throws IOException { + public static BumpSequenceResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; BumpSequenceResult decodedBumpSequenceResult = new BumpSequenceResult(); - BumpSequenceResultCode discriminant = BumpSequenceResultCode.decode(stream); + BumpSequenceResultCode discriminant = BumpSequenceResultCode.decode(stream, maxDepth); decodedBumpSequenceResult.setDiscriminant(discriminant); switch (decodedBumpSequenceResult.getDiscriminant()) { case BUMP_SEQUENCE_SUCCESS: break; case BUMP_SEQUENCE_BAD_SEQ: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedBumpSequenceResult; } + public static BumpSequenceResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static BumpSequenceResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +73,7 @@ public static BumpSequenceResult fromXdrBase64(String xdr) throws IOException { public static BumpSequenceResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/BumpSequenceResultCode.java b/src/main/java/org/stellar/sdk/xdr/BumpSequenceResultCode.java index 22d8057ed..759d2e4d1 100644 --- a/src/main/java/org/stellar/sdk/xdr/BumpSequenceResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/BumpSequenceResultCode.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static BumpSequenceResultCode decode(XdrDataInputStream stream) throws IOException { + public static BumpSequenceResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -46,6 +48,10 @@ public static BumpSequenceResultCode decode(XdrDataInputStream stream) throws IO } } + public static BumpSequenceResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -58,6 +64,7 @@ public static BumpSequenceResultCode fromXdrBase64(String xdr) throws IOExceptio public static BumpSequenceResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ChangeTrustAsset.java b/src/main/java/org/stellar/sdk/xdr/ChangeTrustAsset.java index 5d8f6e8ef..a51879bf0 100644 --- a/src/main/java/org/stellar/sdk/xdr/ChangeTrustAsset.java +++ b/src/main/java/org/stellar/sdk/xdr/ChangeTrustAsset.java @@ -60,26 +60,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ChangeTrustAsset decode(XdrDataInputStream stream) throws IOException { + public static ChangeTrustAsset decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ChangeTrustAsset decodedChangeTrustAsset = new ChangeTrustAsset(); - AssetType discriminant = AssetType.decode(stream); + AssetType discriminant = AssetType.decode(stream, maxDepth); decodedChangeTrustAsset.setDiscriminant(discriminant); switch (decodedChangeTrustAsset.getDiscriminant()) { case ASSET_TYPE_NATIVE: break; case ASSET_TYPE_CREDIT_ALPHANUM4: - decodedChangeTrustAsset.alphaNum4 = AlphaNum4.decode(stream); + decodedChangeTrustAsset.alphaNum4 = AlphaNum4.decode(stream, maxDepth); break; case ASSET_TYPE_CREDIT_ALPHANUM12: - decodedChangeTrustAsset.alphaNum12 = AlphaNum12.decode(stream); + decodedChangeTrustAsset.alphaNum12 = AlphaNum12.decode(stream, maxDepth); break; case ASSET_TYPE_POOL_SHARE: - decodedChangeTrustAsset.liquidityPool = LiquidityPoolParameters.decode(stream); + decodedChangeTrustAsset.liquidityPool = LiquidityPoolParameters.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedChangeTrustAsset; } + public static ChangeTrustAsset decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ChangeTrustAsset fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -88,6 +99,7 @@ public static ChangeTrustAsset fromXdrBase64(String xdr) throws IOException { public static ChangeTrustAsset fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ChangeTrustOp.java b/src/main/java/org/stellar/sdk/xdr/ChangeTrustOp.java index 48e8c0cf6..0a742fa1f 100644 --- a/src/main/java/org/stellar/sdk/xdr/ChangeTrustOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ChangeTrustOp.java @@ -37,13 +37,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { limit.encode(stream); } - public static ChangeTrustOp decode(XdrDataInputStream stream) throws IOException { + public static ChangeTrustOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ChangeTrustOp decodedChangeTrustOp = new ChangeTrustOp(); - decodedChangeTrustOp.line = ChangeTrustAsset.decode(stream); - decodedChangeTrustOp.limit = Int64.decode(stream); + decodedChangeTrustOp.line = ChangeTrustAsset.decode(stream, maxDepth); + decodedChangeTrustOp.limit = Int64.decode(stream, maxDepth); return decodedChangeTrustOp; } + public static ChangeTrustOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ChangeTrustOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -52,6 +60,7 @@ public static ChangeTrustOp fromXdrBase64(String xdr) throws IOException { public static ChangeTrustOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResult.java b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResult.java index 5bc4223bf..321dd127c 100644 --- a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResult.java @@ -55,9 +55,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ChangeTrustResult decode(XdrDataInputStream stream) throws IOException { + public static ChangeTrustResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ChangeTrustResult decodedChangeTrustResult = new ChangeTrustResult(); - ChangeTrustResultCode discriminant = ChangeTrustResultCode.decode(stream); + ChangeTrustResultCode discriminant = ChangeTrustResultCode.decode(stream, maxDepth); decodedChangeTrustResult.setDiscriminant(discriminant); switch (decodedChangeTrustResult.getDiscriminant()) { case CHANGE_TRUST_SUCCESS: @@ -71,10 +76,16 @@ public static ChangeTrustResult decode(XdrDataInputStream stream) throws IOExcep case CHANGE_TRUST_CANNOT_DELETE: case CHANGE_TRUST_NOT_AUTH_MAINTAIN_LIABILITIES: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedChangeTrustResult; } + public static ChangeTrustResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ChangeTrustResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -83,6 +94,7 @@ public static ChangeTrustResult fromXdrBase64(String xdr) throws IOException { public static ChangeTrustResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java index 1b262bde2..c972f1517 100644 --- a/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ChangeTrustResultCode.java @@ -52,7 +52,9 @@ public int getValue() { return value; } - public static ChangeTrustResultCode decode(XdrDataInputStream stream) throws IOException { + public static ChangeTrustResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -78,6 +80,10 @@ public static ChangeTrustResultCode decode(XdrDataInputStream stream) throws IOE } } + public static ChangeTrustResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -90,6 +96,7 @@ public static ChangeTrustResultCode fromXdrBase64(String xdr) throws IOException public static ChangeTrustResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimAtom.java b/src/main/java/org/stellar/sdk/xdr/ClaimAtom.java index 976f6f2da..0448a7f29 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimAtom.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimAtom.java @@ -51,24 +51,34 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClaimAtom decode(XdrDataInputStream stream) throws IOException { + public static ClaimAtom decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimAtom decodedClaimAtom = new ClaimAtom(); - ClaimAtomType discriminant = ClaimAtomType.decode(stream); + ClaimAtomType discriminant = ClaimAtomType.decode(stream, maxDepth); decodedClaimAtom.setDiscriminant(discriminant); switch (decodedClaimAtom.getDiscriminant()) { case CLAIM_ATOM_TYPE_V0: - decodedClaimAtom.v0 = ClaimOfferAtomV0.decode(stream); + decodedClaimAtom.v0 = ClaimOfferAtomV0.decode(stream, maxDepth); break; case CLAIM_ATOM_TYPE_ORDER_BOOK: - decodedClaimAtom.orderBook = ClaimOfferAtom.decode(stream); + decodedClaimAtom.orderBook = ClaimOfferAtom.decode(stream, maxDepth); break; case CLAIM_ATOM_TYPE_LIQUIDITY_POOL: - decodedClaimAtom.liquidityPool = ClaimLiquidityAtom.decode(stream); + decodedClaimAtom.liquidityPool = ClaimLiquidityAtom.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimAtom; } + public static ClaimAtom decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimAtom fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -77,6 +87,7 @@ public static ClaimAtom fromXdrBase64(String xdr) throws IOException { public static ClaimAtom fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimAtomType.java b/src/main/java/org/stellar/sdk/xdr/ClaimAtomType.java index 2fbb23845..16bb358d4 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimAtomType.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimAtomType.java @@ -34,7 +34,8 @@ public int getValue() { return value; } - public static ClaimAtomType decode(XdrDataInputStream stream) throws IOException { + public static ClaimAtomType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +49,10 @@ public static ClaimAtomType decode(XdrDataInputStream stream) throws IOException } } + public static ClaimAtomType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +65,7 @@ public static ClaimAtomType fromXdrBase64(String xdr) throws IOException { public static ClaimAtomType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceOp.java b/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceOp.java index 531cfb9a8..2162e5ece 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceOp.java @@ -32,12 +32,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { balanceID.encode(stream); } - public static ClaimClaimableBalanceOp decode(XdrDataInputStream stream) throws IOException { + public static ClaimClaimableBalanceOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimClaimableBalanceOp decodedClaimClaimableBalanceOp = new ClaimClaimableBalanceOp(); - decodedClaimClaimableBalanceOp.balanceID = ClaimableBalanceID.decode(stream); + decodedClaimClaimableBalanceOp.balanceID = ClaimableBalanceID.decode(stream, maxDepth); return decodedClaimClaimableBalanceOp; } + public static ClaimClaimableBalanceOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimClaimableBalanceOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +55,7 @@ public static ClaimClaimableBalanceOp fromXdrBase64(String xdr) throws IOExcepti public static ClaimClaimableBalanceOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResult.java b/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResult.java index b8c7b78fa..36d5c574e 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResult.java @@ -49,10 +49,16 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClaimClaimableBalanceResult decode(XdrDataInputStream stream) throws IOException { + public static ClaimClaimableBalanceResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimClaimableBalanceResult decodedClaimClaimableBalanceResult = new ClaimClaimableBalanceResult(); - ClaimClaimableBalanceResultCode discriminant = ClaimClaimableBalanceResultCode.decode(stream); + ClaimClaimableBalanceResultCode discriminant = + ClaimClaimableBalanceResultCode.decode(stream, maxDepth); decodedClaimClaimableBalanceResult.setDiscriminant(discriminant); switch (decodedClaimClaimableBalanceResult.getDiscriminant()) { case CLAIM_CLAIMABLE_BALANCE_SUCCESS: @@ -63,10 +69,16 @@ public static ClaimClaimableBalanceResult decode(XdrDataInputStream stream) thro case CLAIM_CLAIMABLE_BALANCE_NO_TRUST: case CLAIM_CLAIMABLE_BALANCE_NOT_AUTHORIZED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimClaimableBalanceResult; } + public static ClaimClaimableBalanceResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimClaimableBalanceResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -75,6 +87,7 @@ public static ClaimClaimableBalanceResult fromXdrBase64(String xdr) throws IOExc public static ClaimClaimableBalanceResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResultCode.java b/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResultCode.java index d38ecb681..80dc50798 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimClaimableBalanceResultCode.java @@ -40,8 +40,9 @@ public int getValue() { return value; } - public static ClaimClaimableBalanceResultCode decode(XdrDataInputStream stream) + public static ClaimClaimableBalanceResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -61,6 +62,11 @@ public static ClaimClaimableBalanceResultCode decode(XdrDataInputStream stream) } } + public static ClaimClaimableBalanceResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -73,6 +79,7 @@ public static ClaimClaimableBalanceResultCode fromXdrBase64(String xdr) throws I public static ClaimClaimableBalanceResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimLiquidityAtom.java b/src/main/java/org/stellar/sdk/xdr/ClaimLiquidityAtom.java index ab537104b..b932b27ea 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimLiquidityAtom.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimLiquidityAtom.java @@ -48,16 +48,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { amountBought.encode(stream); } - public static ClaimLiquidityAtom decode(XdrDataInputStream stream) throws IOException { + public static ClaimLiquidityAtom decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimLiquidityAtom decodedClaimLiquidityAtom = new ClaimLiquidityAtom(); - decodedClaimLiquidityAtom.liquidityPoolID = PoolID.decode(stream); - decodedClaimLiquidityAtom.assetSold = Asset.decode(stream); - decodedClaimLiquidityAtom.amountSold = Int64.decode(stream); - decodedClaimLiquidityAtom.assetBought = Asset.decode(stream); - decodedClaimLiquidityAtom.amountBought = Int64.decode(stream); + decodedClaimLiquidityAtom.liquidityPoolID = PoolID.decode(stream, maxDepth); + decodedClaimLiquidityAtom.assetSold = Asset.decode(stream, maxDepth); + decodedClaimLiquidityAtom.amountSold = Int64.decode(stream, maxDepth); + decodedClaimLiquidityAtom.assetBought = Asset.decode(stream, maxDepth); + decodedClaimLiquidityAtom.amountBought = Int64.decode(stream, maxDepth); return decodedClaimLiquidityAtom; } + public static ClaimLiquidityAtom decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimLiquidityAtom fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +75,7 @@ public static ClaimLiquidityAtom fromXdrBase64(String xdr) throws IOException { public static ClaimLiquidityAtom fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtom.java b/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtom.java index f9958aaf4..ebf7d88cf 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtom.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtom.java @@ -52,17 +52,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { amountBought.encode(stream); } - public static ClaimOfferAtom decode(XdrDataInputStream stream) throws IOException { + public static ClaimOfferAtom decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimOfferAtom decodedClaimOfferAtom = new ClaimOfferAtom(); - decodedClaimOfferAtom.sellerID = AccountID.decode(stream); - decodedClaimOfferAtom.offerID = Int64.decode(stream); - decodedClaimOfferAtom.assetSold = Asset.decode(stream); - decodedClaimOfferAtom.amountSold = Int64.decode(stream); - decodedClaimOfferAtom.assetBought = Asset.decode(stream); - decodedClaimOfferAtom.amountBought = Int64.decode(stream); + decodedClaimOfferAtom.sellerID = AccountID.decode(stream, maxDepth); + decodedClaimOfferAtom.offerID = Int64.decode(stream, maxDepth); + decodedClaimOfferAtom.assetSold = Asset.decode(stream, maxDepth); + decodedClaimOfferAtom.amountSold = Int64.decode(stream, maxDepth); + decodedClaimOfferAtom.assetBought = Asset.decode(stream, maxDepth); + decodedClaimOfferAtom.amountBought = Int64.decode(stream, maxDepth); return decodedClaimOfferAtom; } + public static ClaimOfferAtom decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimOfferAtom fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -71,6 +79,7 @@ public static ClaimOfferAtom fromXdrBase64(String xdr) throws IOException { public static ClaimOfferAtom fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtomV0.java b/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtomV0.java index 921a07a1d..e6fc94210 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtomV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimOfferAtomV0.java @@ -52,17 +52,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { amountBought.encode(stream); } - public static ClaimOfferAtomV0 decode(XdrDataInputStream stream) throws IOException { + public static ClaimOfferAtomV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimOfferAtomV0 decodedClaimOfferAtomV0 = new ClaimOfferAtomV0(); - decodedClaimOfferAtomV0.sellerEd25519 = Uint256.decode(stream); - decodedClaimOfferAtomV0.offerID = Int64.decode(stream); - decodedClaimOfferAtomV0.assetSold = Asset.decode(stream); - decodedClaimOfferAtomV0.amountSold = Int64.decode(stream); - decodedClaimOfferAtomV0.assetBought = Asset.decode(stream); - decodedClaimOfferAtomV0.amountBought = Int64.decode(stream); + decodedClaimOfferAtomV0.sellerEd25519 = Uint256.decode(stream, maxDepth); + decodedClaimOfferAtomV0.offerID = Int64.decode(stream, maxDepth); + decodedClaimOfferAtomV0.assetSold = Asset.decode(stream, maxDepth); + decodedClaimOfferAtomV0.amountSold = Int64.decode(stream, maxDepth); + decodedClaimOfferAtomV0.assetBought = Asset.decode(stream, maxDepth); + decodedClaimOfferAtomV0.amountBought = Int64.decode(stream, maxDepth); return decodedClaimOfferAtomV0; } + public static ClaimOfferAtomV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimOfferAtomV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -71,6 +80,7 @@ public static ClaimOfferAtomV0 fromXdrBase64(String xdr) throws IOException { public static ClaimOfferAtomV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimPredicate.java b/src/main/java/org/stellar/sdk/xdr/ClaimPredicate.java index aa96be423..60e7fe352 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimPredicate.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimPredicate.java @@ -52,6 +52,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { break; case CLAIM_PREDICATE_AND: int andPredicatesSize = getAndPredicates().length; + if (andPredicatesSize > 2) { + throw new IOException("andPredicates size " + andPredicatesSize + " exceeds max size 2"); + } stream.writeInt(andPredicatesSize); for (int i = 0; i < andPredicatesSize; i++) { andPredicates[i].encode(stream); @@ -59,6 +62,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { break; case CLAIM_PREDICATE_OR: int orPredicatesSize = getOrPredicates().length; + if (orPredicatesSize > 2) { + throw new IOException("orPredicates size " + orPredicatesSize + " exceeds max size 2"); + } stream.writeInt(orPredicatesSize); for (int i = 0; i < orPredicatesSize; i++) { orPredicates[i].encode(stream); @@ -81,43 +87,83 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClaimPredicate decode(XdrDataInputStream stream) throws IOException { + public static ClaimPredicate decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimPredicate decodedClaimPredicate = new ClaimPredicate(); - ClaimPredicateType discriminant = ClaimPredicateType.decode(stream); + ClaimPredicateType discriminant = ClaimPredicateType.decode(stream, maxDepth); decodedClaimPredicate.setDiscriminant(discriminant); switch (decodedClaimPredicate.getDiscriminant()) { case CLAIM_PREDICATE_UNCONDITIONAL: break; case CLAIM_PREDICATE_AND: int andPredicatesSize = stream.readInt(); + if (andPredicatesSize < 0) { + throw new IOException("andPredicates size " + andPredicatesSize + " is negative"); + } + if (andPredicatesSize > 2) { + throw new IOException("andPredicates size " + andPredicatesSize + " exceeds max size 2"); + } + int andPredicatesRemainingInputLen = stream.getRemainingInputLen(); + if (andPredicatesRemainingInputLen >= 0 + && andPredicatesRemainingInputLen < andPredicatesSize) { + throw new IOException( + "andPredicates size " + + andPredicatesSize + + " exceeds remaining input length " + + andPredicatesRemainingInputLen); + } decodedClaimPredicate.andPredicates = new ClaimPredicate[andPredicatesSize]; for (int i = 0; i < andPredicatesSize; i++) { - decodedClaimPredicate.andPredicates[i] = ClaimPredicate.decode(stream); + decodedClaimPredicate.andPredicates[i] = ClaimPredicate.decode(stream, maxDepth); } break; case CLAIM_PREDICATE_OR: int orPredicatesSize = stream.readInt(); + if (orPredicatesSize < 0) { + throw new IOException("orPredicates size " + orPredicatesSize + " is negative"); + } + if (orPredicatesSize > 2) { + throw new IOException("orPredicates size " + orPredicatesSize + " exceeds max size 2"); + } + int orPredicatesRemainingInputLen = stream.getRemainingInputLen(); + if (orPredicatesRemainingInputLen >= 0 + && orPredicatesRemainingInputLen < orPredicatesSize) { + throw new IOException( + "orPredicates size " + + orPredicatesSize + + " exceeds remaining input length " + + orPredicatesRemainingInputLen); + } decodedClaimPredicate.orPredicates = new ClaimPredicate[orPredicatesSize]; for (int i = 0; i < orPredicatesSize; i++) { - decodedClaimPredicate.orPredicates[i] = ClaimPredicate.decode(stream); + decodedClaimPredicate.orPredicates[i] = ClaimPredicate.decode(stream, maxDepth); } break; case CLAIM_PREDICATE_NOT: - int notPredicatePresent = stream.readInt(); - if (notPredicatePresent != 0) { - decodedClaimPredicate.notPredicate = ClaimPredicate.decode(stream); + boolean notPredicatePresent = stream.readXdrBoolean(); + if (notPredicatePresent) { + decodedClaimPredicate.notPredicate = ClaimPredicate.decode(stream, maxDepth); } break; case CLAIM_PREDICATE_BEFORE_ABSOLUTE_TIME: - decodedClaimPredicate.absBefore = Int64.decode(stream); + decodedClaimPredicate.absBefore = Int64.decode(stream, maxDepth); break; case CLAIM_PREDICATE_BEFORE_RELATIVE_TIME: - decodedClaimPredicate.relBefore = Int64.decode(stream); + decodedClaimPredicate.relBefore = Int64.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimPredicate; } + public static ClaimPredicate decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimPredicate fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -126,6 +172,7 @@ public static ClaimPredicate fromXdrBase64(String xdr) throws IOException { public static ClaimPredicate fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimPredicateType.java b/src/main/java/org/stellar/sdk/xdr/ClaimPredicateType.java index 85fbb502e..84c607d3a 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimPredicateType.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimPredicateType.java @@ -40,7 +40,9 @@ public int getValue() { return value; } - public static ClaimPredicateType decode(XdrDataInputStream stream) throws IOException { + public static ClaimPredicateType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -60,6 +62,10 @@ public static ClaimPredicateType decode(XdrDataInputStream stream) throws IOExce } } + public static ClaimPredicateType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -72,6 +78,7 @@ public static ClaimPredicateType fromXdrBase64(String xdr) throws IOException { public static ClaimPredicateType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntry.java b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntry.java index ae001877f..14097dd6b 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntry.java @@ -55,6 +55,9 @@ public class ClaimableBalanceEntry implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { balanceID.encode(stream); int claimantsSize = getClaimants().length; + if (claimantsSize > 10) { + throw new IOException("claimants size " + claimantsSize + " exceeds max size 10"); + } stream.writeInt(claimantsSize); for (int i = 0; i < claimantsSize; i++) { claimants[i].encode(stream); @@ -64,20 +67,43 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static ClaimableBalanceEntry decode(XdrDataInputStream stream) throws IOException { + public static ClaimableBalanceEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimableBalanceEntry decodedClaimableBalanceEntry = new ClaimableBalanceEntry(); - decodedClaimableBalanceEntry.balanceID = ClaimableBalanceID.decode(stream); + decodedClaimableBalanceEntry.balanceID = ClaimableBalanceID.decode(stream, maxDepth); int claimantsSize = stream.readInt(); + if (claimantsSize < 0) { + throw new IOException("claimants size " + claimantsSize + " is negative"); + } + if (claimantsSize > 10) { + throw new IOException("claimants size " + claimantsSize + " exceeds max size 10"); + } + int claimantsRemainingInputLen = stream.getRemainingInputLen(); + if (claimantsRemainingInputLen >= 0 && claimantsRemainingInputLen < claimantsSize) { + throw new IOException( + "claimants size " + + claimantsSize + + " exceeds remaining input length " + + claimantsRemainingInputLen); + } decodedClaimableBalanceEntry.claimants = new Claimant[claimantsSize]; for (int i = 0; i < claimantsSize; i++) { - decodedClaimableBalanceEntry.claimants[i] = Claimant.decode(stream); + decodedClaimableBalanceEntry.claimants[i] = Claimant.decode(stream, maxDepth); } - decodedClaimableBalanceEntry.asset = Asset.decode(stream); - decodedClaimableBalanceEntry.amount = Int64.decode(stream); - decodedClaimableBalanceEntry.ext = ClaimableBalanceEntryExt.decode(stream); + decodedClaimableBalanceEntry.asset = Asset.decode(stream, maxDepth); + decodedClaimableBalanceEntry.amount = Int64.decode(stream, maxDepth); + decodedClaimableBalanceEntry.ext = ClaimableBalanceEntryExt.decode(stream, maxDepth); return decodedClaimableBalanceEntry; } + public static ClaimableBalanceEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimableBalanceEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -86,6 +112,7 @@ public static ClaimableBalanceEntry fromXdrBase64(String xdr) throws IOException public static ClaimableBalanceEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -121,7 +148,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClaimableBalanceEntryExt decode(XdrDataInputStream stream) throws IOException { + public static ClaimableBalanceEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimableBalanceEntryExt decodedClaimableBalanceEntryExt = new ClaimableBalanceEntryExt(); Integer discriminant = stream.readInt(); decodedClaimableBalanceEntryExt.setDiscriminant(discriminant); @@ -129,12 +161,19 @@ public static ClaimableBalanceEntryExt decode(XdrDataInputStream stream) throws case 0: break; case 1: - decodedClaimableBalanceEntryExt.v1 = ClaimableBalanceEntryExtensionV1.decode(stream); + decodedClaimableBalanceEntryExt.v1 = + ClaimableBalanceEntryExtensionV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimableBalanceEntryExt; } + public static ClaimableBalanceEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimableBalanceEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -143,6 +182,7 @@ public static ClaimableBalanceEntryExt fromXdrBase64(String xdr) throws IOExcept public static ClaimableBalanceEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntryExtensionV1.java b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntryExtensionV1.java index 01a3a1a80..f6c9c6639 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntryExtensionV1.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceEntryExtensionV1.java @@ -41,16 +41,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { flags.encode(stream); } - public static ClaimableBalanceEntryExtensionV1 decode(XdrDataInputStream stream) + public static ClaimableBalanceEntryExtensionV1 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimableBalanceEntryExtensionV1 decodedClaimableBalanceEntryExtensionV1 = new ClaimableBalanceEntryExtensionV1(); decodedClaimableBalanceEntryExtensionV1.ext = - ClaimableBalanceEntryExtensionV1Ext.decode(stream); - decodedClaimableBalanceEntryExtensionV1.flags = Uint32.decode(stream); + ClaimableBalanceEntryExtensionV1Ext.decode(stream, maxDepth); + decodedClaimableBalanceEntryExtensionV1.flags = Uint32.decode(stream, maxDepth); return decodedClaimableBalanceEntryExtensionV1; } + public static ClaimableBalanceEntryExtensionV1 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimableBalanceEntryExtensionV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +68,7 @@ public static ClaimableBalanceEntryExtensionV1 fromXdrBase64(String xdr) throws public static ClaimableBalanceEntryExtensionV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -88,8 +98,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClaimableBalanceEntryExtensionV1Ext decode(XdrDataInputStream stream) - throws IOException { + public static ClaimableBalanceEntryExtensionV1Ext decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimableBalanceEntryExtensionV1Ext decodedClaimableBalanceEntryExtensionV1Ext = new ClaimableBalanceEntryExtensionV1Ext(); Integer discriminant = stream.readInt(); @@ -97,10 +111,17 @@ public static ClaimableBalanceEntryExtensionV1Ext decode(XdrDataInputStream stre switch (decodedClaimableBalanceEntryExtensionV1Ext.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimableBalanceEntryExtensionV1Ext; } + public static ClaimableBalanceEntryExtensionV1Ext decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimableBalanceEntryExtensionV1Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -110,6 +131,7 @@ public static ClaimableBalanceEntryExtensionV1Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceFlags.java b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceFlags.java index 4819eaa36..ee07f8fcb 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceFlags.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static ClaimableBalanceFlags decode(XdrDataInputStream stream) throws IOException { + public static ClaimableBalanceFlags decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -42,6 +44,10 @@ public static ClaimableBalanceFlags decode(XdrDataInputStream stream) throws IOE } } + public static ClaimableBalanceFlags decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -54,6 +60,7 @@ public static ClaimableBalanceFlags fromXdrBase64(String xdr) throws IOException public static ClaimableBalanceFlags fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceID.java b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceID.java index a68995cab..950d287bf 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceID.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceID.java @@ -39,18 +39,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClaimableBalanceID decode(XdrDataInputStream stream) throws IOException { + public static ClaimableBalanceID decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimableBalanceID decodedClaimableBalanceID = new ClaimableBalanceID(); - ClaimableBalanceIDType discriminant = ClaimableBalanceIDType.decode(stream); + ClaimableBalanceIDType discriminant = ClaimableBalanceIDType.decode(stream, maxDepth); decodedClaimableBalanceID.setDiscriminant(discriminant); switch (decodedClaimableBalanceID.getDiscriminant()) { case CLAIMABLE_BALANCE_ID_TYPE_V0: - decodedClaimableBalanceID.v0 = Hash.decode(stream); + decodedClaimableBalanceID.v0 = Hash.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimableBalanceID; } + public static ClaimableBalanceID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimableBalanceID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +70,7 @@ public static ClaimableBalanceID fromXdrBase64(String xdr) throws IOException { public static ClaimableBalanceID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceIDType.java b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceIDType.java index 95dae1e19..1bf68604c 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceIDType.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimableBalanceIDType.java @@ -30,7 +30,9 @@ public int getValue() { return value; } - public static ClaimableBalanceIDType decode(XdrDataInputStream stream) throws IOException { + public static ClaimableBalanceIDType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -40,6 +42,10 @@ public static ClaimableBalanceIDType decode(XdrDataInputStream stream) throws IO } } + public static ClaimableBalanceIDType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +58,7 @@ public static ClaimableBalanceIDType fromXdrBase64(String xdr) throws IOExceptio public static ClaimableBalanceIDType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Claimant.java b/src/main/java/org/stellar/sdk/xdr/Claimant.java index 0342ac6b5..9978978a7 100644 --- a/src/main/java/org/stellar/sdk/xdr/Claimant.java +++ b/src/main/java/org/stellar/sdk/xdr/Claimant.java @@ -43,18 +43,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static Claimant decode(XdrDataInputStream stream) throws IOException { + public static Claimant decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Claimant decodedClaimant = new Claimant(); - ClaimantType discriminant = ClaimantType.decode(stream); + ClaimantType discriminant = ClaimantType.decode(stream, maxDepth); decodedClaimant.setDiscriminant(discriminant); switch (decodedClaimant.getDiscriminant()) { case CLAIMANT_TYPE_V0: - decodedClaimant.v0 = ClaimantV0.decode(stream); + decodedClaimant.v0 = ClaimantV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClaimant; } + public static Claimant decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Claimant fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -63,6 +73,7 @@ public static Claimant fromXdrBase64(String xdr) throws IOException { public static Claimant fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -90,13 +101,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { predicate.encode(stream); } - public static ClaimantV0 decode(XdrDataInputStream stream) throws IOException { + public static ClaimantV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClaimantV0 decodedClaimantV0 = new ClaimantV0(); - decodedClaimantV0.destination = AccountID.decode(stream); - decodedClaimantV0.predicate = ClaimPredicate.decode(stream); + decodedClaimantV0.destination = AccountID.decode(stream, maxDepth); + decodedClaimantV0.predicate = ClaimPredicate.decode(stream, maxDepth); return decodedClaimantV0; } + public static ClaimantV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClaimantV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -105,6 +124,7 @@ public static ClaimantV0 fromXdrBase64(String xdr) throws IOException { public static ClaimantV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClaimantType.java b/src/main/java/org/stellar/sdk/xdr/ClaimantType.java index 13b9f7f64..29576cef1 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClaimantType.java +++ b/src/main/java/org/stellar/sdk/xdr/ClaimantType.java @@ -30,7 +30,8 @@ public int getValue() { return value; } - public static ClaimantType decode(XdrDataInputStream stream) throws IOException { + public static ClaimantType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -40,6 +41,10 @@ public static ClaimantType decode(XdrDataInputStream stream) throws IOException } } + public static ClaimantType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +57,7 @@ public static ClaimantType fromXdrBase64(String xdr) throws IOException { public static ClaimantType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceOp.java b/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceOp.java index e039ef92e..17332b210 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceOp.java @@ -32,12 +32,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { balanceID.encode(stream); } - public static ClawbackClaimableBalanceOp decode(XdrDataInputStream stream) throws IOException { + public static ClawbackClaimableBalanceOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClawbackClaimableBalanceOp decodedClawbackClaimableBalanceOp = new ClawbackClaimableBalanceOp(); - decodedClawbackClaimableBalanceOp.balanceID = ClaimableBalanceID.decode(stream); + decodedClawbackClaimableBalanceOp.balanceID = ClaimableBalanceID.decode(stream, maxDepth); return decodedClawbackClaimableBalanceOp; } + public static ClawbackClaimableBalanceOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClawbackClaimableBalanceOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +55,7 @@ public static ClawbackClaimableBalanceOp fromXdrBase64(String xdr) throws IOExce public static ClawbackClaimableBalanceOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResult.java b/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResult.java index f299476a8..591cf8989 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResult.java @@ -46,12 +46,16 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClawbackClaimableBalanceResult decode(XdrDataInputStream stream) + public static ClawbackClaimableBalanceResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClawbackClaimableBalanceResult decodedClawbackClaimableBalanceResult = new ClawbackClaimableBalanceResult(); ClawbackClaimableBalanceResultCode discriminant = - ClawbackClaimableBalanceResultCode.decode(stream); + ClawbackClaimableBalanceResultCode.decode(stream, maxDepth); decodedClawbackClaimableBalanceResult.setDiscriminant(discriminant); switch (decodedClawbackClaimableBalanceResult.getDiscriminant()) { case CLAWBACK_CLAIMABLE_BALANCE_SUCCESS: @@ -60,10 +64,17 @@ public static ClawbackClaimableBalanceResult decode(XdrDataInputStream stream) case CLAWBACK_CLAIMABLE_BALANCE_NOT_ISSUER: case CLAWBACK_CLAIMABLE_BALANCE_NOT_CLAWBACK_ENABLED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClawbackClaimableBalanceResult; } + public static ClawbackClaimableBalanceResult decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClawbackClaimableBalanceResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -72,6 +83,7 @@ public static ClawbackClaimableBalanceResult fromXdrBase64(String xdr) throws IO public static ClawbackClaimableBalanceResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResultCode.java b/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResultCode.java index c589aeaa0..a55c2f78c 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ClawbackClaimableBalanceResultCode.java @@ -39,8 +39,9 @@ public int getValue() { return value; } - public static ClawbackClaimableBalanceResultCode decode(XdrDataInputStream stream) + public static ClawbackClaimableBalanceResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -56,6 +57,11 @@ public static ClawbackClaimableBalanceResultCode decode(XdrDataInputStream strea } } + public static ClawbackClaimableBalanceResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -68,6 +74,7 @@ public static ClawbackClaimableBalanceResultCode fromXdrBase64(String xdr) throw public static ClawbackClaimableBalanceResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClawbackOp.java b/src/main/java/org/stellar/sdk/xdr/ClawbackOp.java index f0547c978..29fb91b86 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClawbackOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ClawbackOp.java @@ -38,14 +38,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { amount.encode(stream); } - public static ClawbackOp decode(XdrDataInputStream stream) throws IOException { + public static ClawbackOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClawbackOp decodedClawbackOp = new ClawbackOp(); - decodedClawbackOp.asset = Asset.decode(stream); - decodedClawbackOp.from = MuxedAccount.decode(stream); - decodedClawbackOp.amount = Int64.decode(stream); + decodedClawbackOp.asset = Asset.decode(stream, maxDepth); + decodedClawbackOp.from = MuxedAccount.decode(stream, maxDepth); + decodedClawbackOp.amount = Int64.decode(stream, maxDepth); return decodedClawbackOp; } + public static ClawbackOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClawbackOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +62,7 @@ public static ClawbackOp fromXdrBase64(String xdr) throws IOException { public static ClawbackOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClawbackResult.java b/src/main/java/org/stellar/sdk/xdr/ClawbackResult.java index e1ee5d394..e4b3ad839 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClawbackResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ClawbackResult.java @@ -47,9 +47,13 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ClawbackResult decode(XdrDataInputStream stream) throws IOException { + public static ClawbackResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ClawbackResult decodedClawbackResult = new ClawbackResult(); - ClawbackResultCode discriminant = ClawbackResultCode.decode(stream); + ClawbackResultCode discriminant = ClawbackResultCode.decode(stream, maxDepth); decodedClawbackResult.setDiscriminant(discriminant); switch (decodedClawbackResult.getDiscriminant()) { case CLAWBACK_SUCCESS: @@ -59,10 +63,16 @@ public static ClawbackResult decode(XdrDataInputStream stream) throws IOExceptio case CLAWBACK_NO_TRUST: case CLAWBACK_UNDERFUNDED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedClawbackResult; } + public static ClawbackResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ClawbackResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -71,6 +81,7 @@ public static ClawbackResult fromXdrBase64(String xdr) throws IOException { public static ClawbackResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ClawbackResultCode.java b/src/main/java/org/stellar/sdk/xdr/ClawbackResultCode.java index c483ea33d..a289ac02d 100644 --- a/src/main/java/org/stellar/sdk/xdr/ClawbackResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ClawbackResultCode.java @@ -41,7 +41,9 @@ public int getValue() { return value; } - public static ClawbackResultCode decode(XdrDataInputStream stream) throws IOException { + public static ClawbackResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -59,6 +61,10 @@ public static ClawbackResultCode decode(XdrDataInputStream stream) throws IOExce } } + public static ClawbackResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -71,6 +77,7 @@ public static ClawbackResultCode fromXdrBase64(String xdr) throws IOException { public static ClawbackResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractBandwidthV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractBandwidthV0.java index e2d8da671..01f23c7a3 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractBandwidthV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractBandwidthV0.java @@ -42,16 +42,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { feeTxSize1KB.encode(stream); } - public static ConfigSettingContractBandwidthV0 decode(XdrDataInputStream stream) + public static ConfigSettingContractBandwidthV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractBandwidthV0 decodedConfigSettingContractBandwidthV0 = new ConfigSettingContractBandwidthV0(); - decodedConfigSettingContractBandwidthV0.ledgerMaxTxsSizeBytes = Uint32.decode(stream); - decodedConfigSettingContractBandwidthV0.txMaxSizeBytes = Uint32.decode(stream); - decodedConfigSettingContractBandwidthV0.feeTxSize1KB = Int64.decode(stream); + decodedConfigSettingContractBandwidthV0.ledgerMaxTxsSizeBytes = Uint32.decode(stream, maxDepth); + decodedConfigSettingContractBandwidthV0.txMaxSizeBytes = Uint32.decode(stream, maxDepth); + decodedConfigSettingContractBandwidthV0.feeTxSize1KB = Int64.decode(stream, maxDepth); return decodedConfigSettingContractBandwidthV0; } + public static ConfigSettingContractBandwidthV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractBandwidthV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +69,7 @@ public static ConfigSettingContractBandwidthV0 fromXdrBase64(String xdr) throws public static ConfigSettingContractBandwidthV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractComputeV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractComputeV0.java index fce4795c8..50048719e 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractComputeV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractComputeV0.java @@ -47,17 +47,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { txMemoryLimit.encode(stream); } - public static ConfigSettingContractComputeV0 decode(XdrDataInputStream stream) + public static ConfigSettingContractComputeV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractComputeV0 decodedConfigSettingContractComputeV0 = new ConfigSettingContractComputeV0(); - decodedConfigSettingContractComputeV0.ledgerMaxInstructions = Int64.decode(stream); - decodedConfigSettingContractComputeV0.txMaxInstructions = Int64.decode(stream); - decodedConfigSettingContractComputeV0.feeRatePerInstructionsIncrement = Int64.decode(stream); - decodedConfigSettingContractComputeV0.txMemoryLimit = Uint32.decode(stream); + decodedConfigSettingContractComputeV0.ledgerMaxInstructions = Int64.decode(stream, maxDepth); + decodedConfigSettingContractComputeV0.txMaxInstructions = Int64.decode(stream, maxDepth); + decodedConfigSettingContractComputeV0.feeRatePerInstructionsIncrement = + Int64.decode(stream, maxDepth); + decodedConfigSettingContractComputeV0.txMemoryLimit = Uint32.decode(stream, maxDepth); return decodedConfigSettingContractComputeV0; } + public static ConfigSettingContractComputeV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractComputeV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +76,7 @@ public static ConfigSettingContractComputeV0 fromXdrBase64(String xdr) throws IO public static ConfigSettingContractComputeV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractEventsV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractEventsV0.java index ed1a1b20b..69c5ee0c3 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractEventsV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractEventsV0.java @@ -37,14 +37,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { feeContractEvents1KB.encode(stream); } - public static ConfigSettingContractEventsV0 decode(XdrDataInputStream stream) throws IOException { + public static ConfigSettingContractEventsV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractEventsV0 decodedConfigSettingContractEventsV0 = new ConfigSettingContractEventsV0(); - decodedConfigSettingContractEventsV0.txMaxContractEventsSizeBytes = Uint32.decode(stream); - decodedConfigSettingContractEventsV0.feeContractEvents1KB = Int64.decode(stream); + decodedConfigSettingContractEventsV0.txMaxContractEventsSizeBytes = + Uint32.decode(stream, maxDepth); + decodedConfigSettingContractEventsV0.feeContractEvents1KB = Int64.decode(stream, maxDepth); return decodedConfigSettingContractEventsV0; } + public static ConfigSettingContractEventsV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractEventsV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -53,6 +63,7 @@ public static ConfigSettingContractEventsV0 fromXdrBase64(String xdr) throws IOE public static ConfigSettingContractEventsV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractExecutionLanesV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractExecutionLanesV0.java index 49da207f6..02f068fd2 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractExecutionLanesV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractExecutionLanesV0.java @@ -33,14 +33,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { ledgerMaxTxCount.encode(stream); } - public static ConfigSettingContractExecutionLanesV0 decode(XdrDataInputStream stream) - throws IOException { + public static ConfigSettingContractExecutionLanesV0 decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractExecutionLanesV0 decodedConfigSettingContractExecutionLanesV0 = new ConfigSettingContractExecutionLanesV0(); - decodedConfigSettingContractExecutionLanesV0.ledgerMaxTxCount = Uint32.decode(stream); + decodedConfigSettingContractExecutionLanesV0.ledgerMaxTxCount = Uint32.decode(stream, maxDepth); return decodedConfigSettingContractExecutionLanesV0; } + public static ConfigSettingContractExecutionLanesV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractExecutionLanesV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static ConfigSettingContractExecutionLanesV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractHistoricalDataV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractHistoricalDataV0.java index 1ece62411..6f9502e1b 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractHistoricalDataV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractHistoricalDataV0.java @@ -32,14 +32,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { feeHistorical1KB.encode(stream); } - public static ConfigSettingContractHistoricalDataV0 decode(XdrDataInputStream stream) - throws IOException { + public static ConfigSettingContractHistoricalDataV0 decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractHistoricalDataV0 decodedConfigSettingContractHistoricalDataV0 = new ConfigSettingContractHistoricalDataV0(); - decodedConfigSettingContractHistoricalDataV0.feeHistorical1KB = Int64.decode(stream); + decodedConfigSettingContractHistoricalDataV0.feeHistorical1KB = Int64.decode(stream, maxDepth); return decodedConfigSettingContractHistoricalDataV0; } + public static ConfigSettingContractHistoricalDataV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractHistoricalDataV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +58,7 @@ public static ConfigSettingContractHistoricalDataV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostExtV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostExtV0.java index d191caa00..ffd9b2872 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostExtV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostExtV0.java @@ -39,15 +39,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { feeWrite1KB.encode(stream); } - public static ConfigSettingContractLedgerCostExtV0 decode(XdrDataInputStream stream) + public static ConfigSettingContractLedgerCostExtV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractLedgerCostExtV0 decodedConfigSettingContractLedgerCostExtV0 = new ConfigSettingContractLedgerCostExtV0(); - decodedConfigSettingContractLedgerCostExtV0.txMaxFootprintEntries = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostExtV0.feeWrite1KB = Int64.decode(stream); + decodedConfigSettingContractLedgerCostExtV0.txMaxFootprintEntries = + Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostExtV0.feeWrite1KB = Int64.decode(stream, maxDepth); return decodedConfigSettingContractLedgerCostExtV0; } + public static ConfigSettingContractLedgerCostExtV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractLedgerCostExtV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -57,6 +67,7 @@ public static ConfigSettingContractLedgerCostExtV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostV0.java index a46e9ef99..7c3a688f0 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractLedgerCostV0.java @@ -91,29 +91,46 @@ public void encode(XdrDataOutputStream stream) throws IOException { sorobanStateRentFeeGrowthFactor.encode(stream); } - public static ConfigSettingContractLedgerCostV0 decode(XdrDataInputStream stream) + public static ConfigSettingContractLedgerCostV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractLedgerCostV0 decodedConfigSettingContractLedgerCostV0 = new ConfigSettingContractLedgerCostV0(); - decodedConfigSettingContractLedgerCostV0.ledgerMaxDiskReadEntries = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.ledgerMaxDiskReadBytes = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.ledgerMaxWriteLedgerEntries = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.ledgerMaxWriteBytes = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.txMaxDiskReadEntries = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.txMaxDiskReadBytes = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.txMaxWriteLedgerEntries = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.txMaxWriteBytes = Uint32.decode(stream); - decodedConfigSettingContractLedgerCostV0.feeDiskReadLedgerEntry = Int64.decode(stream); - decodedConfigSettingContractLedgerCostV0.feeWriteLedgerEntry = Int64.decode(stream); - decodedConfigSettingContractLedgerCostV0.feeDiskRead1KB = Int64.decode(stream); - decodedConfigSettingContractLedgerCostV0.sorobanStateTargetSizeBytes = Int64.decode(stream); - decodedConfigSettingContractLedgerCostV0.rentFee1KBSorobanStateSizeLow = Int64.decode(stream); - decodedConfigSettingContractLedgerCostV0.rentFee1KBSorobanStateSizeHigh = Int64.decode(stream); + decodedConfigSettingContractLedgerCostV0.ledgerMaxDiskReadEntries = + Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.ledgerMaxDiskReadBytes = + Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.ledgerMaxWriteLedgerEntries = + Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.ledgerMaxWriteBytes = Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.txMaxDiskReadEntries = Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.txMaxDiskReadBytes = Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.txMaxWriteLedgerEntries = + Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.txMaxWriteBytes = Uint32.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.feeDiskReadLedgerEntry = + Int64.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.feeWriteLedgerEntry = Int64.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.feeDiskRead1KB = Int64.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.sorobanStateTargetSizeBytes = + Int64.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.rentFee1KBSorobanStateSizeLow = + Int64.decode(stream, maxDepth); + decodedConfigSettingContractLedgerCostV0.rentFee1KBSorobanStateSizeHigh = + Int64.decode(stream, maxDepth); decodedConfigSettingContractLedgerCostV0.sorobanStateRentFeeGrowthFactor = - Uint32.decode(stream); + Uint32.decode(stream, maxDepth); return decodedConfigSettingContractLedgerCostV0; } + public static ConfigSettingContractLedgerCostV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractLedgerCostV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -122,6 +139,7 @@ public static ConfigSettingContractLedgerCostV0 fromXdrBase64(String xdr) throws public static ConfigSettingContractLedgerCostV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractParallelComputeV0.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractParallelComputeV0.java index 3e2876bd3..30e66f29f 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractParallelComputeV0.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingContractParallelComputeV0.java @@ -36,15 +36,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { ledgerMaxDependentTxClusters.encode(stream); } - public static ConfigSettingContractParallelComputeV0 decode(XdrDataInputStream stream) - throws IOException { + public static ConfigSettingContractParallelComputeV0 decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingContractParallelComputeV0 decodedConfigSettingContractParallelComputeV0 = new ConfigSettingContractParallelComputeV0(); decodedConfigSettingContractParallelComputeV0.ledgerMaxDependentTxClusters = - Uint32.decode(stream); + Uint32.decode(stream, maxDepth); return decodedConfigSettingContractParallelComputeV0; } + public static ConfigSettingContractParallelComputeV0 decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingContractParallelComputeV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -55,6 +64,7 @@ public static ConfigSettingContractParallelComputeV0 fromXdrByteArray(byte[] xdr throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingEntry.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingEntry.java index 975060a50..901884c35 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingEntry.java @@ -139,77 +139,107 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ConfigSettingEntry decode(XdrDataInputStream stream) throws IOException { + public static ConfigSettingEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingEntry decodedConfigSettingEntry = new ConfigSettingEntry(); - ConfigSettingID discriminant = ConfigSettingID.decode(stream); + ConfigSettingID discriminant = ConfigSettingID.decode(stream, maxDepth); decodedConfigSettingEntry.setDiscriminant(discriminant); switch (decodedConfigSettingEntry.getDiscriminant()) { case CONFIG_SETTING_CONTRACT_MAX_SIZE_BYTES: - decodedConfigSettingEntry.contractMaxSizeBytes = Uint32.decode(stream); + decodedConfigSettingEntry.contractMaxSizeBytes = Uint32.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_COMPUTE_V0: - decodedConfigSettingEntry.contractCompute = ConfigSettingContractComputeV0.decode(stream); + decodedConfigSettingEntry.contractCompute = + ConfigSettingContractComputeV0.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_LEDGER_COST_V0: decodedConfigSettingEntry.contractLedgerCost = - ConfigSettingContractLedgerCostV0.decode(stream); + ConfigSettingContractLedgerCostV0.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_HISTORICAL_DATA_V0: decodedConfigSettingEntry.contractHistoricalData = - ConfigSettingContractHistoricalDataV0.decode(stream); + ConfigSettingContractHistoricalDataV0.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_EVENTS_V0: - decodedConfigSettingEntry.contractEvents = ConfigSettingContractEventsV0.decode(stream); + decodedConfigSettingEntry.contractEvents = + ConfigSettingContractEventsV0.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_BANDWIDTH_V0: decodedConfigSettingEntry.contractBandwidth = - ConfigSettingContractBandwidthV0.decode(stream); + ConfigSettingContractBandwidthV0.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_COST_PARAMS_CPU_INSTRUCTIONS: - decodedConfigSettingEntry.contractCostParamsCpuInsns = ContractCostParams.decode(stream); + decodedConfigSettingEntry.contractCostParamsCpuInsns = + ContractCostParams.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_COST_PARAMS_MEMORY_BYTES: - decodedConfigSettingEntry.contractCostParamsMemBytes = ContractCostParams.decode(stream); + decodedConfigSettingEntry.contractCostParamsMemBytes = + ContractCostParams.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_DATA_KEY_SIZE_BYTES: - decodedConfigSettingEntry.contractDataKeySizeBytes = Uint32.decode(stream); + decodedConfigSettingEntry.contractDataKeySizeBytes = Uint32.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_DATA_ENTRY_SIZE_BYTES: - decodedConfigSettingEntry.contractDataEntrySizeBytes = Uint32.decode(stream); + decodedConfigSettingEntry.contractDataEntrySizeBytes = Uint32.decode(stream, maxDepth); break; case CONFIG_SETTING_STATE_ARCHIVAL: - decodedConfigSettingEntry.stateArchivalSettings = StateArchivalSettings.decode(stream); + decodedConfigSettingEntry.stateArchivalSettings = + StateArchivalSettings.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_EXECUTION_LANES: decodedConfigSettingEntry.contractExecutionLanes = - ConfigSettingContractExecutionLanesV0.decode(stream); + ConfigSettingContractExecutionLanesV0.decode(stream, maxDepth); break; case CONFIG_SETTING_LIVE_SOROBAN_STATE_SIZE_WINDOW: int liveSorobanStateSizeWindowSize = stream.readInt(); + if (liveSorobanStateSizeWindowSize < 0) { + throw new IOException( + "liveSorobanStateSizeWindow size " + liveSorobanStateSizeWindowSize + " is negative"); + } + int liveSorobanStateSizeWindowRemainingInputLen = stream.getRemainingInputLen(); + if (liveSorobanStateSizeWindowRemainingInputLen >= 0 + && liveSorobanStateSizeWindowRemainingInputLen < liveSorobanStateSizeWindowSize) { + throw new IOException( + "liveSorobanStateSizeWindow size " + + liveSorobanStateSizeWindowSize + + " exceeds remaining input length " + + liveSorobanStateSizeWindowRemainingInputLen); + } decodedConfigSettingEntry.liveSorobanStateSizeWindow = new Uint64[liveSorobanStateSizeWindowSize]; for (int i = 0; i < liveSorobanStateSizeWindowSize; i++) { - decodedConfigSettingEntry.liveSorobanStateSizeWindow[i] = Uint64.decode(stream); + decodedConfigSettingEntry.liveSorobanStateSizeWindow[i] = Uint64.decode(stream, maxDepth); } break; case CONFIG_SETTING_EVICTION_ITERATOR: - decodedConfigSettingEntry.evictionIterator = EvictionIterator.decode(stream); + decodedConfigSettingEntry.evictionIterator = EvictionIterator.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_PARALLEL_COMPUTE_V0: decodedConfigSettingEntry.contractParallelCompute = - ConfigSettingContractParallelComputeV0.decode(stream); + ConfigSettingContractParallelComputeV0.decode(stream, maxDepth); break; case CONFIG_SETTING_CONTRACT_LEDGER_COST_EXT_V0: decodedConfigSettingEntry.contractLedgerCostExt = - ConfigSettingContractLedgerCostExtV0.decode(stream); + ConfigSettingContractLedgerCostExtV0.decode(stream, maxDepth); break; case CONFIG_SETTING_SCP_TIMING: - decodedConfigSettingEntry.contractSCPTiming = ConfigSettingSCPTiming.decode(stream); + decodedConfigSettingEntry.contractSCPTiming = + ConfigSettingSCPTiming.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedConfigSettingEntry; } + public static ConfigSettingEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -218,6 +248,7 @@ public static ConfigSettingEntry fromXdrBase64(String xdr) throws IOException { public static ConfigSettingEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingID.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingID.java index f599abac6..d974f9a08 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingID.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingID.java @@ -62,7 +62,8 @@ public int getValue() { return value; } - public static ConfigSettingID decode(XdrDataInputStream stream) throws IOException { + public static ConfigSettingID decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -104,6 +105,10 @@ public static ConfigSettingID decode(XdrDataInputStream stream) throws IOExcepti } } + public static ConfigSettingID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -116,6 +121,7 @@ public static ConfigSettingID fromXdrBase64(String xdr) throws IOException { public static ConfigSettingID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigSettingSCPTiming.java b/src/main/java/org/stellar/sdk/xdr/ConfigSettingSCPTiming.java index b0e2ea3b4..f09207ba3 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigSettingSCPTiming.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigSettingSCPTiming.java @@ -43,16 +43,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { ballotTimeoutIncrementMilliseconds.encode(stream); } - public static ConfigSettingSCPTiming decode(XdrDataInputStream stream) throws IOException { + public static ConfigSettingSCPTiming decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigSettingSCPTiming decodedConfigSettingSCPTiming = new ConfigSettingSCPTiming(); - decodedConfigSettingSCPTiming.ledgerTargetCloseTimeMilliseconds = Uint32.decode(stream); - decodedConfigSettingSCPTiming.nominationTimeoutInitialMilliseconds = Uint32.decode(stream); - decodedConfigSettingSCPTiming.nominationTimeoutIncrementMilliseconds = Uint32.decode(stream); - decodedConfigSettingSCPTiming.ballotTimeoutInitialMilliseconds = Uint32.decode(stream); - decodedConfigSettingSCPTiming.ballotTimeoutIncrementMilliseconds = Uint32.decode(stream); + decodedConfigSettingSCPTiming.ledgerTargetCloseTimeMilliseconds = + Uint32.decode(stream, maxDepth); + decodedConfigSettingSCPTiming.nominationTimeoutInitialMilliseconds = + Uint32.decode(stream, maxDepth); + decodedConfigSettingSCPTiming.nominationTimeoutIncrementMilliseconds = + Uint32.decode(stream, maxDepth); + decodedConfigSettingSCPTiming.ballotTimeoutInitialMilliseconds = + Uint32.decode(stream, maxDepth); + decodedConfigSettingSCPTiming.ballotTimeoutIncrementMilliseconds = + Uint32.decode(stream, maxDepth); return decodedConfigSettingSCPTiming; } + public static ConfigSettingSCPTiming decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigSettingSCPTiming fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -61,6 +75,7 @@ public static ConfigSettingSCPTiming fromXdrBase64(String xdr) throws IOExceptio public static ConfigSettingSCPTiming fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSet.java b/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSet.java index 965aa1a59..af57a4c6c 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSet.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSet.java @@ -35,16 +35,36 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ConfigUpgradeSet decode(XdrDataInputStream stream) throws IOException { + public static ConfigUpgradeSet decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigUpgradeSet decodedConfigUpgradeSet = new ConfigUpgradeSet(); int updatedEntrySize = stream.readInt(); + if (updatedEntrySize < 0) { + throw new IOException("updatedEntry size " + updatedEntrySize + " is negative"); + } + int updatedEntryRemainingInputLen = stream.getRemainingInputLen(); + if (updatedEntryRemainingInputLen >= 0 && updatedEntryRemainingInputLen < updatedEntrySize) { + throw new IOException( + "updatedEntry size " + + updatedEntrySize + + " exceeds remaining input length " + + updatedEntryRemainingInputLen); + } decodedConfigUpgradeSet.updatedEntry = new ConfigSettingEntry[updatedEntrySize]; for (int i = 0; i < updatedEntrySize; i++) { - decodedConfigUpgradeSet.updatedEntry[i] = ConfigSettingEntry.decode(stream); + decodedConfigUpgradeSet.updatedEntry[i] = ConfigSettingEntry.decode(stream, maxDepth); } return decodedConfigUpgradeSet; } + public static ConfigUpgradeSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigUpgradeSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -53,6 +73,7 @@ public static ConfigUpgradeSet fromXdrBase64(String xdr) throws IOException { public static ConfigUpgradeSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSetKey.java b/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSetKey.java index 58d2e3805..e7505a0f6 100644 --- a/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSetKey.java +++ b/src/main/java/org/stellar/sdk/xdr/ConfigUpgradeSetKey.java @@ -34,13 +34,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { contentHash.encode(stream); } - public static ConfigUpgradeSetKey decode(XdrDataInputStream stream) throws IOException { + public static ConfigUpgradeSetKey decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ConfigUpgradeSetKey decodedConfigUpgradeSetKey = new ConfigUpgradeSetKey(); - decodedConfigUpgradeSetKey.contractID = ContractID.decode(stream); - decodedConfigUpgradeSetKey.contentHash = Hash.decode(stream); + decodedConfigUpgradeSetKey.contractID = ContractID.decode(stream, maxDepth); + decodedConfigUpgradeSetKey.contentHash = Hash.decode(stream, maxDepth); return decodedConfigUpgradeSetKey; } + public static ConfigUpgradeSetKey decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ConfigUpgradeSetKey fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +58,7 @@ public static ConfigUpgradeSetKey fromXdrBase64(String xdr) throws IOException { public static ConfigUpgradeSetKey fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractCodeCostInputs.java b/src/main/java/org/stellar/sdk/xdr/ContractCodeCostInputs.java index e2c7c7f00..a2e1fe230 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractCodeCostInputs.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractCodeCostInputs.java @@ -61,22 +61,31 @@ public void encode(XdrDataOutputStream stream) throws IOException { nDataSegmentBytes.encode(stream); } - public static ContractCodeCostInputs decode(XdrDataInputStream stream) throws IOException { + public static ContractCodeCostInputs decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractCodeCostInputs decodedContractCodeCostInputs = new ContractCodeCostInputs(); - decodedContractCodeCostInputs.ext = ExtensionPoint.decode(stream); - decodedContractCodeCostInputs.nInstructions = Uint32.decode(stream); - decodedContractCodeCostInputs.nFunctions = Uint32.decode(stream); - decodedContractCodeCostInputs.nGlobals = Uint32.decode(stream); - decodedContractCodeCostInputs.nTableEntries = Uint32.decode(stream); - decodedContractCodeCostInputs.nTypes = Uint32.decode(stream); - decodedContractCodeCostInputs.nDataSegments = Uint32.decode(stream); - decodedContractCodeCostInputs.nElemSegments = Uint32.decode(stream); - decodedContractCodeCostInputs.nImports = Uint32.decode(stream); - decodedContractCodeCostInputs.nExports = Uint32.decode(stream); - decodedContractCodeCostInputs.nDataSegmentBytes = Uint32.decode(stream); + decodedContractCodeCostInputs.ext = ExtensionPoint.decode(stream, maxDepth); + decodedContractCodeCostInputs.nInstructions = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nFunctions = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nGlobals = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nTableEntries = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nTypes = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nDataSegments = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nElemSegments = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nImports = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nExports = Uint32.decode(stream, maxDepth); + decodedContractCodeCostInputs.nDataSegmentBytes = Uint32.decode(stream, maxDepth); return decodedContractCodeCostInputs; } + public static ContractCodeCostInputs decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractCodeCostInputs fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -85,6 +94,7 @@ public static ContractCodeCostInputs fromXdrBase64(String xdr) throws IOExceptio public static ContractCodeCostInputs fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractCodeEntry.java b/src/main/java/org/stellar/sdk/xdr/ContractCodeEntry.java index 75a8efd14..a8f5b077c 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractCodeEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractCodeEntry.java @@ -50,16 +50,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(getCode(), 0, codeSize); } - public static ContractCodeEntry decode(XdrDataInputStream stream) throws IOException { + public static ContractCodeEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractCodeEntry decodedContractCodeEntry = new ContractCodeEntry(); - decodedContractCodeEntry.ext = ContractCodeEntryExt.decode(stream); - decodedContractCodeEntry.hash = Hash.decode(stream); + decodedContractCodeEntry.ext = ContractCodeEntryExt.decode(stream, maxDepth); + decodedContractCodeEntry.hash = Hash.decode(stream, maxDepth); int codeSize = stream.readInt(); + if (codeSize < 0) { + throw new IOException("code size " + codeSize + " is negative"); + } + int codeRemainingInputLen = stream.getRemainingInputLen(); + if (codeRemainingInputLen >= 0 && codeRemainingInputLen < codeSize) { + throw new IOException( + "code size " + codeSize + " exceeds remaining input length " + codeRemainingInputLen); + } decodedContractCodeEntry.code = new byte[codeSize]; - stream.read(decodedContractCodeEntry.code, 0, codeSize); + stream.readPaddedData(decodedContractCodeEntry.code, 0, codeSize); return decodedContractCodeEntry; } + public static ContractCodeEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractCodeEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +85,7 @@ public static ContractCodeEntry fromXdrBase64(String xdr) throws IOException { public static ContractCodeEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -107,7 +125,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ContractCodeEntryExt decode(XdrDataInputStream stream) throws IOException { + public static ContractCodeEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractCodeEntryExt decodedContractCodeEntryExt = new ContractCodeEntryExt(); Integer discriminant = stream.readInt(); decodedContractCodeEntryExt.setDiscriminant(discriminant); @@ -115,12 +138,18 @@ public static ContractCodeEntryExt decode(XdrDataInputStream stream) throws IOEx case 0: break; case 1: - decodedContractCodeEntryExt.v1 = ContractCodeEntryV1.decode(stream); + decodedContractCodeEntryExt.v1 = ContractCodeEntryV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedContractCodeEntryExt; } + public static ContractCodeEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractCodeEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -129,6 +158,7 @@ public static ContractCodeEntryExt fromXdrBase64(String xdr) throws IOException public static ContractCodeEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -156,13 +186,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { costInputs.encode(stream); } - public static ContractCodeEntryV1 decode(XdrDataInputStream stream) throws IOException { + public static ContractCodeEntryV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractCodeEntryV1 decodedContractCodeEntryV1 = new ContractCodeEntryV1(); - decodedContractCodeEntryV1.ext = ExtensionPoint.decode(stream); - decodedContractCodeEntryV1.costInputs = ContractCodeCostInputs.decode(stream); + decodedContractCodeEntryV1.ext = ExtensionPoint.decode(stream, maxDepth); + decodedContractCodeEntryV1.costInputs = ContractCodeCostInputs.decode(stream, maxDepth); return decodedContractCodeEntryV1; } + public static ContractCodeEntryV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractCodeEntryV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -171,6 +210,7 @@ public static ContractCodeEntryV1 fromXdrBase64(String xdr) throws IOException { public static ContractCodeEntryV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractCostParamEntry.java b/src/main/java/org/stellar/sdk/xdr/ContractCostParamEntry.java index 5a2007bd7..97bcfe053 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractCostParamEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractCostParamEntry.java @@ -39,14 +39,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { linearTerm.encode(stream); } - public static ContractCostParamEntry decode(XdrDataInputStream stream) throws IOException { + public static ContractCostParamEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractCostParamEntry decodedContractCostParamEntry = new ContractCostParamEntry(); - decodedContractCostParamEntry.ext = ExtensionPoint.decode(stream); - decodedContractCostParamEntry.constTerm = Int64.decode(stream); - decodedContractCostParamEntry.linearTerm = Int64.decode(stream); + decodedContractCostParamEntry.ext = ExtensionPoint.decode(stream, maxDepth); + decodedContractCostParamEntry.constTerm = Int64.decode(stream, maxDepth); + decodedContractCostParamEntry.linearTerm = Int64.decode(stream, maxDepth); return decodedContractCostParamEntry; } + public static ContractCostParamEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractCostParamEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -55,6 +64,7 @@ public static ContractCostParamEntry fromXdrBase64(String xdr) throws IOExceptio public static ContractCostParamEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractCostParams.java b/src/main/java/org/stellar/sdk/xdr/ContractCostParams.java index 308047c91..91b29110f 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractCostParams.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractCostParams.java @@ -25,23 +25,53 @@ public class ContractCostParams implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int ContractCostParamsSize = getContractCostParams().length; + if (ContractCostParamsSize > 1024) { + throw new IOException( + "ContractCostParams size " + ContractCostParamsSize + " exceeds max size 1024"); + } stream.writeInt(ContractCostParamsSize); for (int i = 0; i < ContractCostParamsSize; i++) { ContractCostParams[i].encode(stream); } } - public static ContractCostParams decode(XdrDataInputStream stream) throws IOException { + public static ContractCostParams decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractCostParams decodedContractCostParams = new ContractCostParams(); int ContractCostParamsSize = stream.readInt(); + if (ContractCostParamsSize < 0) { + throw new IOException("ContractCostParams size " + ContractCostParamsSize + " is negative"); + } + if (ContractCostParamsSize > 1024) { + throw new IOException( + "ContractCostParams size " + ContractCostParamsSize + " exceeds max size 1024"); + } + int ContractCostParamsRemainingInputLen = stream.getRemainingInputLen(); + if (ContractCostParamsRemainingInputLen >= 0 + && ContractCostParamsRemainingInputLen < ContractCostParamsSize) { + throw new IOException( + "ContractCostParams size " + + ContractCostParamsSize + + " exceeds remaining input length " + + ContractCostParamsRemainingInputLen); + } decodedContractCostParams.ContractCostParams = new ContractCostParamEntry[ContractCostParamsSize]; for (int i = 0; i < ContractCostParamsSize; i++) { - decodedContractCostParams.ContractCostParams[i] = ContractCostParamEntry.decode(stream); + decodedContractCostParams.ContractCostParams[i] = + ContractCostParamEntry.decode(stream, maxDepth); } return decodedContractCostParams; } + public static ContractCostParams decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractCostParams fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +80,7 @@ public static ContractCostParams fromXdrBase64(String xdr) throws IOException { public static ContractCostParams fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractCostType.java b/src/main/java/org/stellar/sdk/xdr/ContractCostType.java index 139d9ddfc..d3d906464 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractCostType.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractCostType.java @@ -247,7 +247,9 @@ public int getValue() { return value; } - public static ContractCostType decode(XdrDataInputStream stream) throws IOException { + public static ContractCostType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -395,6 +397,10 @@ public static ContractCostType decode(XdrDataInputStream stream) throws IOExcept } } + public static ContractCostType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -407,6 +413,7 @@ public static ContractCostType fromXdrBase64(String xdr) throws IOException { public static ContractCostType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractDataDurability.java b/src/main/java/org/stellar/sdk/xdr/ContractDataDurability.java index 24d48ba0f..e7a3ad0ac 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractDataDurability.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractDataDurability.java @@ -31,7 +31,9 @@ public int getValue() { return value; } - public static ContractDataDurability decode(XdrDataInputStream stream) throws IOException { + public static ContractDataDurability decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -43,6 +45,10 @@ public static ContractDataDurability decode(XdrDataInputStream stream) throws IO } } + public static ContractDataDurability decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -55,6 +61,7 @@ public static ContractDataDurability fromXdrBase64(String xdr) throws IOExceptio public static ContractDataDurability fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractDataEntry.java b/src/main/java/org/stellar/sdk/xdr/ContractDataEntry.java index bab99088b..d9f68bcf2 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractDataEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractDataEntry.java @@ -44,16 +44,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { val.encode(stream); } - public static ContractDataEntry decode(XdrDataInputStream stream) throws IOException { + public static ContractDataEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractDataEntry decodedContractDataEntry = new ContractDataEntry(); - decodedContractDataEntry.ext = ExtensionPoint.decode(stream); - decodedContractDataEntry.contract = SCAddress.decode(stream); - decodedContractDataEntry.key = SCVal.decode(stream); - decodedContractDataEntry.durability = ContractDataDurability.decode(stream); - decodedContractDataEntry.val = SCVal.decode(stream); + decodedContractDataEntry.ext = ExtensionPoint.decode(stream, maxDepth); + decodedContractDataEntry.contract = SCAddress.decode(stream, maxDepth); + decodedContractDataEntry.key = SCVal.decode(stream, maxDepth); + decodedContractDataEntry.durability = ContractDataDurability.decode(stream, maxDepth); + decodedContractDataEntry.val = SCVal.decode(stream, maxDepth); return decodedContractDataEntry; } + public static ContractDataEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractDataEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +71,7 @@ public static ContractDataEntry fromXdrBase64(String xdr) throws IOException { public static ContractDataEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractEvent.java b/src/main/java/org/stellar/sdk/xdr/ContractEvent.java index 81d3921dd..2cb59f460 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractEvent.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractEvent.java @@ -59,18 +59,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { body.encode(stream); } - public static ContractEvent decode(XdrDataInputStream stream) throws IOException { + public static ContractEvent decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractEvent decodedContractEvent = new ContractEvent(); - decodedContractEvent.ext = ExtensionPoint.decode(stream); - int contractIDPresent = stream.readInt(); - if (contractIDPresent != 0) { - decodedContractEvent.contractID = ContractID.decode(stream); + decodedContractEvent.ext = ExtensionPoint.decode(stream, maxDepth); + boolean contractIDPresent = stream.readXdrBoolean(); + if (contractIDPresent) { + decodedContractEvent.contractID = ContractID.decode(stream, maxDepth); } - decodedContractEvent.type = ContractEventType.decode(stream); - decodedContractEvent.body = ContractEventBody.decode(stream); + decodedContractEvent.type = ContractEventType.decode(stream, maxDepth); + decodedContractEvent.body = ContractEventBody.decode(stream, maxDepth); return decodedContractEvent; } + public static ContractEvent decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractEvent fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -79,6 +87,7 @@ public static ContractEvent fromXdrBase64(String xdr) throws IOException { public static ContractEvent fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -114,18 +123,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ContractEventBody decode(XdrDataInputStream stream) throws IOException { + public static ContractEventBody decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractEventBody decodedContractEventBody = new ContractEventBody(); Integer discriminant = stream.readInt(); decodedContractEventBody.setDiscriminant(discriminant); switch (decodedContractEventBody.getDiscriminant()) { case 0: - decodedContractEventBody.v0 = ContractEventV0.decode(stream); + decodedContractEventBody.v0 = ContractEventV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedContractEventBody; } + public static ContractEventBody decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractEventBody fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -134,6 +154,7 @@ public static ContractEventBody fromXdrBase64(String xdr) throws IOException { public static ContractEventBody fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -165,17 +186,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { data.encode(stream); } - public static ContractEventV0 decode(XdrDataInputStream stream) throws IOException { + public static ContractEventV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractEventV0 decodedContractEventV0 = new ContractEventV0(); int topicsSize = stream.readInt(); + if (topicsSize < 0) { + throw new IOException("topics size " + topicsSize + " is negative"); + } + int topicsRemainingInputLen = stream.getRemainingInputLen(); + if (topicsRemainingInputLen >= 0 && topicsRemainingInputLen < topicsSize) { + throw new IOException( + "topics size " + + topicsSize + + " exceeds remaining input length " + + topicsRemainingInputLen); + } decodedContractEventV0.topics = new SCVal[topicsSize]; for (int i = 0; i < topicsSize; i++) { - decodedContractEventV0.topics[i] = SCVal.decode(stream); + decodedContractEventV0.topics[i] = SCVal.decode(stream, maxDepth); } - decodedContractEventV0.data = SCVal.decode(stream); + decodedContractEventV0.data = SCVal.decode(stream, maxDepth); return decodedContractEventV0; } + public static ContractEventV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractEventV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -184,6 +225,7 @@ public static ContractEventV0 fromXdrBase64(String xdr) throws IOException { public static ContractEventV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractEventType.java b/src/main/java/org/stellar/sdk/xdr/ContractEventType.java index 9ee009c95..fe3593dd7 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractEventType.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractEventType.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static ContractEventType decode(XdrDataInputStream stream) throws IOException { + public static ContractEventType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +50,10 @@ public static ContractEventType decode(XdrDataInputStream stream) throws IOExcep } } + public static ContractEventType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static ContractEventType fromXdrBase64(String xdr) throws IOException { public static ContractEventType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractExecutable.java b/src/main/java/org/stellar/sdk/xdr/ContractExecutable.java index d94fe2b2c..1c1b45628 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractExecutable.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractExecutable.java @@ -43,20 +43,31 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ContractExecutable decode(XdrDataInputStream stream) throws IOException { + public static ContractExecutable decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractExecutable decodedContractExecutable = new ContractExecutable(); - ContractExecutableType discriminant = ContractExecutableType.decode(stream); + ContractExecutableType discriminant = ContractExecutableType.decode(stream, maxDepth); decodedContractExecutable.setDiscriminant(discriminant); switch (decodedContractExecutable.getDiscriminant()) { case CONTRACT_EXECUTABLE_WASM: - decodedContractExecutable.wasm_hash = Hash.decode(stream); + decodedContractExecutable.wasm_hash = Hash.decode(stream, maxDepth); break; case CONTRACT_EXECUTABLE_STELLAR_ASSET: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedContractExecutable; } + public static ContractExecutable decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractExecutable fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +76,7 @@ public static ContractExecutable fromXdrBase64(String xdr) throws IOException { public static ContractExecutable fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractExecutableType.java b/src/main/java/org/stellar/sdk/xdr/ContractExecutableType.java index 0131a9276..10d034fc9 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractExecutableType.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractExecutableType.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static ContractExecutableType decode(XdrDataInputStream stream) throws IOException { + public static ContractExecutableType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static ContractExecutableType decode(XdrDataInputStream stream) throws IO } } + public static ContractExecutableType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static ContractExecutableType fromXdrBase64(String xdr) throws IOExceptio public static ContractExecutableType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractID.java b/src/main/java/org/stellar/sdk/xdr/ContractID.java index 39b664885..d976341c0 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractID.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractID.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { ContractID.encode(stream); } - public static ContractID decode(XdrDataInputStream stream) throws IOException { + public static ContractID decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractID decodedContractID = new ContractID(); - decodedContractID.ContractID = Hash.decode(stream); + decodedContractID.ContractID = Hash.decode(stream, maxDepth); return decodedContractID; } + public static ContractID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static ContractID fromXdrBase64(String xdr) throws IOException { public static ContractID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractIDPreimage.java b/src/main/java/org/stellar/sdk/xdr/ContractIDPreimage.java index bc3d567c6..6483f451f 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractIDPreimage.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractIDPreimage.java @@ -49,21 +49,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ContractIDPreimage decode(XdrDataInputStream stream) throws IOException { + public static ContractIDPreimage decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractIDPreimage decodedContractIDPreimage = new ContractIDPreimage(); - ContractIDPreimageType discriminant = ContractIDPreimageType.decode(stream); + ContractIDPreimageType discriminant = ContractIDPreimageType.decode(stream, maxDepth); decodedContractIDPreimage.setDiscriminant(discriminant); switch (decodedContractIDPreimage.getDiscriminant()) { case CONTRACT_ID_PREIMAGE_FROM_ADDRESS: - decodedContractIDPreimage.fromAddress = ContractIDPreimageFromAddress.decode(stream); + decodedContractIDPreimage.fromAddress = + ContractIDPreimageFromAddress.decode(stream, maxDepth); break; case CONTRACT_ID_PREIMAGE_FROM_ASSET: - decodedContractIDPreimage.fromAsset = Asset.decode(stream); + decodedContractIDPreimage.fromAsset = Asset.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedContractIDPreimage; } + public static ContractIDPreimage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractIDPreimage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -72,6 +84,7 @@ public static ContractIDPreimage fromXdrBase64(String xdr) throws IOException { public static ContractIDPreimage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -99,15 +112,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { salt.encode(stream); } - public static ContractIDPreimageFromAddress decode(XdrDataInputStream stream) + public static ContractIDPreimageFromAddress decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ContractIDPreimageFromAddress decodedContractIDPreimageFromAddress = new ContractIDPreimageFromAddress(); - decodedContractIDPreimageFromAddress.address = SCAddress.decode(stream); - decodedContractIDPreimageFromAddress.salt = Uint256.decode(stream); + decodedContractIDPreimageFromAddress.address = SCAddress.decode(stream, maxDepth); + decodedContractIDPreimageFromAddress.salt = Uint256.decode(stream, maxDepth); return decodedContractIDPreimageFromAddress; } + public static ContractIDPreimageFromAddress decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ContractIDPreimageFromAddress fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -116,6 +138,7 @@ public static ContractIDPreimageFromAddress fromXdrBase64(String xdr) throws IOE public static ContractIDPreimageFromAddress fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ContractIDPreimageType.java b/src/main/java/org/stellar/sdk/xdr/ContractIDPreimageType.java index 3626ee74d..e175a566b 100644 --- a/src/main/java/org/stellar/sdk/xdr/ContractIDPreimageType.java +++ b/src/main/java/org/stellar/sdk/xdr/ContractIDPreimageType.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static ContractIDPreimageType decode(XdrDataInputStream stream) throws IOException { + public static ContractIDPreimageType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static ContractIDPreimageType decode(XdrDataInputStream stream) throws IO } } + public static ContractIDPreimageType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static ContractIDPreimageType fromXdrBase64(String xdr) throws IOExceptio public static ContractIDPreimageType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateAccountOp.java b/src/main/java/org/stellar/sdk/xdr/CreateAccountOp.java index 1c2ebd4d5..9be1571a3 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateAccountOp.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateAccountOp.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { startingBalance.encode(stream); } - public static CreateAccountOp decode(XdrDataInputStream stream) throws IOException { + public static CreateAccountOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreateAccountOp decodedCreateAccountOp = new CreateAccountOp(); - decodedCreateAccountOp.destination = AccountID.decode(stream); - decodedCreateAccountOp.startingBalance = Int64.decode(stream); + decodedCreateAccountOp.destination = AccountID.decode(stream, maxDepth); + decodedCreateAccountOp.startingBalance = Int64.decode(stream, maxDepth); return decodedCreateAccountOp; } + public static CreateAccountOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreateAccountOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static CreateAccountOp fromXdrBase64(String xdr) throws IOException { public static CreateAccountOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateAccountResult.java b/src/main/java/org/stellar/sdk/xdr/CreateAccountResult.java index 37c506d7e..adb0a4a16 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateAccountResult.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateAccountResult.java @@ -47,9 +47,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static CreateAccountResult decode(XdrDataInputStream stream) throws IOException { + public static CreateAccountResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreateAccountResult decodedCreateAccountResult = new CreateAccountResult(); - CreateAccountResultCode discriminant = CreateAccountResultCode.decode(stream); + CreateAccountResultCode discriminant = CreateAccountResultCode.decode(stream, maxDepth); decodedCreateAccountResult.setDiscriminant(discriminant); switch (decodedCreateAccountResult.getDiscriminant()) { case CREATE_ACCOUNT_SUCCESS: @@ -59,10 +64,16 @@ public static CreateAccountResult decode(XdrDataInputStream stream) throws IOExc case CREATE_ACCOUNT_LOW_RESERVE: case CREATE_ACCOUNT_ALREADY_EXIST: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedCreateAccountResult; } + public static CreateAccountResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreateAccountResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -71,6 +82,7 @@ public static CreateAccountResult fromXdrBase64(String xdr) throws IOException { public static CreateAccountResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateAccountResultCode.java b/src/main/java/org/stellar/sdk/xdr/CreateAccountResultCode.java index 345452350..25cf787b7 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateAccountResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateAccountResultCode.java @@ -42,7 +42,9 @@ public int getValue() { return value; } - public static CreateAccountResultCode decode(XdrDataInputStream stream) throws IOException { + public static CreateAccountResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -60,6 +62,10 @@ public static CreateAccountResultCode decode(XdrDataInputStream stream) throws I } } + public static CreateAccountResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -72,6 +78,7 @@ public static CreateAccountResultCode fromXdrBase64(String xdr) throws IOExcepti public static CreateAccountResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceOp.java b/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceOp.java index 45d6b4122..329a8cfea 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceOp.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceOp.java @@ -36,24 +36,50 @@ public void encode(XdrDataOutputStream stream) throws IOException { asset.encode(stream); amount.encode(stream); int claimantsSize = getClaimants().length; + if (claimantsSize > 10) { + throw new IOException("claimants size " + claimantsSize + " exceeds max size 10"); + } stream.writeInt(claimantsSize); for (int i = 0; i < claimantsSize; i++) { claimants[i].encode(stream); } } - public static CreateClaimableBalanceOp decode(XdrDataInputStream stream) throws IOException { + public static CreateClaimableBalanceOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreateClaimableBalanceOp decodedCreateClaimableBalanceOp = new CreateClaimableBalanceOp(); - decodedCreateClaimableBalanceOp.asset = Asset.decode(stream); - decodedCreateClaimableBalanceOp.amount = Int64.decode(stream); + decodedCreateClaimableBalanceOp.asset = Asset.decode(stream, maxDepth); + decodedCreateClaimableBalanceOp.amount = Int64.decode(stream, maxDepth); int claimantsSize = stream.readInt(); + if (claimantsSize < 0) { + throw new IOException("claimants size " + claimantsSize + " is negative"); + } + if (claimantsSize > 10) { + throw new IOException("claimants size " + claimantsSize + " exceeds max size 10"); + } + int claimantsRemainingInputLen = stream.getRemainingInputLen(); + if (claimantsRemainingInputLen >= 0 && claimantsRemainingInputLen < claimantsSize) { + throw new IOException( + "claimants size " + + claimantsSize + + " exceeds remaining input length " + + claimantsRemainingInputLen); + } decodedCreateClaimableBalanceOp.claimants = new Claimant[claimantsSize]; for (int i = 0; i < claimantsSize; i++) { - decodedCreateClaimableBalanceOp.claimants[i] = Claimant.decode(stream); + decodedCreateClaimableBalanceOp.claimants[i] = Claimant.decode(stream, maxDepth); } return decodedCreateClaimableBalanceOp; } + public static CreateClaimableBalanceOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreateClaimableBalanceOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +88,7 @@ public static CreateClaimableBalanceOp fromXdrBase64(String xdr) throws IOExcept public static CreateClaimableBalanceOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResult.java b/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResult.java index b1d590786..ddb723baa 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResult.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResult.java @@ -52,14 +52,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static CreateClaimableBalanceResult decode(XdrDataInputStream stream) throws IOException { + public static CreateClaimableBalanceResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreateClaimableBalanceResult decodedCreateClaimableBalanceResult = new CreateClaimableBalanceResult(); - CreateClaimableBalanceResultCode discriminant = CreateClaimableBalanceResultCode.decode(stream); + CreateClaimableBalanceResultCode discriminant = + CreateClaimableBalanceResultCode.decode(stream, maxDepth); decodedCreateClaimableBalanceResult.setDiscriminant(discriminant); switch (decodedCreateClaimableBalanceResult.getDiscriminant()) { case CREATE_CLAIMABLE_BALANCE_SUCCESS: - decodedCreateClaimableBalanceResult.balanceID = ClaimableBalanceID.decode(stream); + decodedCreateClaimableBalanceResult.balanceID = ClaimableBalanceID.decode(stream, maxDepth); break; case CREATE_CLAIMABLE_BALANCE_MALFORMED: case CREATE_CLAIMABLE_BALANCE_LOW_RESERVE: @@ -67,10 +73,16 @@ public static CreateClaimableBalanceResult decode(XdrDataInputStream stream) thr case CREATE_CLAIMABLE_BALANCE_NOT_AUTHORIZED: case CREATE_CLAIMABLE_BALANCE_UNDERFUNDED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedCreateClaimableBalanceResult; } + public static CreateClaimableBalanceResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreateClaimableBalanceResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -79,6 +91,7 @@ public static CreateClaimableBalanceResult fromXdrBase64(String xdr) throws IOEx public static CreateClaimableBalanceResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResultCode.java b/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResultCode.java index 56bf5d474..9b1f12451 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateClaimableBalanceResultCode.java @@ -40,8 +40,9 @@ public int getValue() { return value; } - public static CreateClaimableBalanceResultCode decode(XdrDataInputStream stream) + public static CreateClaimableBalanceResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -61,6 +62,11 @@ public static CreateClaimableBalanceResultCode decode(XdrDataInputStream stream) } } + public static CreateClaimableBalanceResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -73,6 +79,7 @@ public static CreateClaimableBalanceResultCode fromXdrBase64(String xdr) throws public static CreateClaimableBalanceResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateContractArgs.java b/src/main/java/org/stellar/sdk/xdr/CreateContractArgs.java index 90fe163c6..543eb91d3 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateContractArgs.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateContractArgs.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { executable.encode(stream); } - public static CreateContractArgs decode(XdrDataInputStream stream) throws IOException { + public static CreateContractArgs decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreateContractArgs decodedCreateContractArgs = new CreateContractArgs(); - decodedCreateContractArgs.contractIDPreimage = ContractIDPreimage.decode(stream); - decodedCreateContractArgs.executable = ContractExecutable.decode(stream); + decodedCreateContractArgs.contractIDPreimage = ContractIDPreimage.decode(stream, maxDepth); + decodedCreateContractArgs.executable = ContractExecutable.decode(stream, maxDepth); return decodedCreateContractArgs; } + public static CreateContractArgs decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreateContractArgs fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static CreateContractArgs fromXdrBase64(String xdr) throws IOException { public static CreateContractArgs fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreateContractArgsV2.java b/src/main/java/org/stellar/sdk/xdr/CreateContractArgsV2.java index ee080eb38..d7391be38 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreateContractArgsV2.java +++ b/src/main/java/org/stellar/sdk/xdr/CreateContractArgsV2.java @@ -43,18 +43,39 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static CreateContractArgsV2 decode(XdrDataInputStream stream) throws IOException { + public static CreateContractArgsV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreateContractArgsV2 decodedCreateContractArgsV2 = new CreateContractArgsV2(); - decodedCreateContractArgsV2.contractIDPreimage = ContractIDPreimage.decode(stream); - decodedCreateContractArgsV2.executable = ContractExecutable.decode(stream); + decodedCreateContractArgsV2.contractIDPreimage = ContractIDPreimage.decode(stream, maxDepth); + decodedCreateContractArgsV2.executable = ContractExecutable.decode(stream, maxDepth); int constructorArgsSize = stream.readInt(); + if (constructorArgsSize < 0) { + throw new IOException("constructorArgs size " + constructorArgsSize + " is negative"); + } + int constructorArgsRemainingInputLen = stream.getRemainingInputLen(); + if (constructorArgsRemainingInputLen >= 0 + && constructorArgsRemainingInputLen < constructorArgsSize) { + throw new IOException( + "constructorArgs size " + + constructorArgsSize + + " exceeds remaining input length " + + constructorArgsRemainingInputLen); + } decodedCreateContractArgsV2.constructorArgs = new SCVal[constructorArgsSize]; for (int i = 0; i < constructorArgsSize; i++) { - decodedCreateContractArgsV2.constructorArgs[i] = SCVal.decode(stream); + decodedCreateContractArgsV2.constructorArgs[i] = SCVal.decode(stream, maxDepth); } return decodedCreateContractArgsV2; } + public static CreateContractArgsV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreateContractArgsV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -63,6 +84,7 @@ public static CreateContractArgsV2 fromXdrBase64(String xdr) throws IOException public static CreateContractArgsV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CreatePassiveSellOfferOp.java b/src/main/java/org/stellar/sdk/xdr/CreatePassiveSellOfferOp.java index 69366b673..3b191330a 100644 --- a/src/main/java/org/stellar/sdk/xdr/CreatePassiveSellOfferOp.java +++ b/src/main/java/org/stellar/sdk/xdr/CreatePassiveSellOfferOp.java @@ -41,15 +41,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { price.encode(stream); } - public static CreatePassiveSellOfferOp decode(XdrDataInputStream stream) throws IOException { + public static CreatePassiveSellOfferOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; CreatePassiveSellOfferOp decodedCreatePassiveSellOfferOp = new CreatePassiveSellOfferOp(); - decodedCreatePassiveSellOfferOp.selling = Asset.decode(stream); - decodedCreatePassiveSellOfferOp.buying = Asset.decode(stream); - decodedCreatePassiveSellOfferOp.amount = Int64.decode(stream); - decodedCreatePassiveSellOfferOp.price = Price.decode(stream); + decodedCreatePassiveSellOfferOp.selling = Asset.decode(stream, maxDepth); + decodedCreatePassiveSellOfferOp.buying = Asset.decode(stream, maxDepth); + decodedCreatePassiveSellOfferOp.amount = Int64.decode(stream, maxDepth); + decodedCreatePassiveSellOfferOp.price = Price.decode(stream, maxDepth); return decodedCreatePassiveSellOfferOp; } + public static CreatePassiveSellOfferOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static CreatePassiveSellOfferOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +67,7 @@ public static CreatePassiveSellOfferOp fromXdrBase64(String xdr) throws IOExcept public static CreatePassiveSellOfferOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java b/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java index 3a091589e..0e92e8c74 100644 --- a/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java +++ b/src/main/java/org/stellar/sdk/xdr/CryptoKeyType.java @@ -40,7 +40,8 @@ public int getValue() { return value; } - public static CryptoKeyType decode(XdrDataInputStream stream) throws IOException { + public static CryptoKeyType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -58,6 +59,10 @@ public static CryptoKeyType decode(XdrDataInputStream stream) throws IOException } } + public static CryptoKeyType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -70,6 +75,7 @@ public static CryptoKeyType fromXdrBase64(String xdr) throws IOException { public static CryptoKeyType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java b/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java index 1088e8324..d14adfba3 100644 --- a/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java +++ b/src/main/java/org/stellar/sdk/xdr/Curve25519Public.java @@ -30,17 +30,29 @@ public class Curve25519Public implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int keySize = key.length; + if (keySize != 32) { + throw new IOException("key size " + keySize + " does not match fixed size 32"); + } stream.write(getKey(), 0, keySize); } - public static Curve25519Public decode(XdrDataInputStream stream) throws IOException { + public static Curve25519Public decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Curve25519Public decodedCurve25519Public = new Curve25519Public(); int keySize = 32; decodedCurve25519Public.key = new byte[keySize]; - stream.read(decodedCurve25519Public.key, 0, keySize); + stream.readPaddedData(decodedCurve25519Public.key, 0, keySize); return decodedCurve25519Public; } + public static Curve25519Public decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Curve25519Public fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +61,7 @@ public static Curve25519Public fromXdrBase64(String xdr) throws IOException { public static Curve25519Public fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java b/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java index 47e39a513..41ea5bb08 100644 --- a/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java +++ b/src/main/java/org/stellar/sdk/xdr/Curve25519Secret.java @@ -30,17 +30,29 @@ public class Curve25519Secret implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int keySize = key.length; + if (keySize != 32) { + throw new IOException("key size " + keySize + " does not match fixed size 32"); + } stream.write(getKey(), 0, keySize); } - public static Curve25519Secret decode(XdrDataInputStream stream) throws IOException { + public static Curve25519Secret decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Curve25519Secret decodedCurve25519Secret = new Curve25519Secret(); int keySize = 32; decodedCurve25519Secret.key = new byte[keySize]; - stream.read(decodedCurve25519Secret.key, 0, keySize); + stream.readPaddedData(decodedCurve25519Secret.key, 0, keySize); return decodedCurve25519Secret; } + public static Curve25519Secret decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Curve25519Secret fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +61,7 @@ public static Curve25519Secret fromXdrBase64(String xdr) throws IOException { public static Curve25519Secret fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/DataEntry.java b/src/main/java/org/stellar/sdk/xdr/DataEntry.java index eb0e53d33..f3e9deb9d 100644 --- a/src/main/java/org/stellar/sdk/xdr/DataEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/DataEntry.java @@ -48,15 +48,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static DataEntry decode(XdrDataInputStream stream) throws IOException { + public static DataEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DataEntry decodedDataEntry = new DataEntry(); - decodedDataEntry.accountID = AccountID.decode(stream); - decodedDataEntry.dataName = String64.decode(stream); - decodedDataEntry.dataValue = DataValue.decode(stream); - decodedDataEntry.ext = DataEntryExt.decode(stream); + decodedDataEntry.accountID = AccountID.decode(stream, maxDepth); + decodedDataEntry.dataName = String64.decode(stream, maxDepth); + decodedDataEntry.dataValue = DataValue.decode(stream, maxDepth); + decodedDataEntry.ext = DataEntryExt.decode(stream, maxDepth); return decodedDataEntry; } + public static DataEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DataEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +73,7 @@ public static DataEntry fromXdrBase64(String xdr) throws IOException { public static DataEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -94,17 +103,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static DataEntryExt decode(XdrDataInputStream stream) throws IOException { + public static DataEntryExt decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DataEntryExt decodedDataEntryExt = new DataEntryExt(); Integer discriminant = stream.readInt(); decodedDataEntryExt.setDiscriminant(discriminant); switch (decodedDataEntryExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedDataEntryExt; } + public static DataEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DataEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -113,6 +132,7 @@ public static DataEntryExt fromXdrBase64(String xdr) throws IOException { public static DataEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/DataValue.java b/src/main/java/org/stellar/sdk/xdr/DataValue.java index cbe4a45ef..215c45a92 100644 --- a/src/main/java/org/stellar/sdk/xdr/DataValue.java +++ b/src/main/java/org/stellar/sdk/xdr/DataValue.java @@ -25,18 +25,43 @@ public class DataValue implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int DataValueSize = DataValue.length; + if (DataValueSize > 64) { + throw new IOException("DataValue size " + DataValueSize + " exceeds max size 64"); + } stream.writeInt(DataValueSize); stream.write(getDataValue(), 0, DataValueSize); } - public static DataValue decode(XdrDataInputStream stream) throws IOException { + public static DataValue decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DataValue decodedDataValue = new DataValue(); int DataValueSize = stream.readInt(); + if (DataValueSize < 0) { + throw new IOException("DataValue size " + DataValueSize + " is negative"); + } + if (DataValueSize > 64) { + throw new IOException("DataValue size " + DataValueSize + " exceeds max size 64"); + } + int DataValueRemainingInputLen = stream.getRemainingInputLen(); + if (DataValueRemainingInputLen >= 0 && DataValueRemainingInputLen < DataValueSize) { + throw new IOException( + "DataValue size " + + DataValueSize + + " exceeds remaining input length " + + DataValueRemainingInputLen); + } decodedDataValue.DataValue = new byte[DataValueSize]; - stream.read(decodedDataValue.DataValue, 0, DataValueSize); + stream.readPaddedData(decodedDataValue.DataValue, 0, DataValueSize); return decodedDataValue; } + public static DataValue decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DataValue fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +70,7 @@ public static DataValue fromXdrBase64(String xdr) throws IOException { public static DataValue fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/DecoratedSignature.java b/src/main/java/org/stellar/sdk/xdr/DecoratedSignature.java index 4c4c049d5..656649289 100644 --- a/src/main/java/org/stellar/sdk/xdr/DecoratedSignature.java +++ b/src/main/java/org/stellar/sdk/xdr/DecoratedSignature.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { signature.encode(stream); } - public static DecoratedSignature decode(XdrDataInputStream stream) throws IOException { + public static DecoratedSignature decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DecoratedSignature decodedDecoratedSignature = new DecoratedSignature(); - decodedDecoratedSignature.hint = SignatureHint.decode(stream); - decodedDecoratedSignature.signature = Signature.decode(stream); + decodedDecoratedSignature.hint = SignatureHint.decode(stream, maxDepth); + decodedDecoratedSignature.signature = Signature.decode(stream, maxDepth); return decodedDecoratedSignature; } + public static DecoratedSignature decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DecoratedSignature fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static DecoratedSignature fromXdrBase64(String xdr) throws IOException { public static DecoratedSignature fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/DependentTxCluster.java b/src/main/java/org/stellar/sdk/xdr/DependentTxCluster.java index cb980868e..4b942e27d 100644 --- a/src/main/java/org/stellar/sdk/xdr/DependentTxCluster.java +++ b/src/main/java/org/stellar/sdk/xdr/DependentTxCluster.java @@ -31,16 +31,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static DependentTxCluster decode(XdrDataInputStream stream) throws IOException { + public static DependentTxCluster decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DependentTxCluster decodedDependentTxCluster = new DependentTxCluster(); int DependentTxClusterSize = stream.readInt(); + if (DependentTxClusterSize < 0) { + throw new IOException("DependentTxCluster size " + DependentTxClusterSize + " is negative"); + } + int DependentTxClusterRemainingInputLen = stream.getRemainingInputLen(); + if (DependentTxClusterRemainingInputLen >= 0 + && DependentTxClusterRemainingInputLen < DependentTxClusterSize) { + throw new IOException( + "DependentTxCluster size " + + DependentTxClusterSize + + " exceeds remaining input length " + + DependentTxClusterRemainingInputLen); + } decodedDependentTxCluster.DependentTxCluster = new TransactionEnvelope[DependentTxClusterSize]; for (int i = 0; i < DependentTxClusterSize; i++) { - decodedDependentTxCluster.DependentTxCluster[i] = TransactionEnvelope.decode(stream); + decodedDependentTxCluster.DependentTxCluster[i] = + TransactionEnvelope.decode(stream, maxDepth); } return decodedDependentTxCluster; } + public static DependentTxCluster decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DependentTxCluster fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +71,7 @@ public static DependentTxCluster fromXdrBase64(String xdr) throws IOException { public static DependentTxCluster fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/DiagnosticEvent.java b/src/main/java/org/stellar/sdk/xdr/DiagnosticEvent.java index 8a065a784..59ce85d3f 100644 --- a/src/main/java/org/stellar/sdk/xdr/DiagnosticEvent.java +++ b/src/main/java/org/stellar/sdk/xdr/DiagnosticEvent.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { event.encode(stream); } - public static DiagnosticEvent decode(XdrDataInputStream stream) throws IOException { + public static DiagnosticEvent decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DiagnosticEvent decodedDiagnosticEvent = new DiagnosticEvent(); - decodedDiagnosticEvent.inSuccessfulContractCall = stream.readInt() == 1 ? true : false; - decodedDiagnosticEvent.event = ContractEvent.decode(stream); + decodedDiagnosticEvent.inSuccessfulContractCall = stream.readXdrBoolean(); + decodedDiagnosticEvent.event = ContractEvent.decode(stream, maxDepth); return decodedDiagnosticEvent; } + public static DiagnosticEvent decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DiagnosticEvent fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static DiagnosticEvent fromXdrBase64(String xdr) throws IOException { public static DiagnosticEvent fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/DontHave.java b/src/main/java/org/stellar/sdk/xdr/DontHave.java index 8a3aebac8..44e485033 100644 --- a/src/main/java/org/stellar/sdk/xdr/DontHave.java +++ b/src/main/java/org/stellar/sdk/xdr/DontHave.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { reqHash.encode(stream); } - public static DontHave decode(XdrDataInputStream stream) throws IOException { + public static DontHave decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; DontHave decodedDontHave = new DontHave(); - decodedDontHave.type = MessageType.decode(stream); - decodedDontHave.reqHash = Uint256.decode(stream); + decodedDontHave.type = MessageType.decode(stream, maxDepth); + decodedDontHave.reqHash = Uint256.decode(stream, maxDepth); return decodedDontHave; } + public static DontHave decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static DontHave fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static DontHave fromXdrBase64(String xdr) throws IOException { public static DontHave fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Duration.java b/src/main/java/org/stellar/sdk/xdr/Duration.java index 2c13824b5..d3f23b4d8 100644 --- a/src/main/java/org/stellar/sdk/xdr/Duration.java +++ b/src/main/java/org/stellar/sdk/xdr/Duration.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { Duration.encode(stream); } - public static Duration decode(XdrDataInputStream stream) throws IOException { + public static Duration decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Duration decodedDuration = new Duration(); - decodedDuration.Duration = Uint64.decode(stream); + decodedDuration.Duration = Uint64.decode(stream, maxDepth); return decodedDuration; } + public static Duration decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Duration fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static Duration fromXdrBase64(String xdr) throws IOException { public static Duration fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/EncryptedBody.java b/src/main/java/org/stellar/sdk/xdr/EncryptedBody.java index cf9b7c4ea..6bf885189 100644 --- a/src/main/java/org/stellar/sdk/xdr/EncryptedBody.java +++ b/src/main/java/org/stellar/sdk/xdr/EncryptedBody.java @@ -25,18 +25,43 @@ public class EncryptedBody implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int EncryptedBodySize = EncryptedBody.length; + if (EncryptedBodySize > 64000) { + throw new IOException("EncryptedBody size " + EncryptedBodySize + " exceeds max size 64000"); + } stream.writeInt(EncryptedBodySize); stream.write(getEncryptedBody(), 0, EncryptedBodySize); } - public static EncryptedBody decode(XdrDataInputStream stream) throws IOException { + public static EncryptedBody decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; EncryptedBody decodedEncryptedBody = new EncryptedBody(); int EncryptedBodySize = stream.readInt(); + if (EncryptedBodySize < 0) { + throw new IOException("EncryptedBody size " + EncryptedBodySize + " is negative"); + } + if (EncryptedBodySize > 64000) { + throw new IOException("EncryptedBody size " + EncryptedBodySize + " exceeds max size 64000"); + } + int EncryptedBodyRemainingInputLen = stream.getRemainingInputLen(); + if (EncryptedBodyRemainingInputLen >= 0 && EncryptedBodyRemainingInputLen < EncryptedBodySize) { + throw new IOException( + "EncryptedBody size " + + EncryptedBodySize + + " exceeds remaining input length " + + EncryptedBodyRemainingInputLen); + } decodedEncryptedBody.EncryptedBody = new byte[EncryptedBodySize]; - stream.read(decodedEncryptedBody.EncryptedBody, 0, EncryptedBodySize); + stream.readPaddedData(decodedEncryptedBody.EncryptedBody, 0, EncryptedBodySize); return decodedEncryptedBody; } + public static EncryptedBody decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static EncryptedBody fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +70,7 @@ public static EncryptedBody fromXdrBase64(String xdr) throws IOException { public static EncryptedBody fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResult.java b/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResult.java index 50116b34a..971233983 100644 --- a/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResult.java +++ b/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResult.java @@ -42,22 +42,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static EndSponsoringFutureReservesResult decode(XdrDataInputStream stream) + public static EndSponsoringFutureReservesResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; EndSponsoringFutureReservesResult decodedEndSponsoringFutureReservesResult = new EndSponsoringFutureReservesResult(); EndSponsoringFutureReservesResultCode discriminant = - EndSponsoringFutureReservesResultCode.decode(stream); + EndSponsoringFutureReservesResultCode.decode(stream, maxDepth); decodedEndSponsoringFutureReservesResult.setDiscriminant(discriminant); switch (decodedEndSponsoringFutureReservesResult.getDiscriminant()) { case END_SPONSORING_FUTURE_RESERVES_SUCCESS: break; case END_SPONSORING_FUTURE_RESERVES_NOT_SPONSORED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedEndSponsoringFutureReservesResult; } + public static EndSponsoringFutureReservesResult decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static EndSponsoringFutureReservesResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +77,7 @@ public static EndSponsoringFutureReservesResult fromXdrBase64(String xdr) throws public static EndSponsoringFutureReservesResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResultCode.java b/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResultCode.java index 74c63b0d6..9a564fa6a 100644 --- a/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/EndSponsoringFutureReservesResultCode.java @@ -35,8 +35,9 @@ public int getValue() { return value; } - public static EndSponsoringFutureReservesResultCode decode(XdrDataInputStream stream) - throws IOException { + public static EndSponsoringFutureReservesResultCode decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +49,11 @@ public static EndSponsoringFutureReservesResultCode decode(XdrDataInputStream st } } + public static EndSponsoringFutureReservesResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -61,6 +67,7 @@ public static EndSponsoringFutureReservesResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java b/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java index c1b9c28df..6ad767842 100644 --- a/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java +++ b/src/main/java/org/stellar/sdk/xdr/EnvelopeType.java @@ -48,7 +48,8 @@ public int getValue() { return value; } - public static EnvelopeType decode(XdrDataInputStream stream) throws IOException { + public static EnvelopeType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -76,6 +77,10 @@ public static EnvelopeType decode(XdrDataInputStream stream) throws IOException } } + public static EnvelopeType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -88,6 +93,7 @@ public static EnvelopeType fromXdrBase64(String xdr) throws IOException { public static EnvelopeType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Error.java b/src/main/java/org/stellar/sdk/xdr/Error.java index 6793a05ee..b78af2c20 100644 --- a/src/main/java/org/stellar/sdk/xdr/Error.java +++ b/src/main/java/org/stellar/sdk/xdr/Error.java @@ -32,16 +32,28 @@ public class Error implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { code.encode(stream); + int msgSize = msg.getBytes().length; + if (msgSize > 100) { + throw new IOException("msg size " + msgSize + " exceeds max size 100"); + } msg.encode(stream); } - public static Error decode(XdrDataInputStream stream) throws IOException { + public static Error decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Error decodedError = new Error(); - decodedError.code = ErrorCode.decode(stream); - decodedError.msg = XdrString.decode(stream, 100); + decodedError.code = ErrorCode.decode(stream, maxDepth); + decodedError.msg = XdrString.decode(stream, maxDepth, 100); return decodedError; } + public static Error decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Error fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +62,7 @@ public static Error fromXdrBase64(String xdr) throws IOException { public static Error fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ErrorCode.java b/src/main/java/org/stellar/sdk/xdr/ErrorCode.java index c4776cecc..d0b06e91f 100644 --- a/src/main/java/org/stellar/sdk/xdr/ErrorCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ErrorCode.java @@ -38,7 +38,8 @@ public int getValue() { return value; } - public static ErrorCode decode(XdrDataInputStream stream) throws IOException { + public static ErrorCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -56,6 +57,10 @@ public static ErrorCode decode(XdrDataInputStream stream) throws IOException { } } + public static ErrorCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -68,6 +73,7 @@ public static ErrorCode fromXdrBase64(String xdr) throws IOException { public static ErrorCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/EvictionIterator.java b/src/main/java/org/stellar/sdk/xdr/EvictionIterator.java index c64d74335..3ff7080bc 100644 --- a/src/main/java/org/stellar/sdk/xdr/EvictionIterator.java +++ b/src/main/java/org/stellar/sdk/xdr/EvictionIterator.java @@ -37,14 +37,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { bucketFileOffset.encode(stream); } - public static EvictionIterator decode(XdrDataInputStream stream) throws IOException { + public static EvictionIterator decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; EvictionIterator decodedEvictionIterator = new EvictionIterator(); - decodedEvictionIterator.bucketListLevel = Uint32.decode(stream); - decodedEvictionIterator.isCurrBucket = stream.readInt() == 1 ? true : false; - decodedEvictionIterator.bucketFileOffset = Uint64.decode(stream); + decodedEvictionIterator.bucketListLevel = Uint32.decode(stream, maxDepth); + decodedEvictionIterator.isCurrBucket = stream.readXdrBoolean(); + decodedEvictionIterator.bucketFileOffset = Uint64.decode(stream, maxDepth); return decodedEvictionIterator; } + public static EvictionIterator decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static EvictionIterator fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -53,6 +62,7 @@ public static EvictionIterator fromXdrBase64(String xdr) throws IOException { public static EvictionIterator fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLOp.java b/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLOp.java index b65611f41..6e5740b1b 100644 --- a/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLOp.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { extendTo.encode(stream); } - public static ExtendFootprintTTLOp decode(XdrDataInputStream stream) throws IOException { + public static ExtendFootprintTTLOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ExtendFootprintTTLOp decodedExtendFootprintTTLOp = new ExtendFootprintTTLOp(); - decodedExtendFootprintTTLOp.ext = ExtensionPoint.decode(stream); - decodedExtendFootprintTTLOp.extendTo = Uint32.decode(stream); + decodedExtendFootprintTTLOp.ext = ExtensionPoint.decode(stream, maxDepth); + decodedExtendFootprintTTLOp.extendTo = Uint32.decode(stream, maxDepth); return decodedExtendFootprintTTLOp; } + public static ExtendFootprintTTLOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ExtendFootprintTTLOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static ExtendFootprintTTLOp fromXdrBase64(String xdr) throws IOException public static ExtendFootprintTTLOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResult.java b/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResult.java index 691892f04..8925e5d21 100644 --- a/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResult.java @@ -45,9 +45,15 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ExtendFootprintTTLResult decode(XdrDataInputStream stream) throws IOException { + public static ExtendFootprintTTLResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ExtendFootprintTTLResult decodedExtendFootprintTTLResult = new ExtendFootprintTTLResult(); - ExtendFootprintTTLResultCode discriminant = ExtendFootprintTTLResultCode.decode(stream); + ExtendFootprintTTLResultCode discriminant = + ExtendFootprintTTLResultCode.decode(stream, maxDepth); decodedExtendFootprintTTLResult.setDiscriminant(discriminant); switch (decodedExtendFootprintTTLResult.getDiscriminant()) { case EXTEND_FOOTPRINT_TTL_SUCCESS: @@ -56,10 +62,16 @@ public static ExtendFootprintTTLResult decode(XdrDataInputStream stream) throws case EXTEND_FOOTPRINT_TTL_RESOURCE_LIMIT_EXCEEDED: case EXTEND_FOOTPRINT_TTL_INSUFFICIENT_REFUNDABLE_FEE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedExtendFootprintTTLResult; } + public static ExtendFootprintTTLResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ExtendFootprintTTLResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +80,7 @@ public static ExtendFootprintTTLResult fromXdrBase64(String xdr) throws IOExcept public static ExtendFootprintTTLResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResultCode.java b/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResultCode.java index 3a05b0919..6ba491d0e 100644 --- a/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ExtendFootprintTTLResultCode.java @@ -39,7 +39,9 @@ public int getValue() { return value; } - public static ExtendFootprintTTLResultCode decode(XdrDataInputStream stream) throws IOException { + public static ExtendFootprintTTLResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -55,6 +57,10 @@ public static ExtendFootprintTTLResultCode decode(XdrDataInputStream stream) thr } } + public static ExtendFootprintTTLResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -67,6 +73,7 @@ public static ExtendFootprintTTLResultCode fromXdrBase64(String xdr) throws IOEx public static ExtendFootprintTTLResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ExtensionPoint.java b/src/main/java/org/stellar/sdk/xdr/ExtensionPoint.java index 5fef68f55..f2abf431a 100644 --- a/src/main/java/org/stellar/sdk/xdr/ExtensionPoint.java +++ b/src/main/java/org/stellar/sdk/xdr/ExtensionPoint.java @@ -37,17 +37,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ExtensionPoint decode(XdrDataInputStream stream) throws IOException { + public static ExtensionPoint decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ExtensionPoint decodedExtensionPoint = new ExtensionPoint(); Integer discriminant = stream.readInt(); decodedExtensionPoint.setDiscriminant(discriminant); switch (decodedExtensionPoint.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedExtensionPoint; } + public static ExtensionPoint decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ExtensionPoint fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -56,6 +66,7 @@ public static ExtensionPoint fromXdrBase64(String xdr) throws IOException { public static ExtensionPoint fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java index fa5d4fdbf..223264204 100644 --- a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java +++ b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransaction.java @@ -51,15 +51,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static FeeBumpTransaction decode(XdrDataInputStream stream) throws IOException { + public static FeeBumpTransaction decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; FeeBumpTransaction decodedFeeBumpTransaction = new FeeBumpTransaction(); - decodedFeeBumpTransaction.feeSource = MuxedAccount.decode(stream); - decodedFeeBumpTransaction.fee = Int64.decode(stream); - decodedFeeBumpTransaction.innerTx = FeeBumpTransactionInnerTx.decode(stream); - decodedFeeBumpTransaction.ext = FeeBumpTransactionExt.decode(stream); + decodedFeeBumpTransaction.feeSource = MuxedAccount.decode(stream, maxDepth); + decodedFeeBumpTransaction.fee = Int64.decode(stream, maxDepth); + decodedFeeBumpTransaction.innerTx = FeeBumpTransactionInnerTx.decode(stream, maxDepth); + decodedFeeBumpTransaction.ext = FeeBumpTransactionExt.decode(stream, maxDepth); return decodedFeeBumpTransaction; } + public static FeeBumpTransaction decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static FeeBumpTransaction fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +77,7 @@ public static FeeBumpTransaction fromXdrBase64(String xdr) throws IOException { public static FeeBumpTransaction fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -99,18 +109,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static FeeBumpTransactionInnerTx decode(XdrDataInputStream stream) throws IOException { + public static FeeBumpTransactionInnerTx decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; FeeBumpTransactionInnerTx decodedFeeBumpTransactionInnerTx = new FeeBumpTransactionInnerTx(); - EnvelopeType discriminant = EnvelopeType.decode(stream); + EnvelopeType discriminant = EnvelopeType.decode(stream, maxDepth); decodedFeeBumpTransactionInnerTx.setDiscriminant(discriminant); switch (decodedFeeBumpTransactionInnerTx.getDiscriminant()) { case ENVELOPE_TYPE_TX: - decodedFeeBumpTransactionInnerTx.v1 = TransactionV1Envelope.decode(stream); + decodedFeeBumpTransactionInnerTx.v1 = TransactionV1Envelope.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedFeeBumpTransactionInnerTx; } + public static FeeBumpTransactionInnerTx decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static FeeBumpTransactionInnerTx fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -119,6 +140,7 @@ public static FeeBumpTransactionInnerTx fromXdrBase64(String xdr) throws IOExcep public static FeeBumpTransactionInnerTx fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -149,17 +171,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static FeeBumpTransactionExt decode(XdrDataInputStream stream) throws IOException { + public static FeeBumpTransactionExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; FeeBumpTransactionExt decodedFeeBumpTransactionExt = new FeeBumpTransactionExt(); Integer discriminant = stream.readInt(); decodedFeeBumpTransactionExt.setDiscriminant(discriminant); switch (decodedFeeBumpTransactionExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedFeeBumpTransactionExt; } + public static FeeBumpTransactionExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static FeeBumpTransactionExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -168,6 +201,7 @@ public static FeeBumpTransactionExt fromXdrBase64(String xdr) throws IOException public static FeeBumpTransactionExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java index 7454255ee..49006676f 100644 --- a/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java +++ b/src/main/java/org/stellar/sdk/xdr/FeeBumpTransactionEnvelope.java @@ -35,23 +35,49 @@ public class FeeBumpTransactionEnvelope implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { tx.encode(stream); int signaturesSize = getSignatures().length; + if (signaturesSize > 20) { + throw new IOException("signatures size " + signaturesSize + " exceeds max size 20"); + } stream.writeInt(signaturesSize); for (int i = 0; i < signaturesSize; i++) { signatures[i].encode(stream); } } - public static FeeBumpTransactionEnvelope decode(XdrDataInputStream stream) throws IOException { + public static FeeBumpTransactionEnvelope decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; FeeBumpTransactionEnvelope decodedFeeBumpTransactionEnvelope = new FeeBumpTransactionEnvelope(); - decodedFeeBumpTransactionEnvelope.tx = FeeBumpTransaction.decode(stream); + decodedFeeBumpTransactionEnvelope.tx = FeeBumpTransaction.decode(stream, maxDepth); int signaturesSize = stream.readInt(); + if (signaturesSize < 0) { + throw new IOException("signatures size " + signaturesSize + " is negative"); + } + if (signaturesSize > 20) { + throw new IOException("signatures size " + signaturesSize + " exceeds max size 20"); + } + int signaturesRemainingInputLen = stream.getRemainingInputLen(); + if (signaturesRemainingInputLen >= 0 && signaturesRemainingInputLen < signaturesSize) { + throw new IOException( + "signatures size " + + signaturesSize + + " exceeds remaining input length " + + signaturesRemainingInputLen); + } decodedFeeBumpTransactionEnvelope.signatures = new DecoratedSignature[signaturesSize]; for (int i = 0; i < signaturesSize; i++) { - decodedFeeBumpTransactionEnvelope.signatures[i] = DecoratedSignature.decode(stream); + decodedFeeBumpTransactionEnvelope.signatures[i] = DecoratedSignature.decode(stream, maxDepth); } return decodedFeeBumpTransactionEnvelope; } + public static FeeBumpTransactionEnvelope decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static FeeBumpTransactionEnvelope fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +86,7 @@ public static FeeBumpTransactionEnvelope fromXdrBase64(String xdr) throws IOExce public static FeeBumpTransactionEnvelope fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/FloodAdvert.java b/src/main/java/org/stellar/sdk/xdr/FloodAdvert.java index 61c340c4b..88c470ef8 100644 --- a/src/main/java/org/stellar/sdk/xdr/FloodAdvert.java +++ b/src/main/java/org/stellar/sdk/xdr/FloodAdvert.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { txHashes.encode(stream); } - public static FloodAdvert decode(XdrDataInputStream stream) throws IOException { + public static FloodAdvert decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; FloodAdvert decodedFloodAdvert = new FloodAdvert(); - decodedFloodAdvert.txHashes = TxAdvertVector.decode(stream); + decodedFloodAdvert.txHashes = TxAdvertVector.decode(stream, maxDepth); return decodedFloodAdvert; } + public static FloodAdvert decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static FloodAdvert fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static FloodAdvert fromXdrBase64(String xdr) throws IOException { public static FloodAdvert fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/FloodDemand.java b/src/main/java/org/stellar/sdk/xdr/FloodDemand.java index ede6bbb71..fe54b044a 100644 --- a/src/main/java/org/stellar/sdk/xdr/FloodDemand.java +++ b/src/main/java/org/stellar/sdk/xdr/FloodDemand.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { txHashes.encode(stream); } - public static FloodDemand decode(XdrDataInputStream stream) throws IOException { + public static FloodDemand decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; FloodDemand decodedFloodDemand = new FloodDemand(); - decodedFloodDemand.txHashes = TxDemandVector.decode(stream); + decodedFloodDemand.txHashes = TxDemandVector.decode(stream, maxDepth); return decodedFloodDemand; } + public static FloodDemand decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static FloodDemand fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static FloodDemand fromXdrBase64(String xdr) throws IOException { public static FloodDemand fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/GeneralizedTransactionSet.java b/src/main/java/org/stellar/sdk/xdr/GeneralizedTransactionSet.java index ef936a2db..cb9554645 100644 --- a/src/main/java/org/stellar/sdk/xdr/GeneralizedTransactionSet.java +++ b/src/main/java/org/stellar/sdk/xdr/GeneralizedTransactionSet.java @@ -40,18 +40,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static GeneralizedTransactionSet decode(XdrDataInputStream stream) throws IOException { + public static GeneralizedTransactionSet decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; GeneralizedTransactionSet decodedGeneralizedTransactionSet = new GeneralizedTransactionSet(); Integer discriminant = stream.readInt(); decodedGeneralizedTransactionSet.setDiscriminant(discriminant); switch (decodedGeneralizedTransactionSet.getDiscriminant()) { case 1: - decodedGeneralizedTransactionSet.v1TxSet = TransactionSetV1.decode(stream); + decodedGeneralizedTransactionSet.v1TxSet = TransactionSetV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedGeneralizedTransactionSet; } + public static GeneralizedTransactionSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static GeneralizedTransactionSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +71,7 @@ public static GeneralizedTransactionSet fromXdrBase64(String xdr) throws IOExcep public static GeneralizedTransactionSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Hash.java b/src/main/java/org/stellar/sdk/xdr/Hash.java index 5e12af7d2..e11fd0019 100644 --- a/src/main/java/org/stellar/sdk/xdr/Hash.java +++ b/src/main/java/org/stellar/sdk/xdr/Hash.java @@ -25,17 +25,28 @@ public class Hash implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int HashSize = Hash.length; + if (HashSize != 32) { + throw new IOException("Hash size " + HashSize + " does not match fixed size 32"); + } stream.write(getHash(), 0, HashSize); } - public static Hash decode(XdrDataInputStream stream) throws IOException { + public static Hash decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Hash decodedHash = new Hash(); int HashSize = 32; decodedHash.Hash = new byte[HashSize]; - stream.read(decodedHash.Hash, 0, HashSize); + stream.readPaddedData(decodedHash.Hash, 0, HashSize); return decodedHash; } + public static Hash decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Hash fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -44,6 +55,7 @@ public static Hash fromXdrBase64(String xdr) throws IOException { public static Hash fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HashIDPreimage.java b/src/main/java/org/stellar/sdk/xdr/HashIDPreimage.java index d1e91420d..ba9db762e 100644 --- a/src/main/java/org/stellar/sdk/xdr/HashIDPreimage.java +++ b/src/main/java/org/stellar/sdk/xdr/HashIDPreimage.java @@ -79,28 +79,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static HashIDPreimage decode(XdrDataInputStream stream) throws IOException { + public static HashIDPreimage decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HashIDPreimage decodedHashIDPreimage = new HashIDPreimage(); - EnvelopeType discriminant = EnvelopeType.decode(stream); + EnvelopeType discriminant = EnvelopeType.decode(stream, maxDepth); decodedHashIDPreimage.setDiscriminant(discriminant); switch (decodedHashIDPreimage.getDiscriminant()) { case ENVELOPE_TYPE_OP_ID: - decodedHashIDPreimage.operationID = HashIDPreimageOperationID.decode(stream); + decodedHashIDPreimage.operationID = HashIDPreimageOperationID.decode(stream, maxDepth); break; case ENVELOPE_TYPE_POOL_REVOKE_OP_ID: - decodedHashIDPreimage.revokeID = HashIDPreimageRevokeID.decode(stream); + decodedHashIDPreimage.revokeID = HashIDPreimageRevokeID.decode(stream, maxDepth); break; case ENVELOPE_TYPE_CONTRACT_ID: - decodedHashIDPreimage.contractID = HashIDPreimageContractID.decode(stream); + decodedHashIDPreimage.contractID = HashIDPreimageContractID.decode(stream, maxDepth); break; case ENVELOPE_TYPE_SOROBAN_AUTHORIZATION: decodedHashIDPreimage.sorobanAuthorization = - HashIDPreimageSorobanAuthorization.decode(stream); + HashIDPreimageSorobanAuthorization.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedHashIDPreimage; } + public static HashIDPreimage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HashIDPreimage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -109,6 +119,7 @@ public static HashIDPreimage fromXdrBase64(String xdr) throws IOException { public static HashIDPreimage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -139,14 +150,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { opNum.encode(stream); } - public static HashIDPreimageOperationID decode(XdrDataInputStream stream) throws IOException { + public static HashIDPreimageOperationID decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HashIDPreimageOperationID decodedHashIDPreimageOperationID = new HashIDPreimageOperationID(); - decodedHashIDPreimageOperationID.sourceAccount = AccountID.decode(stream); - decodedHashIDPreimageOperationID.seqNum = SequenceNumber.decode(stream); - decodedHashIDPreimageOperationID.opNum = Uint32.decode(stream); + decodedHashIDPreimageOperationID.sourceAccount = AccountID.decode(stream, maxDepth); + decodedHashIDPreimageOperationID.seqNum = SequenceNumber.decode(stream, maxDepth); + decodedHashIDPreimageOperationID.opNum = Uint32.decode(stream, maxDepth); return decodedHashIDPreimageOperationID; } + public static HashIDPreimageOperationID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HashIDPreimageOperationID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -155,6 +175,7 @@ public static HashIDPreimageOperationID fromXdrBase64(String xdr) throws IOExcep public static HashIDPreimageOperationID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -192,16 +213,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { asset.encode(stream); } - public static HashIDPreimageRevokeID decode(XdrDataInputStream stream) throws IOException { + public static HashIDPreimageRevokeID decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HashIDPreimageRevokeID decodedHashIDPreimageRevokeID = new HashIDPreimageRevokeID(); - decodedHashIDPreimageRevokeID.sourceAccount = AccountID.decode(stream); - decodedHashIDPreimageRevokeID.seqNum = SequenceNumber.decode(stream); - decodedHashIDPreimageRevokeID.opNum = Uint32.decode(stream); - decodedHashIDPreimageRevokeID.liquidityPoolID = PoolID.decode(stream); - decodedHashIDPreimageRevokeID.asset = Asset.decode(stream); + decodedHashIDPreimageRevokeID.sourceAccount = AccountID.decode(stream, maxDepth); + decodedHashIDPreimageRevokeID.seqNum = SequenceNumber.decode(stream, maxDepth); + decodedHashIDPreimageRevokeID.opNum = Uint32.decode(stream, maxDepth); + decodedHashIDPreimageRevokeID.liquidityPoolID = PoolID.decode(stream, maxDepth); + decodedHashIDPreimageRevokeID.asset = Asset.decode(stream, maxDepth); return decodedHashIDPreimageRevokeID; } + public static HashIDPreimageRevokeID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HashIDPreimageRevokeID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -210,6 +240,7 @@ public static HashIDPreimageRevokeID fromXdrBase64(String xdr) throws IOExceptio public static HashIDPreimageRevokeID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -238,13 +269,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { contractIDPreimage.encode(stream); } - public static HashIDPreimageContractID decode(XdrDataInputStream stream) throws IOException { + public static HashIDPreimageContractID decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HashIDPreimageContractID decodedHashIDPreimageContractID = new HashIDPreimageContractID(); - decodedHashIDPreimageContractID.networkID = Hash.decode(stream); - decodedHashIDPreimageContractID.contractIDPreimage = ContractIDPreimage.decode(stream); + decodedHashIDPreimageContractID.networkID = Hash.decode(stream, maxDepth); + decodedHashIDPreimageContractID.contractIDPreimage = + ContractIDPreimage.decode(stream, maxDepth); return decodedHashIDPreimageContractID; } + public static HashIDPreimageContractID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HashIDPreimageContractID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -253,6 +294,7 @@ public static HashIDPreimageContractID fromXdrBase64(String xdr) throws IOExcept public static HashIDPreimageContractID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -287,18 +329,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { invocation.encode(stream); } - public static HashIDPreimageSorobanAuthorization decode(XdrDataInputStream stream) + public static HashIDPreimageSorobanAuthorization decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HashIDPreimageSorobanAuthorization decodedHashIDPreimageSorobanAuthorization = new HashIDPreimageSorobanAuthorization(); - decodedHashIDPreimageSorobanAuthorization.networkID = Hash.decode(stream); - decodedHashIDPreimageSorobanAuthorization.nonce = Int64.decode(stream); - decodedHashIDPreimageSorobanAuthorization.signatureExpirationLedger = Uint32.decode(stream); + decodedHashIDPreimageSorobanAuthorization.networkID = Hash.decode(stream, maxDepth); + decodedHashIDPreimageSorobanAuthorization.nonce = Int64.decode(stream, maxDepth); + decodedHashIDPreimageSorobanAuthorization.signatureExpirationLedger = + Uint32.decode(stream, maxDepth); decodedHashIDPreimageSorobanAuthorization.invocation = - SorobanAuthorizedInvocation.decode(stream); + SorobanAuthorizedInvocation.decode(stream, maxDepth); return decodedHashIDPreimageSorobanAuthorization; } + public static HashIDPreimageSorobanAuthorization decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HashIDPreimageSorobanAuthorization fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -308,6 +360,7 @@ public static HashIDPreimageSorobanAuthorization fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Hello.java b/src/main/java/org/stellar/sdk/xdr/Hello.java index 53888e208..372672462 100644 --- a/src/main/java/org/stellar/sdk/xdr/Hello.java +++ b/src/main/java/org/stellar/sdk/xdr/Hello.java @@ -49,6 +49,10 @@ public void encode(XdrDataOutputStream stream) throws IOException { overlayVersion.encode(stream); overlayMinVersion.encode(stream); networkID.encode(stream); + int versionStrSize = versionStr.getBytes().length; + if (versionStrSize > 100) { + throw new IOException("versionStr size " + versionStrSize + " exceeds max size 100"); + } versionStr.encode(stream); stream.writeInt(listeningPort); peerID.encode(stream); @@ -56,20 +60,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { nonce.encode(stream); } - public static Hello decode(XdrDataInputStream stream) throws IOException { + public static Hello decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Hello decodedHello = new Hello(); - decodedHello.ledgerVersion = Uint32.decode(stream); - decodedHello.overlayVersion = Uint32.decode(stream); - decodedHello.overlayMinVersion = Uint32.decode(stream); - decodedHello.networkID = Hash.decode(stream); - decodedHello.versionStr = XdrString.decode(stream, 100); + decodedHello.ledgerVersion = Uint32.decode(stream, maxDepth); + decodedHello.overlayVersion = Uint32.decode(stream, maxDepth); + decodedHello.overlayMinVersion = Uint32.decode(stream, maxDepth); + decodedHello.networkID = Hash.decode(stream, maxDepth); + decodedHello.versionStr = XdrString.decode(stream, maxDepth, 100); decodedHello.listeningPort = stream.readInt(); - decodedHello.peerID = NodeID.decode(stream); - decodedHello.cert = AuthCert.decode(stream); - decodedHello.nonce = Uint256.decode(stream); + decodedHello.peerID = NodeID.decode(stream, maxDepth); + decodedHello.cert = AuthCert.decode(stream, maxDepth); + decodedHello.nonce = Uint256.decode(stream, maxDepth); return decodedHello; } + public static Hello decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Hello fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -78,6 +90,7 @@ public static Hello fromXdrBase64(String xdr) throws IOException { public static Hello fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java b/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java index 99a5d60c4..68115736c 100644 --- a/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java +++ b/src/main/java/org/stellar/sdk/xdr/HmacSha256Key.java @@ -30,17 +30,28 @@ public class HmacSha256Key implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int keySize = key.length; + if (keySize != 32) { + throw new IOException("key size " + keySize + " does not match fixed size 32"); + } stream.write(getKey(), 0, keySize); } - public static HmacSha256Key decode(XdrDataInputStream stream) throws IOException { + public static HmacSha256Key decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HmacSha256Key decodedHmacSha256Key = new HmacSha256Key(); int keySize = 32; decodedHmacSha256Key.key = new byte[keySize]; - stream.read(decodedHmacSha256Key.key, 0, keySize); + stream.readPaddedData(decodedHmacSha256Key.key, 0, keySize); return decodedHmacSha256Key; } + public static HmacSha256Key decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HmacSha256Key fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +60,7 @@ public static HmacSha256Key fromXdrBase64(String xdr) throws IOException { public static HmacSha256Key fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java b/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java index 724769c2e..4bf8a902f 100644 --- a/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java +++ b/src/main/java/org/stellar/sdk/xdr/HmacSha256Mac.java @@ -30,17 +30,28 @@ public class HmacSha256Mac implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int macSize = mac.length; + if (macSize != 32) { + throw new IOException("mac size " + macSize + " does not match fixed size 32"); + } stream.write(getMac(), 0, macSize); } - public static HmacSha256Mac decode(XdrDataInputStream stream) throws IOException { + public static HmacSha256Mac decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HmacSha256Mac decodedHmacSha256Mac = new HmacSha256Mac(); int macSize = 32; decodedHmacSha256Mac.mac = new byte[macSize]; - stream.read(decodedHmacSha256Mac.mac, 0, macSize); + stream.readPaddedData(decodedHmacSha256Mac.mac, 0, macSize); return decodedHmacSha256Mac; } + public static HmacSha256Mac decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HmacSha256Mac fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +60,7 @@ public static HmacSha256Mac fromXdrBase64(String xdr) throws IOException { public static HmacSha256Mac fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HostFunction.java b/src/main/java/org/stellar/sdk/xdr/HostFunction.java index 30008da52..b1dbcbbef 100644 --- a/src/main/java/org/stellar/sdk/xdr/HostFunction.java +++ b/src/main/java/org/stellar/sdk/xdr/HostFunction.java @@ -59,29 +59,47 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static HostFunction decode(XdrDataInputStream stream) throws IOException { + public static HostFunction decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HostFunction decodedHostFunction = new HostFunction(); - HostFunctionType discriminant = HostFunctionType.decode(stream); + HostFunctionType discriminant = HostFunctionType.decode(stream, maxDepth); decodedHostFunction.setDiscriminant(discriminant); switch (decodedHostFunction.getDiscriminant()) { case HOST_FUNCTION_TYPE_INVOKE_CONTRACT: - decodedHostFunction.invokeContract = InvokeContractArgs.decode(stream); + decodedHostFunction.invokeContract = InvokeContractArgs.decode(stream, maxDepth); break; case HOST_FUNCTION_TYPE_CREATE_CONTRACT: - decodedHostFunction.createContract = CreateContractArgs.decode(stream); + decodedHostFunction.createContract = CreateContractArgs.decode(stream, maxDepth); break; case HOST_FUNCTION_TYPE_UPLOAD_CONTRACT_WASM: int wasmSize = stream.readInt(); + if (wasmSize < 0) { + throw new IOException("wasm size " + wasmSize + " is negative"); + } + int wasmRemainingInputLen = stream.getRemainingInputLen(); + if (wasmRemainingInputLen >= 0 && wasmRemainingInputLen < wasmSize) { + throw new IOException( + "wasm size " + wasmSize + " exceeds remaining input length " + wasmRemainingInputLen); + } decodedHostFunction.wasm = new byte[wasmSize]; - stream.read(decodedHostFunction.wasm, 0, wasmSize); + stream.readPaddedData(decodedHostFunction.wasm, 0, wasmSize); break; case HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2: - decodedHostFunction.createContractV2 = CreateContractArgsV2.decode(stream); + decodedHostFunction.createContractV2 = CreateContractArgsV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedHostFunction; } + public static HostFunction decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HostFunction fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -90,6 +108,7 @@ public static HostFunction fromXdrBase64(String xdr) throws IOException { public static HostFunction fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HostFunctionType.java b/src/main/java/org/stellar/sdk/xdr/HostFunctionType.java index 1a101de2e..735101234 100644 --- a/src/main/java/org/stellar/sdk/xdr/HostFunctionType.java +++ b/src/main/java/org/stellar/sdk/xdr/HostFunctionType.java @@ -36,7 +36,9 @@ public int getValue() { return value; } - public static HostFunctionType decode(XdrDataInputStream stream) throws IOException { + public static HostFunctionType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -52,6 +54,10 @@ public static HostFunctionType decode(XdrDataInputStream stream) throws IOExcept } } + public static HostFunctionType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -64,6 +70,7 @@ public static HostFunctionType fromXdrBase64(String xdr) throws IOException { public static HostFunctionType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntry.java b/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntry.java index a51a5111e..1880211f5 100644 --- a/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntry.java @@ -52,24 +52,35 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static HotArchiveBucketEntry decode(XdrDataInputStream stream) throws IOException { + public static HotArchiveBucketEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; HotArchiveBucketEntry decodedHotArchiveBucketEntry = new HotArchiveBucketEntry(); - HotArchiveBucketEntryType discriminant = HotArchiveBucketEntryType.decode(stream); + HotArchiveBucketEntryType discriminant = HotArchiveBucketEntryType.decode(stream, maxDepth); decodedHotArchiveBucketEntry.setDiscriminant(discriminant); switch (decodedHotArchiveBucketEntry.getDiscriminant()) { case HOT_ARCHIVE_ARCHIVED: - decodedHotArchiveBucketEntry.archivedEntry = LedgerEntry.decode(stream); + decodedHotArchiveBucketEntry.archivedEntry = LedgerEntry.decode(stream, maxDepth); break; case HOT_ARCHIVE_LIVE: - decodedHotArchiveBucketEntry.key = LedgerKey.decode(stream); + decodedHotArchiveBucketEntry.key = LedgerKey.decode(stream, maxDepth); break; case HOT_ARCHIVE_METAENTRY: - decodedHotArchiveBucketEntry.metaEntry = BucketMetadata.decode(stream); + decodedHotArchiveBucketEntry.metaEntry = BucketMetadata.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedHotArchiveBucketEntry; } + public static HotArchiveBucketEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static HotArchiveBucketEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -78,6 +89,7 @@ public static HotArchiveBucketEntry fromXdrBase64(String xdr) throws IOException public static HotArchiveBucketEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntryType.java b/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntryType.java index 62872bd16..45af4c42b 100644 --- a/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntryType.java +++ b/src/main/java/org/stellar/sdk/xdr/HotArchiveBucketEntryType.java @@ -36,7 +36,9 @@ public int getValue() { return value; } - public static HotArchiveBucketEntryType decode(XdrDataInputStream stream) throws IOException { + public static HotArchiveBucketEntryType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case -1: @@ -50,6 +52,10 @@ public static HotArchiveBucketEntryType decode(XdrDataInputStream stream) throws } } + public static HotArchiveBucketEntryType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -62,6 +68,7 @@ public static HotArchiveBucketEntryType fromXdrBase64(String xdr) throws IOExcep public static HotArchiveBucketEntryType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/IPAddrType.java b/src/main/java/org/stellar/sdk/xdr/IPAddrType.java index 460a9a5b8..8f79b1884 100644 --- a/src/main/java/org/stellar/sdk/xdr/IPAddrType.java +++ b/src/main/java/org/stellar/sdk/xdr/IPAddrType.java @@ -32,7 +32,8 @@ public int getValue() { return value; } - public static IPAddrType decode(XdrDataInputStream stream) throws IOException { + public static IPAddrType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +45,10 @@ public static IPAddrType decode(XdrDataInputStream stream) throws IOException { } } + public static IPAddrType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +61,7 @@ public static IPAddrType fromXdrBase64(String xdr) throws IOException { public static IPAddrType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InflationPayout.java b/src/main/java/org/stellar/sdk/xdr/InflationPayout.java index d7dd343f4..90bb15cc9 100644 --- a/src/main/java/org/stellar/sdk/xdr/InflationPayout.java +++ b/src/main/java/org/stellar/sdk/xdr/InflationPayout.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { amount.encode(stream); } - public static InflationPayout decode(XdrDataInputStream stream) throws IOException { + public static InflationPayout decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InflationPayout decodedInflationPayout = new InflationPayout(); - decodedInflationPayout.destination = AccountID.decode(stream); - decodedInflationPayout.amount = Int64.decode(stream); + decodedInflationPayout.destination = AccountID.decode(stream, maxDepth); + decodedInflationPayout.amount = Int64.decode(stream, maxDepth); return decodedInflationPayout; } + public static InflationPayout decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InflationPayout fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static InflationPayout fromXdrBase64(String xdr) throws IOException { public static InflationPayout fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InflationResult.java b/src/main/java/org/stellar/sdk/xdr/InflationResult.java index 713596bf4..570f08b7b 100644 --- a/src/main/java/org/stellar/sdk/xdr/InflationResult.java +++ b/src/main/java/org/stellar/sdk/xdr/InflationResult.java @@ -47,24 +47,45 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InflationResult decode(XdrDataInputStream stream) throws IOException { + public static InflationResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InflationResult decodedInflationResult = new InflationResult(); - InflationResultCode discriminant = InflationResultCode.decode(stream); + InflationResultCode discriminant = InflationResultCode.decode(stream, maxDepth); decodedInflationResult.setDiscriminant(discriminant); switch (decodedInflationResult.getDiscriminant()) { case INFLATION_SUCCESS: int payoutsSize = stream.readInt(); + if (payoutsSize < 0) { + throw new IOException("payouts size " + payoutsSize + " is negative"); + } + int payoutsRemainingInputLen = stream.getRemainingInputLen(); + if (payoutsRemainingInputLen >= 0 && payoutsRemainingInputLen < payoutsSize) { + throw new IOException( + "payouts size " + + payoutsSize + + " exceeds remaining input length " + + payoutsRemainingInputLen); + } decodedInflationResult.payouts = new InflationPayout[payoutsSize]; for (int i = 0; i < payoutsSize; i++) { - decodedInflationResult.payouts[i] = InflationPayout.decode(stream); + decodedInflationResult.payouts[i] = InflationPayout.decode(stream, maxDepth); } break; case INFLATION_NOT_TIME: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedInflationResult; } + public static InflationResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InflationResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -73,6 +94,7 @@ public static InflationResult fromXdrBase64(String xdr) throws IOException { public static InflationResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InflationResultCode.java b/src/main/java/org/stellar/sdk/xdr/InflationResultCode.java index b4364a4ec..a49cd5b1c 100644 --- a/src/main/java/org/stellar/sdk/xdr/InflationResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/InflationResultCode.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static InflationResultCode decode(XdrDataInputStream stream) throws IOException { + public static InflationResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -46,6 +48,10 @@ public static InflationResultCode decode(XdrDataInputStream stream) throws IOExc } } + public static InflationResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -58,6 +64,7 @@ public static InflationResultCode fromXdrBase64(String xdr) throws IOException { public static InflationResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java index 37732726f..1b6dbf5ec 100644 --- a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java +++ b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResult.java @@ -71,14 +71,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static InnerTransactionResult decode(XdrDataInputStream stream) throws IOException { + public static InnerTransactionResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InnerTransactionResult decodedInnerTransactionResult = new InnerTransactionResult(); - decodedInnerTransactionResult.feeCharged = Int64.decode(stream); - decodedInnerTransactionResult.result = InnerTransactionResultResult.decode(stream); - decodedInnerTransactionResult.ext = InnerTransactionResultExt.decode(stream); + decodedInnerTransactionResult.feeCharged = Int64.decode(stream, maxDepth); + decodedInnerTransactionResult.result = InnerTransactionResultResult.decode(stream, maxDepth); + decodedInnerTransactionResult.ext = InnerTransactionResultExt.decode(stream, maxDepth); return decodedInnerTransactionResult; } + public static InnerTransactionResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InnerTransactionResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -87,6 +96,7 @@ public static InnerTransactionResult fromXdrBase64(String xdr) throws IOExceptio public static InnerTransactionResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -158,19 +168,35 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InnerTransactionResultResult decode(XdrDataInputStream stream) + public static InnerTransactionResultResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InnerTransactionResultResult decodedInnerTransactionResultResult = new InnerTransactionResultResult(); - TransactionResultCode discriminant = TransactionResultCode.decode(stream); + TransactionResultCode discriminant = TransactionResultCode.decode(stream, maxDepth); decodedInnerTransactionResultResult.setDiscriminant(discriminant); switch (decodedInnerTransactionResultResult.getDiscriminant()) { case txSUCCESS: case txFAILED: int resultsSize = stream.readInt(); + if (resultsSize < 0) { + throw new IOException("results size " + resultsSize + " is negative"); + } + int resultsRemainingInputLen = stream.getRemainingInputLen(); + if (resultsRemainingInputLen >= 0 && resultsRemainingInputLen < resultsSize) { + throw new IOException( + "results size " + + resultsSize + + " exceeds remaining input length " + + resultsRemainingInputLen); + } decodedInnerTransactionResultResult.results = new OperationResult[resultsSize]; for (int i = 0; i < resultsSize; i++) { - decodedInnerTransactionResultResult.results[i] = OperationResult.decode(stream); + decodedInnerTransactionResultResult.results[i] = + OperationResult.decode(stream, maxDepth); } break; case txTOO_EARLY: @@ -189,10 +215,17 @@ public static InnerTransactionResultResult decode(XdrDataInputStream stream) case txMALFORMED: case txSOROBAN_INVALID: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedInnerTransactionResultResult; } + public static InnerTransactionResultResult decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InnerTransactionResultResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -201,6 +234,7 @@ public static InnerTransactionResultResult fromXdrBase64(String xdr) throws IOEx public static InnerTransactionResultResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -231,17 +265,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InnerTransactionResultExt decode(XdrDataInputStream stream) throws IOException { + public static InnerTransactionResultExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InnerTransactionResultExt decodedInnerTransactionResultExt = new InnerTransactionResultExt(); Integer discriminant = stream.readInt(); decodedInnerTransactionResultExt.setDiscriminant(discriminant); switch (decodedInnerTransactionResultExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedInnerTransactionResultExt; } + public static InnerTransactionResultExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InnerTransactionResultExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -250,6 +295,7 @@ public static InnerTransactionResultExt fromXdrBase64(String xdr) throws IOExcep public static InnerTransactionResultExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java index cfa7ee907..2bda9b7c4 100644 --- a/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java +++ b/src/main/java/org/stellar/sdk/xdr/InnerTransactionResultPair.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { result.encode(stream); } - public static InnerTransactionResultPair decode(XdrDataInputStream stream) throws IOException { + public static InnerTransactionResultPair decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InnerTransactionResultPair decodedInnerTransactionResultPair = new InnerTransactionResultPair(); - decodedInnerTransactionResultPair.transactionHash = Hash.decode(stream); - decodedInnerTransactionResultPair.result = InnerTransactionResult.decode(stream); + decodedInnerTransactionResultPair.transactionHash = Hash.decode(stream, maxDepth); + decodedInnerTransactionResultPair.result = InnerTransactionResult.decode(stream, maxDepth); return decodedInnerTransactionResultPair; } + public static InnerTransactionResultPair decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InnerTransactionResultPair fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static InnerTransactionResultPair fromXdrBase64(String xdr) throws IOExce public static InnerTransactionResultPair fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Int128Parts.java b/src/main/java/org/stellar/sdk/xdr/Int128Parts.java index 90a50fa66..9b4302e60 100644 --- a/src/main/java/org/stellar/sdk/xdr/Int128Parts.java +++ b/src/main/java/org/stellar/sdk/xdr/Int128Parts.java @@ -34,13 +34,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { lo.encode(stream); } - public static Int128Parts decode(XdrDataInputStream stream) throws IOException { + public static Int128Parts decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Int128Parts decodedInt128Parts = new Int128Parts(); - decodedInt128Parts.hi = Int64.decode(stream); - decodedInt128Parts.lo = Uint64.decode(stream); + decodedInt128Parts.hi = Int64.decode(stream, maxDepth); + decodedInt128Parts.lo = Uint64.decode(stream, maxDepth); return decodedInt128Parts; } + public static Int128Parts decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Int128Parts fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +57,7 @@ public static Int128Parts fromXdrBase64(String xdr) throws IOException { public static Int128Parts fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Int256Parts.java b/src/main/java/org/stellar/sdk/xdr/Int256Parts.java index c16004932..653642619 100644 --- a/src/main/java/org/stellar/sdk/xdr/Int256Parts.java +++ b/src/main/java/org/stellar/sdk/xdr/Int256Parts.java @@ -40,15 +40,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { lo_lo.encode(stream); } - public static Int256Parts decode(XdrDataInputStream stream) throws IOException { + public static Int256Parts decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Int256Parts decodedInt256Parts = new Int256Parts(); - decodedInt256Parts.hi_hi = Int64.decode(stream); - decodedInt256Parts.hi_lo = Uint64.decode(stream); - decodedInt256Parts.lo_hi = Uint64.decode(stream); - decodedInt256Parts.lo_lo = Uint64.decode(stream); + decodedInt256Parts.hi_hi = Int64.decode(stream, maxDepth); + decodedInt256Parts.hi_lo = Uint64.decode(stream, maxDepth); + decodedInt256Parts.lo_hi = Uint64.decode(stream, maxDepth); + decodedInt256Parts.lo_lo = Uint64.decode(stream, maxDepth); return decodedInt256Parts; } + public static Int256Parts decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Int256Parts fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -57,6 +65,7 @@ public static Int256Parts fromXdrBase64(String xdr) throws IOException { public static Int256Parts fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Int32.java b/src/main/java/org/stellar/sdk/xdr/Int32.java index 78a3fc591..a349b6155 100644 --- a/src/main/java/org/stellar/sdk/xdr/Int32.java +++ b/src/main/java/org/stellar/sdk/xdr/Int32.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(int32); } - public static Int32 decode(XdrDataInputStream stream) throws IOException { + public static Int32 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Int32 decodedInt32 = new Int32(); decodedInt32.int32 = stream.readInt(); return decodedInt32; } + public static Int32 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Int32 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static Int32 fromXdrBase64(String xdr) throws IOException { public static Int32 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Int64.java b/src/main/java/org/stellar/sdk/xdr/Int64.java index 552d33f46..1e1d850e6 100644 --- a/src/main/java/org/stellar/sdk/xdr/Int64.java +++ b/src/main/java/org/stellar/sdk/xdr/Int64.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.writeLong(int64); } - public static Int64 decode(XdrDataInputStream stream) throws IOException { + public static Int64 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Int64 decodedInt64 = new Int64(); decodedInt64.int64 = stream.readLong(); return decodedInt64; } + public static Int64 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Int64 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static Int64 fromXdrBase64(String xdr) throws IOException { public static Int64 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InvokeContractArgs.java b/src/main/java/org/stellar/sdk/xdr/InvokeContractArgs.java index 6b9254205..4d23c218b 100644 --- a/src/main/java/org/stellar/sdk/xdr/InvokeContractArgs.java +++ b/src/main/java/org/stellar/sdk/xdr/InvokeContractArgs.java @@ -41,18 +41,35 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InvokeContractArgs decode(XdrDataInputStream stream) throws IOException { + public static InvokeContractArgs decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InvokeContractArgs decodedInvokeContractArgs = new InvokeContractArgs(); - decodedInvokeContractArgs.contractAddress = SCAddress.decode(stream); - decodedInvokeContractArgs.functionName = SCSymbol.decode(stream); + decodedInvokeContractArgs.contractAddress = SCAddress.decode(stream, maxDepth); + decodedInvokeContractArgs.functionName = SCSymbol.decode(stream, maxDepth); int argsSize = stream.readInt(); + if (argsSize < 0) { + throw new IOException("args size " + argsSize + " is negative"); + } + int argsRemainingInputLen = stream.getRemainingInputLen(); + if (argsRemainingInputLen >= 0 && argsRemainingInputLen < argsSize) { + throw new IOException( + "args size " + argsSize + " exceeds remaining input length " + argsRemainingInputLen); + } decodedInvokeContractArgs.args = new SCVal[argsSize]; for (int i = 0; i < argsSize; i++) { - decodedInvokeContractArgs.args[i] = SCVal.decode(stream); + decodedInvokeContractArgs.args[i] = SCVal.decode(stream, maxDepth); } return decodedInvokeContractArgs; } + public static InvokeContractArgs decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InvokeContractArgs fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -61,6 +78,7 @@ public static InvokeContractArgs fromXdrBase64(String xdr) throws IOException { public static InvokeContractArgs fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionOp.java b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionOp.java index 006863189..922f384b9 100644 --- a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionOp.java +++ b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionOp.java @@ -41,17 +41,34 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InvokeHostFunctionOp decode(XdrDataInputStream stream) throws IOException { + public static InvokeHostFunctionOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InvokeHostFunctionOp decodedInvokeHostFunctionOp = new InvokeHostFunctionOp(); - decodedInvokeHostFunctionOp.hostFunction = HostFunction.decode(stream); + decodedInvokeHostFunctionOp.hostFunction = HostFunction.decode(stream, maxDepth); int authSize = stream.readInt(); + if (authSize < 0) { + throw new IOException("auth size " + authSize + " is negative"); + } + int authRemainingInputLen = stream.getRemainingInputLen(); + if (authRemainingInputLen >= 0 && authRemainingInputLen < authSize) { + throw new IOException( + "auth size " + authSize + " exceeds remaining input length " + authRemainingInputLen); + } decodedInvokeHostFunctionOp.auth = new SorobanAuthorizationEntry[authSize]; for (int i = 0; i < authSize; i++) { - decodedInvokeHostFunctionOp.auth[i] = SorobanAuthorizationEntry.decode(stream); + decodedInvokeHostFunctionOp.auth[i] = SorobanAuthorizationEntry.decode(stream, maxDepth); } return decodedInvokeHostFunctionOp; } + public static InvokeHostFunctionOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InvokeHostFunctionOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +77,7 @@ public static InvokeHostFunctionOp fromXdrBase64(String xdr) throws IOException public static InvokeHostFunctionOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResult.java b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResult.java index 262a61e95..4650fa384 100644 --- a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResult.java +++ b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResult.java @@ -51,13 +51,19 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InvokeHostFunctionResult decode(XdrDataInputStream stream) throws IOException { + public static InvokeHostFunctionResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InvokeHostFunctionResult decodedInvokeHostFunctionResult = new InvokeHostFunctionResult(); - InvokeHostFunctionResultCode discriminant = InvokeHostFunctionResultCode.decode(stream); + InvokeHostFunctionResultCode discriminant = + InvokeHostFunctionResultCode.decode(stream, maxDepth); decodedInvokeHostFunctionResult.setDiscriminant(discriminant); switch (decodedInvokeHostFunctionResult.getDiscriminant()) { case INVOKE_HOST_FUNCTION_SUCCESS: - decodedInvokeHostFunctionResult.success = Hash.decode(stream); + decodedInvokeHostFunctionResult.success = Hash.decode(stream, maxDepth); break; case INVOKE_HOST_FUNCTION_MALFORMED: case INVOKE_HOST_FUNCTION_TRAPPED: @@ -65,10 +71,16 @@ public static InvokeHostFunctionResult decode(XdrDataInputStream stream) throws case INVOKE_HOST_FUNCTION_ENTRY_ARCHIVED: case INVOKE_HOST_FUNCTION_INSUFFICIENT_REFUNDABLE_FEE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedInvokeHostFunctionResult; } + public static InvokeHostFunctionResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InvokeHostFunctionResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -77,6 +89,7 @@ public static InvokeHostFunctionResult fromXdrBase64(String xdr) throws IOExcept public static InvokeHostFunctionResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResultCode.java b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResultCode.java index 9c9e0928b..bed021d7f 100644 --- a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionResultCode.java @@ -43,7 +43,9 @@ public int getValue() { return value; } - public static InvokeHostFunctionResultCode decode(XdrDataInputStream stream) throws IOException { + public static InvokeHostFunctionResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -63,6 +65,10 @@ public static InvokeHostFunctionResultCode decode(XdrDataInputStream stream) thr } } + public static InvokeHostFunctionResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -75,6 +81,7 @@ public static InvokeHostFunctionResultCode fromXdrBase64(String xdr) throws IOEx public static InvokeHostFunctionResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionSuccessPreImage.java b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionSuccessPreImage.java index 011db4b3a..6286378d8 100644 --- a/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionSuccessPreImage.java +++ b/src/main/java/org/stellar/sdk/xdr/InvokeHostFunctionSuccessPreImage.java @@ -39,19 +39,39 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static InvokeHostFunctionSuccessPreImage decode(XdrDataInputStream stream) + public static InvokeHostFunctionSuccessPreImage decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; InvokeHostFunctionSuccessPreImage decodedInvokeHostFunctionSuccessPreImage = new InvokeHostFunctionSuccessPreImage(); - decodedInvokeHostFunctionSuccessPreImage.returnValue = SCVal.decode(stream); + decodedInvokeHostFunctionSuccessPreImage.returnValue = SCVal.decode(stream, maxDepth); int eventsSize = stream.readInt(); + if (eventsSize < 0) { + throw new IOException("events size " + eventsSize + " is negative"); + } + int eventsRemainingInputLen = stream.getRemainingInputLen(); + if (eventsRemainingInputLen >= 0 && eventsRemainingInputLen < eventsSize) { + throw new IOException( + "events size " + + eventsSize + + " exceeds remaining input length " + + eventsRemainingInputLen); + } decodedInvokeHostFunctionSuccessPreImage.events = new ContractEvent[eventsSize]; for (int i = 0; i < eventsSize; i++) { - decodedInvokeHostFunctionSuccessPreImage.events[i] = ContractEvent.decode(stream); + decodedInvokeHostFunctionSuccessPreImage.events[i] = ContractEvent.decode(stream, maxDepth); } return decodedInvokeHostFunctionSuccessPreImage; } + public static InvokeHostFunctionSuccessPreImage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static InvokeHostFunctionSuccessPreImage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +80,7 @@ public static InvokeHostFunctionSuccessPreImage fromXdrBase64(String xdr) throws public static InvokeHostFunctionSuccessPreImage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerBounds.java b/src/main/java/org/stellar/sdk/xdr/LedgerBounds.java index 89517e965..95c0868cd 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerBounds.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerBounds.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { maxLedger.encode(stream); } - public static LedgerBounds decode(XdrDataInputStream stream) throws IOException { + public static LedgerBounds decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerBounds decodedLedgerBounds = new LedgerBounds(); - decodedLedgerBounds.minLedger = Uint32.decode(stream); - decodedLedgerBounds.maxLedger = Uint32.decode(stream); + decodedLedgerBounds.minLedger = Uint32.decode(stream, maxDepth); + decodedLedgerBounds.maxLedger = Uint32.decode(stream, maxDepth); return decodedLedgerBounds; } + public static LedgerBounds decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerBounds fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static LedgerBounds fromXdrBase64(String xdr) throws IOException { public static LedgerBounds fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java index 856c587ed..a9d6537b5 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMeta.java @@ -51,24 +51,34 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerCloseMeta decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMeta decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMeta decodedLedgerCloseMeta = new LedgerCloseMeta(); Integer discriminant = stream.readInt(); decodedLedgerCloseMeta.setDiscriminant(discriminant); switch (decodedLedgerCloseMeta.getDiscriminant()) { case 0: - decodedLedgerCloseMeta.v0 = LedgerCloseMetaV0.decode(stream); + decodedLedgerCloseMeta.v0 = LedgerCloseMetaV0.decode(stream, maxDepth); break; case 1: - decodedLedgerCloseMeta.v1 = LedgerCloseMetaV1.decode(stream); + decodedLedgerCloseMeta.v1 = LedgerCloseMetaV1.decode(stream, maxDepth); break; case 2: - decodedLedgerCloseMeta.v2 = LedgerCloseMetaV2.decode(stream); + decodedLedgerCloseMeta.v2 = LedgerCloseMetaV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerCloseMeta; } + public static LedgerCloseMeta decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMeta fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -77,6 +87,7 @@ public static LedgerCloseMeta fromXdrBase64(String xdr) throws IOException { public static LedgerCloseMeta fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaBatch.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaBatch.java index 7eaa4d2eb..e6630f107 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaBatch.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaBatch.java @@ -47,18 +47,39 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerCloseMetaBatch decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMetaBatch decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMetaBatch decodedLedgerCloseMetaBatch = new LedgerCloseMetaBatch(); - decodedLedgerCloseMetaBatch.startSequence = Uint32.decode(stream); - decodedLedgerCloseMetaBatch.endSequence = Uint32.decode(stream); + decodedLedgerCloseMetaBatch.startSequence = Uint32.decode(stream, maxDepth); + decodedLedgerCloseMetaBatch.endSequence = Uint32.decode(stream, maxDepth); int ledgerCloseMetasSize = stream.readInt(); + if (ledgerCloseMetasSize < 0) { + throw new IOException("ledgerCloseMetas size " + ledgerCloseMetasSize + " is negative"); + } + int ledgerCloseMetasRemainingInputLen = stream.getRemainingInputLen(); + if (ledgerCloseMetasRemainingInputLen >= 0 + && ledgerCloseMetasRemainingInputLen < ledgerCloseMetasSize) { + throw new IOException( + "ledgerCloseMetas size " + + ledgerCloseMetasSize + + " exceeds remaining input length " + + ledgerCloseMetasRemainingInputLen); + } decodedLedgerCloseMetaBatch.ledgerCloseMetas = new LedgerCloseMeta[ledgerCloseMetasSize]; for (int i = 0; i < ledgerCloseMetasSize; i++) { - decodedLedgerCloseMetaBatch.ledgerCloseMetas[i] = LedgerCloseMeta.decode(stream); + decodedLedgerCloseMetaBatch.ledgerCloseMetas[i] = LedgerCloseMeta.decode(stream, maxDepth); } return decodedLedgerCloseMetaBatch; } + public static LedgerCloseMetaBatch decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMetaBatch fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -67,6 +88,7 @@ public static LedgerCloseMetaBatch fromXdrBase64(String xdr) throws IOException public static LedgerCloseMetaBatch fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExt.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExt.java index b337c94ff..daa8f9c37 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExt.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExt.java @@ -43,7 +43,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerCloseMetaExt decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMetaExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMetaExt decodedLedgerCloseMetaExt = new LedgerCloseMetaExt(); Integer discriminant = stream.readInt(); decodedLedgerCloseMetaExt.setDiscriminant(discriminant); @@ -51,12 +56,18 @@ public static LedgerCloseMetaExt decode(XdrDataInputStream stream) throws IOExce case 0: break; case 1: - decodedLedgerCloseMetaExt.v1 = LedgerCloseMetaExtV1.decode(stream); + decodedLedgerCloseMetaExt.v1 = LedgerCloseMetaExtV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerCloseMetaExt; } + public static LedgerCloseMetaExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMetaExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +76,7 @@ public static LedgerCloseMetaExt fromXdrBase64(String xdr) throws IOException { public static LedgerCloseMetaExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExtV1.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExtV1.java index b95bf02f7..4ed85774b 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExtV1.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaExtV1.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { sorobanFeeWrite1KB.encode(stream); } - public static LedgerCloseMetaExtV1 decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMetaExtV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMetaExtV1 decodedLedgerCloseMetaExtV1 = new LedgerCloseMetaExtV1(); - decodedLedgerCloseMetaExtV1.ext = ExtensionPoint.decode(stream); - decodedLedgerCloseMetaExtV1.sorobanFeeWrite1KB = Int64.decode(stream); + decodedLedgerCloseMetaExtV1.ext = ExtensionPoint.decode(stream, maxDepth); + decodedLedgerCloseMetaExtV1.sorobanFeeWrite1KB = Int64.decode(stream, maxDepth); return decodedLedgerCloseMetaExtV1; } + public static LedgerCloseMetaExtV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMetaExtV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static LedgerCloseMetaExtV1 fromXdrBase64(String xdr) throws IOException public static LedgerCloseMetaExtV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV0.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV0.java index d328eba7d..bfc895754 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV0.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV0.java @@ -65,28 +65,71 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerCloseMetaV0 decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMetaV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMetaV0 decodedLedgerCloseMetaV0 = new LedgerCloseMetaV0(); - decodedLedgerCloseMetaV0.ledgerHeader = LedgerHeaderHistoryEntry.decode(stream); - decodedLedgerCloseMetaV0.txSet = TransactionSet.decode(stream); + decodedLedgerCloseMetaV0.ledgerHeader = LedgerHeaderHistoryEntry.decode(stream, maxDepth); + decodedLedgerCloseMetaV0.txSet = TransactionSet.decode(stream, maxDepth); int txProcessingSize = stream.readInt(); + if (txProcessingSize < 0) { + throw new IOException("txProcessing size " + txProcessingSize + " is negative"); + } + int txProcessingRemainingInputLen = stream.getRemainingInputLen(); + if (txProcessingRemainingInputLen >= 0 && txProcessingRemainingInputLen < txProcessingSize) { + throw new IOException( + "txProcessing size " + + txProcessingSize + + " exceeds remaining input length " + + txProcessingRemainingInputLen); + } decodedLedgerCloseMetaV0.txProcessing = new TransactionResultMeta[txProcessingSize]; for (int i = 0; i < txProcessingSize; i++) { - decodedLedgerCloseMetaV0.txProcessing[i] = TransactionResultMeta.decode(stream); + decodedLedgerCloseMetaV0.txProcessing[i] = TransactionResultMeta.decode(stream, maxDepth); } int upgradesProcessingSize = stream.readInt(); + if (upgradesProcessingSize < 0) { + throw new IOException("upgradesProcessing size " + upgradesProcessingSize + " is negative"); + } + int upgradesProcessingRemainingInputLen = stream.getRemainingInputLen(); + if (upgradesProcessingRemainingInputLen >= 0 + && upgradesProcessingRemainingInputLen < upgradesProcessingSize) { + throw new IOException( + "upgradesProcessing size " + + upgradesProcessingSize + + " exceeds remaining input length " + + upgradesProcessingRemainingInputLen); + } decodedLedgerCloseMetaV0.upgradesProcessing = new UpgradeEntryMeta[upgradesProcessingSize]; for (int i = 0; i < upgradesProcessingSize; i++) { - decodedLedgerCloseMetaV0.upgradesProcessing[i] = UpgradeEntryMeta.decode(stream); + decodedLedgerCloseMetaV0.upgradesProcessing[i] = UpgradeEntryMeta.decode(stream, maxDepth); } int scpInfoSize = stream.readInt(); + if (scpInfoSize < 0) { + throw new IOException("scpInfo size " + scpInfoSize + " is negative"); + } + int scpInfoRemainingInputLen = stream.getRemainingInputLen(); + if (scpInfoRemainingInputLen >= 0 && scpInfoRemainingInputLen < scpInfoSize) { + throw new IOException( + "scpInfo size " + + scpInfoSize + + " exceeds remaining input length " + + scpInfoRemainingInputLen); + } decodedLedgerCloseMetaV0.scpInfo = new SCPHistoryEntry[scpInfoSize]; for (int i = 0; i < scpInfoSize; i++) { - decodedLedgerCloseMetaV0.scpInfo[i] = SCPHistoryEntry.decode(stream); + decodedLedgerCloseMetaV0.scpInfo[i] = SCPHistoryEntry.decode(stream, maxDepth); } return decodedLedgerCloseMetaV0; } + public static LedgerCloseMetaV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMetaV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -95,6 +138,7 @@ public static LedgerCloseMetaV0 fromXdrBase64(String xdr) throws IOException { public static LedgerCloseMetaV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV1.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV1.java index 76cc5e7d7..b4adc834c 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV1.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV1.java @@ -93,40 +93,105 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerCloseMetaV1 decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMetaV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMetaV1 decodedLedgerCloseMetaV1 = new LedgerCloseMetaV1(); - decodedLedgerCloseMetaV1.ext = LedgerCloseMetaExt.decode(stream); - decodedLedgerCloseMetaV1.ledgerHeader = LedgerHeaderHistoryEntry.decode(stream); - decodedLedgerCloseMetaV1.txSet = GeneralizedTransactionSet.decode(stream); + decodedLedgerCloseMetaV1.ext = LedgerCloseMetaExt.decode(stream, maxDepth); + decodedLedgerCloseMetaV1.ledgerHeader = LedgerHeaderHistoryEntry.decode(stream, maxDepth); + decodedLedgerCloseMetaV1.txSet = GeneralizedTransactionSet.decode(stream, maxDepth); int txProcessingSize = stream.readInt(); + if (txProcessingSize < 0) { + throw new IOException("txProcessing size " + txProcessingSize + " is negative"); + } + int txProcessingRemainingInputLen = stream.getRemainingInputLen(); + if (txProcessingRemainingInputLen >= 0 && txProcessingRemainingInputLen < txProcessingSize) { + throw new IOException( + "txProcessing size " + + txProcessingSize + + " exceeds remaining input length " + + txProcessingRemainingInputLen); + } decodedLedgerCloseMetaV1.txProcessing = new TransactionResultMeta[txProcessingSize]; for (int i = 0; i < txProcessingSize; i++) { - decodedLedgerCloseMetaV1.txProcessing[i] = TransactionResultMeta.decode(stream); + decodedLedgerCloseMetaV1.txProcessing[i] = TransactionResultMeta.decode(stream, maxDepth); } int upgradesProcessingSize = stream.readInt(); + if (upgradesProcessingSize < 0) { + throw new IOException("upgradesProcessing size " + upgradesProcessingSize + " is negative"); + } + int upgradesProcessingRemainingInputLen = stream.getRemainingInputLen(); + if (upgradesProcessingRemainingInputLen >= 0 + && upgradesProcessingRemainingInputLen < upgradesProcessingSize) { + throw new IOException( + "upgradesProcessing size " + + upgradesProcessingSize + + " exceeds remaining input length " + + upgradesProcessingRemainingInputLen); + } decodedLedgerCloseMetaV1.upgradesProcessing = new UpgradeEntryMeta[upgradesProcessingSize]; for (int i = 0; i < upgradesProcessingSize; i++) { - decodedLedgerCloseMetaV1.upgradesProcessing[i] = UpgradeEntryMeta.decode(stream); + decodedLedgerCloseMetaV1.upgradesProcessing[i] = UpgradeEntryMeta.decode(stream, maxDepth); } int scpInfoSize = stream.readInt(); + if (scpInfoSize < 0) { + throw new IOException("scpInfo size " + scpInfoSize + " is negative"); + } + int scpInfoRemainingInputLen = stream.getRemainingInputLen(); + if (scpInfoRemainingInputLen >= 0 && scpInfoRemainingInputLen < scpInfoSize) { + throw new IOException( + "scpInfo size " + + scpInfoSize + + " exceeds remaining input length " + + scpInfoRemainingInputLen); + } decodedLedgerCloseMetaV1.scpInfo = new SCPHistoryEntry[scpInfoSize]; for (int i = 0; i < scpInfoSize; i++) { - decodedLedgerCloseMetaV1.scpInfo[i] = SCPHistoryEntry.decode(stream); + decodedLedgerCloseMetaV1.scpInfo[i] = SCPHistoryEntry.decode(stream, maxDepth); } - decodedLedgerCloseMetaV1.totalByteSizeOfLiveSorobanState = Uint64.decode(stream); + decodedLedgerCloseMetaV1.totalByteSizeOfLiveSorobanState = Uint64.decode(stream, maxDepth); int evictedKeysSize = stream.readInt(); + if (evictedKeysSize < 0) { + throw new IOException("evictedKeys size " + evictedKeysSize + " is negative"); + } + int evictedKeysRemainingInputLen = stream.getRemainingInputLen(); + if (evictedKeysRemainingInputLen >= 0 && evictedKeysRemainingInputLen < evictedKeysSize) { + throw new IOException( + "evictedKeys size " + + evictedKeysSize + + " exceeds remaining input length " + + evictedKeysRemainingInputLen); + } decodedLedgerCloseMetaV1.evictedKeys = new LedgerKey[evictedKeysSize]; for (int i = 0; i < evictedKeysSize; i++) { - decodedLedgerCloseMetaV1.evictedKeys[i] = LedgerKey.decode(stream); + decodedLedgerCloseMetaV1.evictedKeys[i] = LedgerKey.decode(stream, maxDepth); } int unusedSize = stream.readInt(); + if (unusedSize < 0) { + throw new IOException("unused size " + unusedSize + " is negative"); + } + int unusedRemainingInputLen = stream.getRemainingInputLen(); + if (unusedRemainingInputLen >= 0 && unusedRemainingInputLen < unusedSize) { + throw new IOException( + "unused size " + + unusedSize + + " exceeds remaining input length " + + unusedRemainingInputLen); + } decodedLedgerCloseMetaV1.unused = new LedgerEntry[unusedSize]; for (int i = 0; i < unusedSize; i++) { - decodedLedgerCloseMetaV1.unused[i] = LedgerEntry.decode(stream); + decodedLedgerCloseMetaV1.unused[i] = LedgerEntry.decode(stream, maxDepth); } return decodedLedgerCloseMetaV1; } + public static LedgerCloseMetaV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMetaV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -135,6 +200,7 @@ public static LedgerCloseMetaV1 fromXdrBase64(String xdr) throws IOException { public static LedgerCloseMetaV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV2.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV2.java index 0dedddbfc..b90ed65c6 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV2.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseMetaV2.java @@ -84,35 +84,89 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerCloseMetaV2 decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseMetaV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseMetaV2 decodedLedgerCloseMetaV2 = new LedgerCloseMetaV2(); - decodedLedgerCloseMetaV2.ext = LedgerCloseMetaExt.decode(stream); - decodedLedgerCloseMetaV2.ledgerHeader = LedgerHeaderHistoryEntry.decode(stream); - decodedLedgerCloseMetaV2.txSet = GeneralizedTransactionSet.decode(stream); + decodedLedgerCloseMetaV2.ext = LedgerCloseMetaExt.decode(stream, maxDepth); + decodedLedgerCloseMetaV2.ledgerHeader = LedgerHeaderHistoryEntry.decode(stream, maxDepth); + decodedLedgerCloseMetaV2.txSet = GeneralizedTransactionSet.decode(stream, maxDepth); int txProcessingSize = stream.readInt(); + if (txProcessingSize < 0) { + throw new IOException("txProcessing size " + txProcessingSize + " is negative"); + } + int txProcessingRemainingInputLen = stream.getRemainingInputLen(); + if (txProcessingRemainingInputLen >= 0 && txProcessingRemainingInputLen < txProcessingSize) { + throw new IOException( + "txProcessing size " + + txProcessingSize + + " exceeds remaining input length " + + txProcessingRemainingInputLen); + } decodedLedgerCloseMetaV2.txProcessing = new TransactionResultMetaV1[txProcessingSize]; for (int i = 0; i < txProcessingSize; i++) { - decodedLedgerCloseMetaV2.txProcessing[i] = TransactionResultMetaV1.decode(stream); + decodedLedgerCloseMetaV2.txProcessing[i] = TransactionResultMetaV1.decode(stream, maxDepth); } int upgradesProcessingSize = stream.readInt(); + if (upgradesProcessingSize < 0) { + throw new IOException("upgradesProcessing size " + upgradesProcessingSize + " is negative"); + } + int upgradesProcessingRemainingInputLen = stream.getRemainingInputLen(); + if (upgradesProcessingRemainingInputLen >= 0 + && upgradesProcessingRemainingInputLen < upgradesProcessingSize) { + throw new IOException( + "upgradesProcessing size " + + upgradesProcessingSize + + " exceeds remaining input length " + + upgradesProcessingRemainingInputLen); + } decodedLedgerCloseMetaV2.upgradesProcessing = new UpgradeEntryMeta[upgradesProcessingSize]; for (int i = 0; i < upgradesProcessingSize; i++) { - decodedLedgerCloseMetaV2.upgradesProcessing[i] = UpgradeEntryMeta.decode(stream); + decodedLedgerCloseMetaV2.upgradesProcessing[i] = UpgradeEntryMeta.decode(stream, maxDepth); } int scpInfoSize = stream.readInt(); + if (scpInfoSize < 0) { + throw new IOException("scpInfo size " + scpInfoSize + " is negative"); + } + int scpInfoRemainingInputLen = stream.getRemainingInputLen(); + if (scpInfoRemainingInputLen >= 0 && scpInfoRemainingInputLen < scpInfoSize) { + throw new IOException( + "scpInfo size " + + scpInfoSize + + " exceeds remaining input length " + + scpInfoRemainingInputLen); + } decodedLedgerCloseMetaV2.scpInfo = new SCPHistoryEntry[scpInfoSize]; for (int i = 0; i < scpInfoSize; i++) { - decodedLedgerCloseMetaV2.scpInfo[i] = SCPHistoryEntry.decode(stream); + decodedLedgerCloseMetaV2.scpInfo[i] = SCPHistoryEntry.decode(stream, maxDepth); } - decodedLedgerCloseMetaV2.totalByteSizeOfLiveSorobanState = Uint64.decode(stream); + decodedLedgerCloseMetaV2.totalByteSizeOfLiveSorobanState = Uint64.decode(stream, maxDepth); int evictedKeysSize = stream.readInt(); + if (evictedKeysSize < 0) { + throw new IOException("evictedKeys size " + evictedKeysSize + " is negative"); + } + int evictedKeysRemainingInputLen = stream.getRemainingInputLen(); + if (evictedKeysRemainingInputLen >= 0 && evictedKeysRemainingInputLen < evictedKeysSize) { + throw new IOException( + "evictedKeys size " + + evictedKeysSize + + " exceeds remaining input length " + + evictedKeysRemainingInputLen); + } decodedLedgerCloseMetaV2.evictedKeys = new LedgerKey[evictedKeysSize]; for (int i = 0; i < evictedKeysSize; i++) { - decodedLedgerCloseMetaV2.evictedKeys[i] = LedgerKey.decode(stream); + decodedLedgerCloseMetaV2.evictedKeys[i] = LedgerKey.decode(stream, maxDepth); } return decodedLedgerCloseMetaV2; } + public static LedgerCloseMetaV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseMetaV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -121,6 +175,7 @@ public static LedgerCloseMetaV2 fromXdrBase64(String xdr) throws IOException { public static LedgerCloseMetaV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerCloseValueSignature.java b/src/main/java/org/stellar/sdk/xdr/LedgerCloseValueSignature.java index 849686164..5ce4fba2e 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerCloseValueSignature.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerCloseValueSignature.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { signature.encode(stream); } - public static LedgerCloseValueSignature decode(XdrDataInputStream stream) throws IOException { + public static LedgerCloseValueSignature decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerCloseValueSignature decodedLedgerCloseValueSignature = new LedgerCloseValueSignature(); - decodedLedgerCloseValueSignature.nodeID = NodeID.decode(stream); - decodedLedgerCloseValueSignature.signature = Signature.decode(stream); + decodedLedgerCloseValueSignature.nodeID = NodeID.decode(stream, maxDepth); + decodedLedgerCloseValueSignature.signature = Signature.decode(stream, maxDepth); return decodedLedgerCloseValueSignature; } + public static LedgerCloseValueSignature decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerCloseValueSignature fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static LedgerCloseValueSignature fromXdrBase64(String xdr) throws IOExcep public static LedgerCloseValueSignature fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerEntry.java b/src/main/java/org/stellar/sdk/xdr/LedgerEntry.java index 26c0ea62f..52697fd67 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerEntry.java @@ -71,14 +71,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static LedgerEntry decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntry decodedLedgerEntry = new LedgerEntry(); - decodedLedgerEntry.lastModifiedLedgerSeq = Uint32.decode(stream); - decodedLedgerEntry.data = LedgerEntryData.decode(stream); - decodedLedgerEntry.ext = LedgerEntryExt.decode(stream); + decodedLedgerEntry.lastModifiedLedgerSeq = Uint32.decode(stream, maxDepth); + decodedLedgerEntry.data = LedgerEntryData.decode(stream, maxDepth); + decodedLedgerEntry.ext = LedgerEntryExt.decode(stream, maxDepth); return decodedLedgerEntry; } + public static LedgerEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -87,6 +95,7 @@ public static LedgerEntry fromXdrBase64(String xdr) throws IOException { public static LedgerEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -172,45 +181,56 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerEntryData decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryData decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntryData decodedLedgerEntryData = new LedgerEntryData(); - LedgerEntryType discriminant = LedgerEntryType.decode(stream); + LedgerEntryType discriminant = LedgerEntryType.decode(stream, maxDepth); decodedLedgerEntryData.setDiscriminant(discriminant); switch (decodedLedgerEntryData.getDiscriminant()) { case ACCOUNT: - decodedLedgerEntryData.account = AccountEntry.decode(stream); + decodedLedgerEntryData.account = AccountEntry.decode(stream, maxDepth); break; case TRUSTLINE: - decodedLedgerEntryData.trustLine = TrustLineEntry.decode(stream); + decodedLedgerEntryData.trustLine = TrustLineEntry.decode(stream, maxDepth); break; case OFFER: - decodedLedgerEntryData.offer = OfferEntry.decode(stream); + decodedLedgerEntryData.offer = OfferEntry.decode(stream, maxDepth); break; case DATA: - decodedLedgerEntryData.data = DataEntry.decode(stream); + decodedLedgerEntryData.data = DataEntry.decode(stream, maxDepth); break; case CLAIMABLE_BALANCE: - decodedLedgerEntryData.claimableBalance = ClaimableBalanceEntry.decode(stream); + decodedLedgerEntryData.claimableBalance = ClaimableBalanceEntry.decode(stream, maxDepth); break; case LIQUIDITY_POOL: - decodedLedgerEntryData.liquidityPool = LiquidityPoolEntry.decode(stream); + decodedLedgerEntryData.liquidityPool = LiquidityPoolEntry.decode(stream, maxDepth); break; case CONTRACT_DATA: - decodedLedgerEntryData.contractData = ContractDataEntry.decode(stream); + decodedLedgerEntryData.contractData = ContractDataEntry.decode(stream, maxDepth); break; case CONTRACT_CODE: - decodedLedgerEntryData.contractCode = ContractCodeEntry.decode(stream); + decodedLedgerEntryData.contractCode = ContractCodeEntry.decode(stream, maxDepth); break; case CONFIG_SETTING: - decodedLedgerEntryData.configSetting = ConfigSettingEntry.decode(stream); + decodedLedgerEntryData.configSetting = ConfigSettingEntry.decode(stream, maxDepth); break; case TTL: - decodedLedgerEntryData.ttl = TTLEntry.decode(stream); + decodedLedgerEntryData.ttl = TTLEntry.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerEntryData; } + public static LedgerEntryData decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntryData fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -219,6 +239,7 @@ public static LedgerEntryData fromXdrBase64(String xdr) throws IOException { public static LedgerEntryData fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -255,7 +276,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerEntryExt decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntryExt decodedLedgerEntryExt = new LedgerEntryExt(); Integer discriminant = stream.readInt(); decodedLedgerEntryExt.setDiscriminant(discriminant); @@ -263,12 +289,18 @@ public static LedgerEntryExt decode(XdrDataInputStream stream) throws IOExceptio case 0: break; case 1: - decodedLedgerEntryExt.v1 = LedgerEntryExtensionV1.decode(stream); + decodedLedgerEntryExt.v1 = LedgerEntryExtensionV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerEntryExt; } + public static LedgerEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -277,6 +309,7 @@ public static LedgerEntryExt fromXdrBase64(String xdr) throws IOException { public static LedgerEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerEntryChange.java b/src/main/java/org/stellar/sdk/xdr/LedgerEntryChange.java index 511452357..d516a605e 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerEntryChange.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerEntryChange.java @@ -63,30 +63,41 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerEntryChange decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryChange decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntryChange decodedLedgerEntryChange = new LedgerEntryChange(); - LedgerEntryChangeType discriminant = LedgerEntryChangeType.decode(stream); + LedgerEntryChangeType discriminant = LedgerEntryChangeType.decode(stream, maxDepth); decodedLedgerEntryChange.setDiscriminant(discriminant); switch (decodedLedgerEntryChange.getDiscriminant()) { case LEDGER_ENTRY_CREATED: - decodedLedgerEntryChange.created = LedgerEntry.decode(stream); + decodedLedgerEntryChange.created = LedgerEntry.decode(stream, maxDepth); break; case LEDGER_ENTRY_UPDATED: - decodedLedgerEntryChange.updated = LedgerEntry.decode(stream); + decodedLedgerEntryChange.updated = LedgerEntry.decode(stream, maxDepth); break; case LEDGER_ENTRY_REMOVED: - decodedLedgerEntryChange.removed = LedgerKey.decode(stream); + decodedLedgerEntryChange.removed = LedgerKey.decode(stream, maxDepth); break; case LEDGER_ENTRY_STATE: - decodedLedgerEntryChange.state = LedgerEntry.decode(stream); + decodedLedgerEntryChange.state = LedgerEntry.decode(stream, maxDepth); break; case LEDGER_ENTRY_RESTORED: - decodedLedgerEntryChange.restored = LedgerEntry.decode(stream); + decodedLedgerEntryChange.restored = LedgerEntry.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerEntryChange; } + public static LedgerEntryChange decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntryChange fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -95,6 +106,7 @@ public static LedgerEntryChange fromXdrBase64(String xdr) throws IOException { public static LedgerEntryChange fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerEntryChangeType.java b/src/main/java/org/stellar/sdk/xdr/LedgerEntryChangeType.java index 74a5058d2..a36b08bf3 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerEntryChangeType.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerEntryChangeType.java @@ -38,7 +38,9 @@ public int getValue() { return value; } - public static LedgerEntryChangeType decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryChangeType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -56,6 +58,10 @@ public static LedgerEntryChangeType decode(XdrDataInputStream stream) throws IOE } } + public static LedgerEntryChangeType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -68,6 +74,7 @@ public static LedgerEntryChangeType fromXdrBase64(String xdr) throws IOException public static LedgerEntryChangeType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerEntryChanges.java b/src/main/java/org/stellar/sdk/xdr/LedgerEntryChanges.java index ee1f918e3..d3bf0e205 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerEntryChanges.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerEntryChanges.java @@ -31,16 +31,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerEntryChanges decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryChanges decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntryChanges decodedLedgerEntryChanges = new LedgerEntryChanges(); int LedgerEntryChangesSize = stream.readInt(); + if (LedgerEntryChangesSize < 0) { + throw new IOException("LedgerEntryChanges size " + LedgerEntryChangesSize + " is negative"); + } + int LedgerEntryChangesRemainingInputLen = stream.getRemainingInputLen(); + if (LedgerEntryChangesRemainingInputLen >= 0 + && LedgerEntryChangesRemainingInputLen < LedgerEntryChangesSize) { + throw new IOException( + "LedgerEntryChanges size " + + LedgerEntryChangesSize + + " exceeds remaining input length " + + LedgerEntryChangesRemainingInputLen); + } decodedLedgerEntryChanges.LedgerEntryChanges = new LedgerEntryChange[LedgerEntryChangesSize]; for (int i = 0; i < LedgerEntryChangesSize; i++) { - decodedLedgerEntryChanges.LedgerEntryChanges[i] = LedgerEntryChange.decode(stream); + decodedLedgerEntryChanges.LedgerEntryChanges[i] = LedgerEntryChange.decode(stream, maxDepth); } return decodedLedgerEntryChanges; } + public static LedgerEntryChanges decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntryChanges fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +70,7 @@ public static LedgerEntryChanges fromXdrBase64(String xdr) throws IOException { public static LedgerEntryChanges fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerEntryExtensionV1.java b/src/main/java/org/stellar/sdk/xdr/LedgerEntryExtensionV1.java index 1161aee40..8e8688156 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerEntryExtensionV1.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerEntryExtensionV1.java @@ -41,13 +41,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static LedgerEntryExtensionV1 decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryExtensionV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntryExtensionV1 decodedLedgerEntryExtensionV1 = new LedgerEntryExtensionV1(); - decodedLedgerEntryExtensionV1.sponsoringID = SponsorshipDescriptor.decode(stream); - decodedLedgerEntryExtensionV1.ext = LedgerEntryExtensionV1Ext.decode(stream); + decodedLedgerEntryExtensionV1.sponsoringID = SponsorshipDescriptor.decode(stream, maxDepth); + decodedLedgerEntryExtensionV1.ext = LedgerEntryExtensionV1Ext.decode(stream, maxDepth); return decodedLedgerEntryExtensionV1; } + public static LedgerEntryExtensionV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntryExtensionV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -56,6 +65,7 @@ public static LedgerEntryExtensionV1 fromXdrBase64(String xdr) throws IOExceptio public static LedgerEntryExtensionV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -85,17 +95,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerEntryExtensionV1Ext decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryExtensionV1Ext decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerEntryExtensionV1Ext decodedLedgerEntryExtensionV1Ext = new LedgerEntryExtensionV1Ext(); Integer discriminant = stream.readInt(); decodedLedgerEntryExtensionV1Ext.setDiscriminant(discriminant); switch (decodedLedgerEntryExtensionV1Ext.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerEntryExtensionV1Ext; } + public static LedgerEntryExtensionV1Ext decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerEntryExtensionV1Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -104,6 +125,7 @@ public static LedgerEntryExtensionV1Ext fromXdrBase64(String xdr) throws IOExcep public static LedgerEntryExtensionV1Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerEntryType.java b/src/main/java/org/stellar/sdk/xdr/LedgerEntryType.java index dee8c4eef..1a81369cc 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerEntryType.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerEntryType.java @@ -48,7 +48,8 @@ public int getValue() { return value; } - public static LedgerEntryType decode(XdrDataInputStream stream) throws IOException { + public static LedgerEntryType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -76,6 +77,10 @@ public static LedgerEntryType decode(XdrDataInputStream stream) throws IOExcepti } } + public static LedgerEntryType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -88,6 +93,7 @@ public static LedgerEntryType fromXdrBase64(String xdr) throws IOException { public static LedgerEntryType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerFootprint.java b/src/main/java/org/stellar/sdk/xdr/LedgerFootprint.java index d1b8adb06..3b80e8740 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerFootprint.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerFootprint.java @@ -43,21 +43,51 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerFootprint decode(XdrDataInputStream stream) throws IOException { + public static LedgerFootprint decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerFootprint decodedLedgerFootprint = new LedgerFootprint(); int readOnlySize = stream.readInt(); + if (readOnlySize < 0) { + throw new IOException("readOnly size " + readOnlySize + " is negative"); + } + int readOnlyRemainingInputLen = stream.getRemainingInputLen(); + if (readOnlyRemainingInputLen >= 0 && readOnlyRemainingInputLen < readOnlySize) { + throw new IOException( + "readOnly size " + + readOnlySize + + " exceeds remaining input length " + + readOnlyRemainingInputLen); + } decodedLedgerFootprint.readOnly = new LedgerKey[readOnlySize]; for (int i = 0; i < readOnlySize; i++) { - decodedLedgerFootprint.readOnly[i] = LedgerKey.decode(stream); + decodedLedgerFootprint.readOnly[i] = LedgerKey.decode(stream, maxDepth); } int readWriteSize = stream.readInt(); + if (readWriteSize < 0) { + throw new IOException("readWrite size " + readWriteSize + " is negative"); + } + int readWriteRemainingInputLen = stream.getRemainingInputLen(); + if (readWriteRemainingInputLen >= 0 && readWriteRemainingInputLen < readWriteSize) { + throw new IOException( + "readWrite size " + + readWriteSize + + " exceeds remaining input length " + + readWriteRemainingInputLen); + } decodedLedgerFootprint.readWrite = new LedgerKey[readWriteSize]; for (int i = 0; i < readWriteSize; i++) { - decodedLedgerFootprint.readWrite[i] = LedgerKey.decode(stream); + decodedLedgerFootprint.readWrite[i] = LedgerKey.decode(stream, maxDepth); } return decodedLedgerFootprint; } + public static LedgerFootprint decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerFootprint fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +96,7 @@ public static LedgerFootprint fromXdrBase64(String xdr) throws IOException { public static LedgerFootprint fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerHeader.java b/src/main/java/org/stellar/sdk/xdr/LedgerHeader.java index 118b014f8..baf382ab9 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerHeader.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerHeader.java @@ -92,36 +92,47 @@ public void encode(XdrDataOutputStream stream) throws IOException { baseReserve.encode(stream); maxTxSetSize.encode(stream); int skipListSize = getSkipList().length; + if (skipListSize != 4) { + throw new IOException("skipList size " + skipListSize + " does not match fixed size 4"); + } for (int i = 0; i < skipListSize; i++) { skipList[i].encode(stream); } ext.encode(stream); } - public static LedgerHeader decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeader decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerHeader decodedLedgerHeader = new LedgerHeader(); - decodedLedgerHeader.ledgerVersion = Uint32.decode(stream); - decodedLedgerHeader.previousLedgerHash = Hash.decode(stream); - decodedLedgerHeader.scpValue = StellarValue.decode(stream); - decodedLedgerHeader.txSetResultHash = Hash.decode(stream); - decodedLedgerHeader.bucketListHash = Hash.decode(stream); - decodedLedgerHeader.ledgerSeq = Uint32.decode(stream); - decodedLedgerHeader.totalCoins = Int64.decode(stream); - decodedLedgerHeader.feePool = Int64.decode(stream); - decodedLedgerHeader.inflationSeq = Uint32.decode(stream); - decodedLedgerHeader.idPool = Uint64.decode(stream); - decodedLedgerHeader.baseFee = Uint32.decode(stream); - decodedLedgerHeader.baseReserve = Uint32.decode(stream); - decodedLedgerHeader.maxTxSetSize = Uint32.decode(stream); + decodedLedgerHeader.ledgerVersion = Uint32.decode(stream, maxDepth); + decodedLedgerHeader.previousLedgerHash = Hash.decode(stream, maxDepth); + decodedLedgerHeader.scpValue = StellarValue.decode(stream, maxDepth); + decodedLedgerHeader.txSetResultHash = Hash.decode(stream, maxDepth); + decodedLedgerHeader.bucketListHash = Hash.decode(stream, maxDepth); + decodedLedgerHeader.ledgerSeq = Uint32.decode(stream, maxDepth); + decodedLedgerHeader.totalCoins = Int64.decode(stream, maxDepth); + decodedLedgerHeader.feePool = Int64.decode(stream, maxDepth); + decodedLedgerHeader.inflationSeq = Uint32.decode(stream, maxDepth); + decodedLedgerHeader.idPool = Uint64.decode(stream, maxDepth); + decodedLedgerHeader.baseFee = Uint32.decode(stream, maxDepth); + decodedLedgerHeader.baseReserve = Uint32.decode(stream, maxDepth); + decodedLedgerHeader.maxTxSetSize = Uint32.decode(stream, maxDepth); int skipListSize = 4; decodedLedgerHeader.skipList = new Hash[skipListSize]; for (int i = 0; i < skipListSize; i++) { - decodedLedgerHeader.skipList[i] = Hash.decode(stream); + decodedLedgerHeader.skipList[i] = Hash.decode(stream, maxDepth); } - decodedLedgerHeader.ext = LedgerHeaderExt.decode(stream); + decodedLedgerHeader.ext = LedgerHeaderExt.decode(stream, maxDepth); return decodedLedgerHeader; } + public static LedgerHeader decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerHeader fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -130,6 +141,7 @@ public static LedgerHeader fromXdrBase64(String xdr) throws IOException { public static LedgerHeader fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -165,7 +177,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerHeaderExt decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeaderExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerHeaderExt decodedLedgerHeaderExt = new LedgerHeaderExt(); Integer discriminant = stream.readInt(); decodedLedgerHeaderExt.setDiscriminant(discriminant); @@ -173,12 +190,18 @@ public static LedgerHeaderExt decode(XdrDataInputStream stream) throws IOExcepti case 0: break; case 1: - decodedLedgerHeaderExt.v1 = LedgerHeaderExtensionV1.decode(stream); + decodedLedgerHeaderExt.v1 = LedgerHeaderExtensionV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerHeaderExt; } + public static LedgerHeaderExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerHeaderExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -187,6 +210,7 @@ public static LedgerHeaderExt fromXdrBase64(String xdr) throws IOException { public static LedgerHeaderExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerHeaderExtensionV1.java b/src/main/java/org/stellar/sdk/xdr/LedgerHeaderExtensionV1.java index 908c6fd04..53514ef53 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerHeaderExtensionV1.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerHeaderExtensionV1.java @@ -41,13 +41,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static LedgerHeaderExtensionV1 decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeaderExtensionV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerHeaderExtensionV1 decodedLedgerHeaderExtensionV1 = new LedgerHeaderExtensionV1(); - decodedLedgerHeaderExtensionV1.flags = Uint32.decode(stream); - decodedLedgerHeaderExtensionV1.ext = LedgerHeaderExtensionV1Ext.decode(stream); + decodedLedgerHeaderExtensionV1.flags = Uint32.decode(stream, maxDepth); + decodedLedgerHeaderExtensionV1.ext = LedgerHeaderExtensionV1Ext.decode(stream, maxDepth); return decodedLedgerHeaderExtensionV1; } + public static LedgerHeaderExtensionV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerHeaderExtensionV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -56,6 +65,7 @@ public static LedgerHeaderExtensionV1 fromXdrBase64(String xdr) throws IOExcepti public static LedgerHeaderExtensionV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -85,7 +95,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerHeaderExtensionV1Ext decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeaderExtensionV1Ext decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerHeaderExtensionV1Ext decodedLedgerHeaderExtensionV1Ext = new LedgerHeaderExtensionV1Ext(); Integer discriminant = stream.readInt(); @@ -93,10 +108,16 @@ public static LedgerHeaderExtensionV1Ext decode(XdrDataInputStream stream) throw switch (decodedLedgerHeaderExtensionV1Ext.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerHeaderExtensionV1Ext; } + public static LedgerHeaderExtensionV1Ext decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerHeaderExtensionV1Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -105,6 +126,7 @@ public static LedgerHeaderExtensionV1Ext fromXdrBase64(String xdr) throws IOExce public static LedgerHeaderExtensionV1Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerHeaderFlags.java b/src/main/java/org/stellar/sdk/xdr/LedgerHeaderFlags.java index a8857e5aa..a61550686 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerHeaderFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerHeaderFlags.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static LedgerHeaderFlags decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeaderFlags decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -48,6 +50,10 @@ public static LedgerHeaderFlags decode(XdrDataInputStream stream) throws IOExcep } } + public static LedgerHeaderFlags decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static LedgerHeaderFlags fromXdrBase64(String xdr) throws IOException { public static LedgerHeaderFlags fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerHeaderHistoryEntry.java b/src/main/java/org/stellar/sdk/xdr/LedgerHeaderHistoryEntry.java index 639e07464..6302ef95a 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerHeaderHistoryEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerHeaderHistoryEntry.java @@ -45,14 +45,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static LedgerHeaderHistoryEntry decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeaderHistoryEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerHeaderHistoryEntry decodedLedgerHeaderHistoryEntry = new LedgerHeaderHistoryEntry(); - decodedLedgerHeaderHistoryEntry.hash = Hash.decode(stream); - decodedLedgerHeaderHistoryEntry.header = LedgerHeader.decode(stream); - decodedLedgerHeaderHistoryEntry.ext = LedgerHeaderHistoryEntryExt.decode(stream); + decodedLedgerHeaderHistoryEntry.hash = Hash.decode(stream, maxDepth); + decodedLedgerHeaderHistoryEntry.header = LedgerHeader.decode(stream, maxDepth); + decodedLedgerHeaderHistoryEntry.ext = LedgerHeaderHistoryEntryExt.decode(stream, maxDepth); return decodedLedgerHeaderHistoryEntry; } + public static LedgerHeaderHistoryEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerHeaderHistoryEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -61,6 +70,7 @@ public static LedgerHeaderHistoryEntry fromXdrBase64(String xdr) throws IOExcept public static LedgerHeaderHistoryEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -90,7 +100,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerHeaderHistoryEntryExt decode(XdrDataInputStream stream) throws IOException { + public static LedgerHeaderHistoryEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerHeaderHistoryEntryExt decodedLedgerHeaderHistoryEntryExt = new LedgerHeaderHistoryEntryExt(); Integer discriminant = stream.readInt(); @@ -98,10 +113,16 @@ public static LedgerHeaderHistoryEntryExt decode(XdrDataInputStream stream) thro switch (decodedLedgerHeaderHistoryEntryExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerHeaderHistoryEntryExt; } + public static LedgerHeaderHistoryEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerHeaderHistoryEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -110,6 +131,7 @@ public static LedgerHeaderHistoryEntryExt fromXdrBase64(String xdr) throws IOExc public static LedgerHeaderHistoryEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerKey.java b/src/main/java/org/stellar/sdk/xdr/LedgerKey.java index cf7edff6d..4b5104e2b 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerKey.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerKey.java @@ -134,45 +134,55 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerKey decode(XdrDataInputStream stream) throws IOException { + public static LedgerKey decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKey decodedLedgerKey = new LedgerKey(); - LedgerEntryType discriminant = LedgerEntryType.decode(stream); + LedgerEntryType discriminant = LedgerEntryType.decode(stream, maxDepth); decodedLedgerKey.setDiscriminant(discriminant); switch (decodedLedgerKey.getDiscriminant()) { case ACCOUNT: - decodedLedgerKey.account = LedgerKeyAccount.decode(stream); + decodedLedgerKey.account = LedgerKeyAccount.decode(stream, maxDepth); break; case TRUSTLINE: - decodedLedgerKey.trustLine = LedgerKeyTrustLine.decode(stream); + decodedLedgerKey.trustLine = LedgerKeyTrustLine.decode(stream, maxDepth); break; case OFFER: - decodedLedgerKey.offer = LedgerKeyOffer.decode(stream); + decodedLedgerKey.offer = LedgerKeyOffer.decode(stream, maxDepth); break; case DATA: - decodedLedgerKey.data = LedgerKeyData.decode(stream); + decodedLedgerKey.data = LedgerKeyData.decode(stream, maxDepth); break; case CLAIMABLE_BALANCE: - decodedLedgerKey.claimableBalance = LedgerKeyClaimableBalance.decode(stream); + decodedLedgerKey.claimableBalance = LedgerKeyClaimableBalance.decode(stream, maxDepth); break; case LIQUIDITY_POOL: - decodedLedgerKey.liquidityPool = LedgerKeyLiquidityPool.decode(stream); + decodedLedgerKey.liquidityPool = LedgerKeyLiquidityPool.decode(stream, maxDepth); break; case CONTRACT_DATA: - decodedLedgerKey.contractData = LedgerKeyContractData.decode(stream); + decodedLedgerKey.contractData = LedgerKeyContractData.decode(stream, maxDepth); break; case CONTRACT_CODE: - decodedLedgerKey.contractCode = LedgerKeyContractCode.decode(stream); + decodedLedgerKey.contractCode = LedgerKeyContractCode.decode(stream, maxDepth); break; case CONFIG_SETTING: - decodedLedgerKey.configSetting = LedgerKeyConfigSetting.decode(stream); + decodedLedgerKey.configSetting = LedgerKeyConfigSetting.decode(stream, maxDepth); break; case TTL: - decodedLedgerKey.ttl = LedgerKeyTtl.decode(stream); + decodedLedgerKey.ttl = LedgerKeyTtl.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerKey; } + public static LedgerKey decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKey fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -181,6 +191,7 @@ public static LedgerKey fromXdrBase64(String xdr) throws IOException { public static LedgerKey fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -205,12 +216,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { accountID.encode(stream); } - public static LedgerKeyAccount decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyAccount decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyAccount decodedLedgerKeyAccount = new LedgerKeyAccount(); - decodedLedgerKeyAccount.accountID = AccountID.decode(stream); + decodedLedgerKeyAccount.accountID = AccountID.decode(stream, maxDepth); return decodedLedgerKeyAccount; } + public static LedgerKeyAccount decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyAccount fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -219,6 +239,7 @@ public static LedgerKeyAccount fromXdrBase64(String xdr) throws IOException { public static LedgerKeyAccount fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -247,13 +268,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { asset.encode(stream); } - public static LedgerKeyTrustLine decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyTrustLine decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyTrustLine decodedLedgerKeyTrustLine = new LedgerKeyTrustLine(); - decodedLedgerKeyTrustLine.accountID = AccountID.decode(stream); - decodedLedgerKeyTrustLine.asset = TrustLineAsset.decode(stream); + decodedLedgerKeyTrustLine.accountID = AccountID.decode(stream, maxDepth); + decodedLedgerKeyTrustLine.asset = TrustLineAsset.decode(stream, maxDepth); return decodedLedgerKeyTrustLine; } + public static LedgerKeyTrustLine decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyTrustLine fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -262,6 +292,7 @@ public static LedgerKeyTrustLine fromXdrBase64(String xdr) throws IOException { public static LedgerKeyTrustLine fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -290,13 +321,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { offerID.encode(stream); } - public static LedgerKeyOffer decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyOffer decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyOffer decodedLedgerKeyOffer = new LedgerKeyOffer(); - decodedLedgerKeyOffer.sellerID = AccountID.decode(stream); - decodedLedgerKeyOffer.offerID = Int64.decode(stream); + decodedLedgerKeyOffer.sellerID = AccountID.decode(stream, maxDepth); + decodedLedgerKeyOffer.offerID = Int64.decode(stream, maxDepth); return decodedLedgerKeyOffer; } + public static LedgerKeyOffer decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyOffer fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -305,6 +345,7 @@ public static LedgerKeyOffer fromXdrBase64(String xdr) throws IOException { public static LedgerKeyOffer fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -333,13 +374,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { dataName.encode(stream); } - public static LedgerKeyData decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyData decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyData decodedLedgerKeyData = new LedgerKeyData(); - decodedLedgerKeyData.accountID = AccountID.decode(stream); - decodedLedgerKeyData.dataName = String64.decode(stream); + decodedLedgerKeyData.accountID = AccountID.decode(stream, maxDepth); + decodedLedgerKeyData.dataName = String64.decode(stream, maxDepth); return decodedLedgerKeyData; } + public static LedgerKeyData decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyData fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -348,6 +397,7 @@ public static LedgerKeyData fromXdrBase64(String xdr) throws IOException { public static LedgerKeyData fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -373,12 +423,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { balanceID.encode(stream); } - public static LedgerKeyClaimableBalance decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyClaimableBalance decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyClaimableBalance decodedLedgerKeyClaimableBalance = new LedgerKeyClaimableBalance(); - decodedLedgerKeyClaimableBalance.balanceID = ClaimableBalanceID.decode(stream); + decodedLedgerKeyClaimableBalance.balanceID = ClaimableBalanceID.decode(stream, maxDepth); return decodedLedgerKeyClaimableBalance; } + public static LedgerKeyClaimableBalance decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyClaimableBalance fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -387,6 +446,7 @@ public static LedgerKeyClaimableBalance fromXdrBase64(String xdr) throws IOExcep public static LedgerKeyClaimableBalance fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -412,12 +472,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { liquidityPoolID.encode(stream); } - public static LedgerKeyLiquidityPool decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyLiquidityPool decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyLiquidityPool decodedLedgerKeyLiquidityPool = new LedgerKeyLiquidityPool(); - decodedLedgerKeyLiquidityPool.liquidityPoolID = PoolID.decode(stream); + decodedLedgerKeyLiquidityPool.liquidityPoolID = PoolID.decode(stream, maxDepth); return decodedLedgerKeyLiquidityPool; } + public static LedgerKeyLiquidityPool decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyLiquidityPool fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -426,6 +495,7 @@ public static LedgerKeyLiquidityPool fromXdrBase64(String xdr) throws IOExceptio public static LedgerKeyLiquidityPool fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -457,14 +527,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { durability.encode(stream); } - public static LedgerKeyContractData decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyContractData decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyContractData decodedLedgerKeyContractData = new LedgerKeyContractData(); - decodedLedgerKeyContractData.contract = SCAddress.decode(stream); - decodedLedgerKeyContractData.key = SCVal.decode(stream); - decodedLedgerKeyContractData.durability = ContractDataDurability.decode(stream); + decodedLedgerKeyContractData.contract = SCAddress.decode(stream, maxDepth); + decodedLedgerKeyContractData.key = SCVal.decode(stream, maxDepth); + decodedLedgerKeyContractData.durability = ContractDataDurability.decode(stream, maxDepth); return decodedLedgerKeyContractData; } + public static LedgerKeyContractData decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyContractData fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -473,6 +552,7 @@ public static LedgerKeyContractData fromXdrBase64(String xdr) throws IOException public static LedgerKeyContractData fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -498,12 +578,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { hash.encode(stream); } - public static LedgerKeyContractCode decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyContractCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyContractCode decodedLedgerKeyContractCode = new LedgerKeyContractCode(); - decodedLedgerKeyContractCode.hash = Hash.decode(stream); + decodedLedgerKeyContractCode.hash = Hash.decode(stream, maxDepth); return decodedLedgerKeyContractCode; } + public static LedgerKeyContractCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyContractCode fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -512,6 +601,7 @@ public static LedgerKeyContractCode fromXdrBase64(String xdr) throws IOException public static LedgerKeyContractCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -537,12 +627,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { configSettingID.encode(stream); } - public static LedgerKeyConfigSetting decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyConfigSetting decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyConfigSetting decodedLedgerKeyConfigSetting = new LedgerKeyConfigSetting(); - decodedLedgerKeyConfigSetting.configSettingID = ConfigSettingID.decode(stream); + decodedLedgerKeyConfigSetting.configSettingID = ConfigSettingID.decode(stream, maxDepth); return decodedLedgerKeyConfigSetting; } + public static LedgerKeyConfigSetting decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyConfigSetting fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -551,6 +650,7 @@ public static LedgerKeyConfigSetting fromXdrBase64(String xdr) throws IOExceptio public static LedgerKeyConfigSetting fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -577,12 +677,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { keyHash.encode(stream); } - public static LedgerKeyTtl decode(XdrDataInputStream stream) throws IOException { + public static LedgerKeyTtl decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerKeyTtl decodedLedgerKeyTtl = new LedgerKeyTtl(); - decodedLedgerKeyTtl.keyHash = Hash.decode(stream); + decodedLedgerKeyTtl.keyHash = Hash.decode(stream, maxDepth); return decodedLedgerKeyTtl; } + public static LedgerKeyTtl decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerKeyTtl fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -591,6 +699,7 @@ public static LedgerKeyTtl fromXdrBase64(String xdr) throws IOException { public static LedgerKeyTtl fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerSCPMessages.java b/src/main/java/org/stellar/sdk/xdr/LedgerSCPMessages.java index eb460bca3..b316ac74b 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerSCPMessages.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerSCPMessages.java @@ -39,17 +39,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerSCPMessages decode(XdrDataInputStream stream) throws IOException { + public static LedgerSCPMessages decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerSCPMessages decodedLedgerSCPMessages = new LedgerSCPMessages(); - decodedLedgerSCPMessages.ledgerSeq = Uint32.decode(stream); + decodedLedgerSCPMessages.ledgerSeq = Uint32.decode(stream, maxDepth); int messagesSize = stream.readInt(); + if (messagesSize < 0) { + throw new IOException("messages size " + messagesSize + " is negative"); + } + int messagesRemainingInputLen = stream.getRemainingInputLen(); + if (messagesRemainingInputLen >= 0 && messagesRemainingInputLen < messagesSize) { + throw new IOException( + "messages size " + + messagesSize + + " exceeds remaining input length " + + messagesRemainingInputLen); + } decodedLedgerSCPMessages.messages = new SCPEnvelope[messagesSize]; for (int i = 0; i < messagesSize; i++) { - decodedLedgerSCPMessages.messages[i] = SCPEnvelope.decode(stream); + decodedLedgerSCPMessages.messages[i] = SCPEnvelope.decode(stream, maxDepth); } return decodedLedgerSCPMessages; } + public static LedgerSCPMessages decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerSCPMessages fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +78,7 @@ public static LedgerSCPMessages fromXdrBase64(String xdr) throws IOException { public static LedgerSCPMessages fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerUpgrade.java b/src/main/java/org/stellar/sdk/xdr/LedgerUpgrade.java index 511148ef5..68fb018be 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerUpgrade.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerUpgrade.java @@ -78,36 +78,46 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LedgerUpgrade decode(XdrDataInputStream stream) throws IOException { + public static LedgerUpgrade decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LedgerUpgrade decodedLedgerUpgrade = new LedgerUpgrade(); - LedgerUpgradeType discriminant = LedgerUpgradeType.decode(stream); + LedgerUpgradeType discriminant = LedgerUpgradeType.decode(stream, maxDepth); decodedLedgerUpgrade.setDiscriminant(discriminant); switch (decodedLedgerUpgrade.getDiscriminant()) { case LEDGER_UPGRADE_VERSION: - decodedLedgerUpgrade.newLedgerVersion = Uint32.decode(stream); + decodedLedgerUpgrade.newLedgerVersion = Uint32.decode(stream, maxDepth); break; case LEDGER_UPGRADE_BASE_FEE: - decodedLedgerUpgrade.newBaseFee = Uint32.decode(stream); + decodedLedgerUpgrade.newBaseFee = Uint32.decode(stream, maxDepth); break; case LEDGER_UPGRADE_MAX_TX_SET_SIZE: - decodedLedgerUpgrade.newMaxTxSetSize = Uint32.decode(stream); + decodedLedgerUpgrade.newMaxTxSetSize = Uint32.decode(stream, maxDepth); break; case LEDGER_UPGRADE_BASE_RESERVE: - decodedLedgerUpgrade.newBaseReserve = Uint32.decode(stream); + decodedLedgerUpgrade.newBaseReserve = Uint32.decode(stream, maxDepth); break; case LEDGER_UPGRADE_FLAGS: - decodedLedgerUpgrade.newFlags = Uint32.decode(stream); + decodedLedgerUpgrade.newFlags = Uint32.decode(stream, maxDepth); break; case LEDGER_UPGRADE_CONFIG: - decodedLedgerUpgrade.newConfig = ConfigUpgradeSetKey.decode(stream); + decodedLedgerUpgrade.newConfig = ConfigUpgradeSetKey.decode(stream, maxDepth); break; case LEDGER_UPGRADE_MAX_SOROBAN_TX_SET_SIZE: - decodedLedgerUpgrade.newMaxSorobanTxSetSize = Uint32.decode(stream); + decodedLedgerUpgrade.newMaxSorobanTxSetSize = Uint32.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLedgerUpgrade; } + public static LedgerUpgrade decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LedgerUpgrade fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -116,6 +126,7 @@ public static LedgerUpgrade fromXdrBase64(String xdr) throws IOException { public static LedgerUpgrade fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LedgerUpgradeType.java b/src/main/java/org/stellar/sdk/xdr/LedgerUpgradeType.java index 79b8a3166..250cf4f68 100644 --- a/src/main/java/org/stellar/sdk/xdr/LedgerUpgradeType.java +++ b/src/main/java/org/stellar/sdk/xdr/LedgerUpgradeType.java @@ -42,7 +42,9 @@ public int getValue() { return value; } - public static LedgerUpgradeType decode(XdrDataInputStream stream) throws IOException { + public static LedgerUpgradeType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -64,6 +66,10 @@ public static LedgerUpgradeType decode(XdrDataInputStream stream) throws IOExcep } } + public static LedgerUpgradeType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -76,6 +82,7 @@ public static LedgerUpgradeType fromXdrBase64(String xdr) throws IOException { public static LedgerUpgradeType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Liabilities.java b/src/main/java/org/stellar/sdk/xdr/Liabilities.java index e78888ad1..a132eb3fe 100644 --- a/src/main/java/org/stellar/sdk/xdr/Liabilities.java +++ b/src/main/java/org/stellar/sdk/xdr/Liabilities.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { selling.encode(stream); } - public static Liabilities decode(XdrDataInputStream stream) throws IOException { + public static Liabilities decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Liabilities decodedLiabilities = new Liabilities(); - decodedLiabilities.buying = Int64.decode(stream); - decodedLiabilities.selling = Int64.decode(stream); + decodedLiabilities.buying = Int64.decode(stream, maxDepth); + decodedLiabilities.selling = Int64.decode(stream, maxDepth); return decodedLiabilities; } + public static Liabilities decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Liabilities fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static Liabilities fromXdrBase64(String xdr) throws IOException { public static Liabilities fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolConstantProductParameters.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolConstantProductParameters.java index 7f888c986..c5d65a32d 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolConstantProductParameters.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolConstantProductParameters.java @@ -38,16 +38,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { fee.encode(stream); } - public static LiquidityPoolConstantProductParameters decode(XdrDataInputStream stream) - throws IOException { + public static LiquidityPoolConstantProductParameters decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolConstantProductParameters decodedLiquidityPoolConstantProductParameters = new LiquidityPoolConstantProductParameters(); - decodedLiquidityPoolConstantProductParameters.assetA = Asset.decode(stream); - decodedLiquidityPoolConstantProductParameters.assetB = Asset.decode(stream); - decodedLiquidityPoolConstantProductParameters.fee = Int32.decode(stream); + decodedLiquidityPoolConstantProductParameters.assetA = Asset.decode(stream, maxDepth); + decodedLiquidityPoolConstantProductParameters.assetB = Asset.decode(stream, maxDepth); + decodedLiquidityPoolConstantProductParameters.fee = Int32.decode(stream, maxDepth); return decodedLiquidityPoolConstantProductParameters; } + public static LiquidityPoolConstantProductParameters decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolConstantProductParameters fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -58,6 +67,7 @@ public static LiquidityPoolConstantProductParameters fromXdrByteArray(byte[] xdr throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositOp.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositOp.java index f683e1924..c38669a67 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositOp.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositOp.java @@ -44,16 +44,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { maxPrice.encode(stream); } - public static LiquidityPoolDepositOp decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolDepositOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolDepositOp decodedLiquidityPoolDepositOp = new LiquidityPoolDepositOp(); - decodedLiquidityPoolDepositOp.liquidityPoolID = PoolID.decode(stream); - decodedLiquidityPoolDepositOp.maxAmountA = Int64.decode(stream); - decodedLiquidityPoolDepositOp.maxAmountB = Int64.decode(stream); - decodedLiquidityPoolDepositOp.minPrice = Price.decode(stream); - decodedLiquidityPoolDepositOp.maxPrice = Price.decode(stream); + decodedLiquidityPoolDepositOp.liquidityPoolID = PoolID.decode(stream, maxDepth); + decodedLiquidityPoolDepositOp.maxAmountA = Int64.decode(stream, maxDepth); + decodedLiquidityPoolDepositOp.maxAmountB = Int64.decode(stream, maxDepth); + decodedLiquidityPoolDepositOp.minPrice = Price.decode(stream, maxDepth); + decodedLiquidityPoolDepositOp.maxPrice = Price.decode(stream, maxDepth); return decodedLiquidityPoolDepositOp; } + public static LiquidityPoolDepositOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolDepositOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +71,7 @@ public static LiquidityPoolDepositOp fromXdrBase64(String xdr) throws IOExceptio public static LiquidityPoolDepositOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResult.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResult.java index 4c74394cb..7f9114bfb 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResult.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResult.java @@ -53,9 +53,15 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LiquidityPoolDepositResult decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolDepositResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolDepositResult decodedLiquidityPoolDepositResult = new LiquidityPoolDepositResult(); - LiquidityPoolDepositResultCode discriminant = LiquidityPoolDepositResultCode.decode(stream); + LiquidityPoolDepositResultCode discriminant = + LiquidityPoolDepositResultCode.decode(stream, maxDepth); decodedLiquidityPoolDepositResult.setDiscriminant(discriminant); switch (decodedLiquidityPoolDepositResult.getDiscriminant()) { case LIQUIDITY_POOL_DEPOSIT_SUCCESS: @@ -68,10 +74,16 @@ public static LiquidityPoolDepositResult decode(XdrDataInputStream stream) throw case LIQUIDITY_POOL_DEPOSIT_BAD_PRICE: case LIQUIDITY_POOL_DEPOSIT_POOL_FULL: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLiquidityPoolDepositResult; } + public static LiquidityPoolDepositResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolDepositResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -80,6 +92,7 @@ public static LiquidityPoolDepositResult fromXdrBase64(String xdr) throws IOExce public static LiquidityPoolDepositResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResultCode.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResultCode.java index 818aae13c..ecd71ddd5 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolDepositResultCode.java @@ -51,8 +51,9 @@ public int getValue() { return value; } - public static LiquidityPoolDepositResultCode decode(XdrDataInputStream stream) + public static LiquidityPoolDepositResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -76,6 +77,11 @@ public static LiquidityPoolDepositResultCode decode(XdrDataInputStream stream) } } + public static LiquidityPoolDepositResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -88,6 +94,7 @@ public static LiquidityPoolDepositResultCode fromXdrBase64(String xdr) throws IO public static LiquidityPoolDepositResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolEntry.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolEntry.java index b8122bdd5..c6964c501 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolEntry.java @@ -50,13 +50,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { body.encode(stream); } - public static LiquidityPoolEntry decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolEntry decodedLiquidityPoolEntry = new LiquidityPoolEntry(); - decodedLiquidityPoolEntry.liquidityPoolID = PoolID.decode(stream); - decodedLiquidityPoolEntry.body = LiquidityPoolEntryBody.decode(stream); + decodedLiquidityPoolEntry.liquidityPoolID = PoolID.decode(stream, maxDepth); + decodedLiquidityPoolEntry.body = LiquidityPoolEntryBody.decode(stream, maxDepth); return decodedLiquidityPoolEntry; } + public static LiquidityPoolEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +74,7 @@ public static LiquidityPoolEntry fromXdrBase64(String xdr) throws IOException { public static LiquidityPoolEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -105,19 +115,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LiquidityPoolEntryBody decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolEntryBody decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolEntryBody decodedLiquidityPoolEntryBody = new LiquidityPoolEntryBody(); - LiquidityPoolType discriminant = LiquidityPoolType.decode(stream); + LiquidityPoolType discriminant = LiquidityPoolType.decode(stream, maxDepth); decodedLiquidityPoolEntryBody.setDiscriminant(discriminant); switch (decodedLiquidityPoolEntryBody.getDiscriminant()) { case LIQUIDITY_POOL_CONSTANT_PRODUCT: decodedLiquidityPoolEntryBody.constantProduct = - LiquidityPoolEntryConstantProduct.decode(stream); + LiquidityPoolEntryConstantProduct.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLiquidityPoolEntryBody; } + public static LiquidityPoolEntryBody decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolEntryBody fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -126,6 +147,7 @@ public static LiquidityPoolEntryBody fromXdrBase64(String xdr) throws IOExceptio public static LiquidityPoolEntryBody fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -164,19 +186,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { poolSharesTrustLineCount.encode(stream); } - public static LiquidityPoolEntryConstantProduct decode(XdrDataInputStream stream) - throws IOException { + public static LiquidityPoolEntryConstantProduct decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolEntryConstantProduct decodedLiquidityPoolEntryConstantProduct = new LiquidityPoolEntryConstantProduct(); decodedLiquidityPoolEntryConstantProduct.params = - LiquidityPoolConstantProductParameters.decode(stream); - decodedLiquidityPoolEntryConstantProduct.reserveA = Int64.decode(stream); - decodedLiquidityPoolEntryConstantProduct.reserveB = Int64.decode(stream); - decodedLiquidityPoolEntryConstantProduct.totalPoolShares = Int64.decode(stream); - decodedLiquidityPoolEntryConstantProduct.poolSharesTrustLineCount = Int64.decode(stream); + LiquidityPoolConstantProductParameters.decode(stream, maxDepth); + decodedLiquidityPoolEntryConstantProduct.reserveA = Int64.decode(stream, maxDepth); + decodedLiquidityPoolEntryConstantProduct.reserveB = Int64.decode(stream, maxDepth); + decodedLiquidityPoolEntryConstantProduct.totalPoolShares = Int64.decode(stream, maxDepth); + decodedLiquidityPoolEntryConstantProduct.poolSharesTrustLineCount = + Int64.decode(stream, maxDepth); return decodedLiquidityPoolEntryConstantProduct; } + public static LiquidityPoolEntryConstantProduct decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolEntryConstantProduct fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -186,6 +218,7 @@ public static LiquidityPoolEntryConstantProduct fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolParameters.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolParameters.java index 3d2a2c5d2..a44f9ddf8 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolParameters.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolParameters.java @@ -39,19 +39,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LiquidityPoolParameters decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolParameters decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolParameters decodedLiquidityPoolParameters = new LiquidityPoolParameters(); - LiquidityPoolType discriminant = LiquidityPoolType.decode(stream); + LiquidityPoolType discriminant = LiquidityPoolType.decode(stream, maxDepth); decodedLiquidityPoolParameters.setDiscriminant(discriminant); switch (decodedLiquidityPoolParameters.getDiscriminant()) { case LIQUIDITY_POOL_CONSTANT_PRODUCT: decodedLiquidityPoolParameters.constantProduct = - LiquidityPoolConstantProductParameters.decode(stream); + LiquidityPoolConstantProductParameters.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLiquidityPoolParameters; } + public static LiquidityPoolParameters decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolParameters fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +71,7 @@ public static LiquidityPoolParameters fromXdrBase64(String xdr) throws IOExcepti public static LiquidityPoolParameters fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolType.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolType.java index a6faf7726..5cd7a9a7f 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolType.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolType.java @@ -30,7 +30,9 @@ public int getValue() { return value; } - public static LiquidityPoolType decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -40,6 +42,10 @@ public static LiquidityPoolType decode(XdrDataInputStream stream) throws IOExcep } } + public static LiquidityPoolType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +58,7 @@ public static LiquidityPoolType fromXdrBase64(String xdr) throws IOException { public static LiquidityPoolType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawOp.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawOp.java index f1b078314..fd774c4ae 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawOp.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawOp.java @@ -41,15 +41,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { minAmountB.encode(stream); } - public static LiquidityPoolWithdrawOp decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolWithdrawOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolWithdrawOp decodedLiquidityPoolWithdrawOp = new LiquidityPoolWithdrawOp(); - decodedLiquidityPoolWithdrawOp.liquidityPoolID = PoolID.decode(stream); - decodedLiquidityPoolWithdrawOp.amount = Int64.decode(stream); - decodedLiquidityPoolWithdrawOp.minAmountA = Int64.decode(stream); - decodedLiquidityPoolWithdrawOp.minAmountB = Int64.decode(stream); + decodedLiquidityPoolWithdrawOp.liquidityPoolID = PoolID.decode(stream, maxDepth); + decodedLiquidityPoolWithdrawOp.amount = Int64.decode(stream, maxDepth); + decodedLiquidityPoolWithdrawOp.minAmountA = Int64.decode(stream, maxDepth); + decodedLiquidityPoolWithdrawOp.minAmountB = Int64.decode(stream, maxDepth); return decodedLiquidityPoolWithdrawOp; } + public static LiquidityPoolWithdrawOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolWithdrawOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +67,7 @@ public static LiquidityPoolWithdrawOp fromXdrBase64(String xdr) throws IOExcepti public static LiquidityPoolWithdrawOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResult.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResult.java index 145243fc3..a949b6764 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResult.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResult.java @@ -49,10 +49,16 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static LiquidityPoolWithdrawResult decode(XdrDataInputStream stream) throws IOException { + public static LiquidityPoolWithdrawResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; LiquidityPoolWithdrawResult decodedLiquidityPoolWithdrawResult = new LiquidityPoolWithdrawResult(); - LiquidityPoolWithdrawResultCode discriminant = LiquidityPoolWithdrawResultCode.decode(stream); + LiquidityPoolWithdrawResultCode discriminant = + LiquidityPoolWithdrawResultCode.decode(stream, maxDepth); decodedLiquidityPoolWithdrawResult.setDiscriminant(discriminant); switch (decodedLiquidityPoolWithdrawResult.getDiscriminant()) { case LIQUIDITY_POOL_WITHDRAW_SUCCESS: @@ -63,10 +69,16 @@ public static LiquidityPoolWithdrawResult decode(XdrDataInputStream stream) thro case LIQUIDITY_POOL_WITHDRAW_LINE_FULL: case LIQUIDITY_POOL_WITHDRAW_UNDER_MINIMUM: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedLiquidityPoolWithdrawResult; } + public static LiquidityPoolWithdrawResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static LiquidityPoolWithdrawResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -75,6 +87,7 @@ public static LiquidityPoolWithdrawResult fromXdrBase64(String xdr) throws IOExc public static LiquidityPoolWithdrawResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResultCode.java b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResultCode.java index 43e30cfa4..317030b18 100644 --- a/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/LiquidityPoolWithdrawResultCode.java @@ -46,8 +46,9 @@ public int getValue() { return value; } - public static LiquidityPoolWithdrawResultCode decode(XdrDataInputStream stream) + public static LiquidityPoolWithdrawResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -67,6 +68,11 @@ public static LiquidityPoolWithdrawResultCode decode(XdrDataInputStream stream) } } + public static LiquidityPoolWithdrawResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -79,6 +85,7 @@ public static LiquidityPoolWithdrawResultCode fromXdrBase64(String xdr) throws I public static LiquidityPoolWithdrawResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferOp.java b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferOp.java index d576e0455..d56f49e8e 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferOp.java @@ -47,16 +47,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { offerID.encode(stream); } - public static ManageBuyOfferOp decode(XdrDataInputStream stream) throws IOException { + public static ManageBuyOfferOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageBuyOfferOp decodedManageBuyOfferOp = new ManageBuyOfferOp(); - decodedManageBuyOfferOp.selling = Asset.decode(stream); - decodedManageBuyOfferOp.buying = Asset.decode(stream); - decodedManageBuyOfferOp.buyAmount = Int64.decode(stream); - decodedManageBuyOfferOp.price = Price.decode(stream); - decodedManageBuyOfferOp.offerID = Int64.decode(stream); + decodedManageBuyOfferOp.selling = Asset.decode(stream, maxDepth); + decodedManageBuyOfferOp.buying = Asset.decode(stream, maxDepth); + decodedManageBuyOfferOp.buyAmount = Int64.decode(stream, maxDepth); + decodedManageBuyOfferOp.price = Price.decode(stream, maxDepth); + decodedManageBuyOfferOp.offerID = Int64.decode(stream, maxDepth); return decodedManageBuyOfferOp; } + public static ManageBuyOfferOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageBuyOfferOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +74,7 @@ public static ManageBuyOfferOp fromXdrBase64(String xdr) throws IOException { public static ManageBuyOfferOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResult.java b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResult.java index 46fd5abf5..ce789c076 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResult.java @@ -65,13 +65,18 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ManageBuyOfferResult decode(XdrDataInputStream stream) throws IOException { + public static ManageBuyOfferResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageBuyOfferResult decodedManageBuyOfferResult = new ManageBuyOfferResult(); - ManageBuyOfferResultCode discriminant = ManageBuyOfferResultCode.decode(stream); + ManageBuyOfferResultCode discriminant = ManageBuyOfferResultCode.decode(stream, maxDepth); decodedManageBuyOfferResult.setDiscriminant(discriminant); switch (decodedManageBuyOfferResult.getDiscriminant()) { case MANAGE_BUY_OFFER_SUCCESS: - decodedManageBuyOfferResult.success = ManageOfferSuccessResult.decode(stream); + decodedManageBuyOfferResult.success = ManageOfferSuccessResult.decode(stream, maxDepth); break; case MANAGE_BUY_OFFER_MALFORMED: case MANAGE_BUY_OFFER_SELL_NO_TRUST: @@ -86,10 +91,16 @@ public static ManageBuyOfferResult decode(XdrDataInputStream stream) throws IOEx case MANAGE_BUY_OFFER_NOT_FOUND: case MANAGE_BUY_OFFER_LOW_RESERVE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedManageBuyOfferResult; } + public static ManageBuyOfferResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageBuyOfferResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -98,6 +109,7 @@ public static ManageBuyOfferResult fromXdrBase64(String xdr) throws IOException public static ManageBuyOfferResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java index fb443252e..5b5066c53 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageBuyOfferResultCode.java @@ -61,7 +61,9 @@ public int getValue() { return value; } - public static ManageBuyOfferResultCode decode(XdrDataInputStream stream) throws IOException { + public static ManageBuyOfferResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -95,6 +97,10 @@ public static ManageBuyOfferResultCode decode(XdrDataInputStream stream) throws } } + public static ManageBuyOfferResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -107,6 +113,7 @@ public static ManageBuyOfferResultCode fromXdrBase64(String xdr) throws IOExcept public static ManageBuyOfferResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageDataOp.java b/src/main/java/org/stellar/sdk/xdr/ManageDataOp.java index f98154073..baa998c01 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageDataOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageDataOp.java @@ -40,16 +40,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ManageDataOp decode(XdrDataInputStream stream) throws IOException { + public static ManageDataOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageDataOp decodedManageDataOp = new ManageDataOp(); - decodedManageDataOp.dataName = String64.decode(stream); - int dataValuePresent = stream.readInt(); - if (dataValuePresent != 0) { - decodedManageDataOp.dataValue = DataValue.decode(stream); + decodedManageDataOp.dataName = String64.decode(stream, maxDepth); + boolean dataValuePresent = stream.readXdrBoolean(); + if (dataValuePresent) { + decodedManageDataOp.dataValue = DataValue.decode(stream, maxDepth); } return decodedManageDataOp; } + public static ManageDataOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageDataOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +66,7 @@ public static ManageDataOp fromXdrBase64(String xdr) throws IOException { public static ManageDataOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageDataResult.java b/src/main/java/org/stellar/sdk/xdr/ManageDataResult.java index a1576a8bd..47c49eae3 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageDataResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageDataResult.java @@ -47,9 +47,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ManageDataResult decode(XdrDataInputStream stream) throws IOException { + public static ManageDataResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageDataResult decodedManageDataResult = new ManageDataResult(); - ManageDataResultCode discriminant = ManageDataResultCode.decode(stream); + ManageDataResultCode discriminant = ManageDataResultCode.decode(stream, maxDepth); decodedManageDataResult.setDiscriminant(discriminant); switch (decodedManageDataResult.getDiscriminant()) { case MANAGE_DATA_SUCCESS: @@ -59,10 +64,16 @@ public static ManageDataResult decode(XdrDataInputStream stream) throws IOExcept case MANAGE_DATA_LOW_RESERVE: case MANAGE_DATA_INVALID_NAME: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedManageDataResult; } + public static ManageDataResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageDataResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -71,6 +82,7 @@ public static ManageDataResult fromXdrBase64(String xdr) throws IOException { public static ManageDataResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageDataResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageDataResultCode.java index 6c44fc5da..d76db8bf9 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageDataResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageDataResultCode.java @@ -42,7 +42,9 @@ public int getValue() { return value; } - public static ManageDataResultCode decode(XdrDataInputStream stream) throws IOException { + public static ManageDataResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -60,6 +62,10 @@ public static ManageDataResultCode decode(XdrDataInputStream stream) throws IOEx } } + public static ManageDataResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -72,6 +78,7 @@ public static ManageDataResultCode fromXdrBase64(String xdr) throws IOException public static ManageDataResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageOfferEffect.java b/src/main/java/org/stellar/sdk/xdr/ManageOfferEffect.java index dcabe6331..14f38c88a 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageOfferEffect.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageOfferEffect.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static ManageOfferEffect decode(XdrDataInputStream stream) throws IOException { + public static ManageOfferEffect decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +50,10 @@ public static ManageOfferEffect decode(XdrDataInputStream stream) throws IOExcep } } + public static ManageOfferEffect decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static ManageOfferEffect fromXdrBase64(String xdr) throws IOException { public static ManageOfferEffect fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageOfferSuccessResult.java b/src/main/java/org/stellar/sdk/xdr/ManageOfferSuccessResult.java index 85a0acda7..208a8dc5a 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageOfferSuccessResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageOfferSuccessResult.java @@ -49,17 +49,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { offer.encode(stream); } - public static ManageOfferSuccessResult decode(XdrDataInputStream stream) throws IOException { + public static ManageOfferSuccessResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageOfferSuccessResult decodedManageOfferSuccessResult = new ManageOfferSuccessResult(); int offersClaimedSize = stream.readInt(); + if (offersClaimedSize < 0) { + throw new IOException("offersClaimed size " + offersClaimedSize + " is negative"); + } + int offersClaimedRemainingInputLen = stream.getRemainingInputLen(); + if (offersClaimedRemainingInputLen >= 0 && offersClaimedRemainingInputLen < offersClaimedSize) { + throw new IOException( + "offersClaimed size " + + offersClaimedSize + + " exceeds remaining input length " + + offersClaimedRemainingInputLen); + } decodedManageOfferSuccessResult.offersClaimed = new ClaimAtom[offersClaimedSize]; for (int i = 0; i < offersClaimedSize; i++) { - decodedManageOfferSuccessResult.offersClaimed[i] = ClaimAtom.decode(stream); + decodedManageOfferSuccessResult.offersClaimed[i] = ClaimAtom.decode(stream, maxDepth); } - decodedManageOfferSuccessResult.offer = ManageOfferSuccessResultOffer.decode(stream); + decodedManageOfferSuccessResult.offer = ManageOfferSuccessResultOffer.decode(stream, maxDepth); return decodedManageOfferSuccessResult; } + public static ManageOfferSuccessResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageOfferSuccessResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +88,7 @@ public static ManageOfferSuccessResult fromXdrBase64(String xdr) throws IOExcept public static ManageOfferSuccessResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -105,23 +126,34 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ManageOfferSuccessResultOffer decode(XdrDataInputStream stream) + public static ManageOfferSuccessResultOffer decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageOfferSuccessResultOffer decodedManageOfferSuccessResultOffer = new ManageOfferSuccessResultOffer(); - ManageOfferEffect discriminant = ManageOfferEffect.decode(stream); + ManageOfferEffect discriminant = ManageOfferEffect.decode(stream, maxDepth); decodedManageOfferSuccessResultOffer.setDiscriminant(discriminant); switch (decodedManageOfferSuccessResultOffer.getDiscriminant()) { case MANAGE_OFFER_CREATED: case MANAGE_OFFER_UPDATED: - decodedManageOfferSuccessResultOffer.offer = OfferEntry.decode(stream); + decodedManageOfferSuccessResultOffer.offer = OfferEntry.decode(stream, maxDepth); break; case MANAGE_OFFER_DELETED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedManageOfferSuccessResultOffer; } + public static ManageOfferSuccessResultOffer decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageOfferSuccessResultOffer fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -130,6 +162,7 @@ public static ManageOfferSuccessResultOffer fromXdrBase64(String xdr) throws IOE public static ManageOfferSuccessResultOffer fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferOp.java b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferOp.java index 00cb9db33..1858df7ec 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferOp.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferOp.java @@ -46,16 +46,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { offerID.encode(stream); } - public static ManageSellOfferOp decode(XdrDataInputStream stream) throws IOException { + public static ManageSellOfferOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageSellOfferOp decodedManageSellOfferOp = new ManageSellOfferOp(); - decodedManageSellOfferOp.selling = Asset.decode(stream); - decodedManageSellOfferOp.buying = Asset.decode(stream); - decodedManageSellOfferOp.amount = Int64.decode(stream); - decodedManageSellOfferOp.price = Price.decode(stream); - decodedManageSellOfferOp.offerID = Int64.decode(stream); + decodedManageSellOfferOp.selling = Asset.decode(stream, maxDepth); + decodedManageSellOfferOp.buying = Asset.decode(stream, maxDepth); + decodedManageSellOfferOp.amount = Int64.decode(stream, maxDepth); + decodedManageSellOfferOp.price = Price.decode(stream, maxDepth); + decodedManageSellOfferOp.offerID = Int64.decode(stream, maxDepth); return decodedManageSellOfferOp; } + public static ManageSellOfferOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageSellOfferOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -64,6 +73,7 @@ public static ManageSellOfferOp fromXdrBase64(String xdr) throws IOException { public static ManageSellOfferOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResult.java b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResult.java index b21b33399..31c65d979 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResult.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResult.java @@ -65,13 +65,18 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ManageSellOfferResult decode(XdrDataInputStream stream) throws IOException { + public static ManageSellOfferResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ManageSellOfferResult decodedManageSellOfferResult = new ManageSellOfferResult(); - ManageSellOfferResultCode discriminant = ManageSellOfferResultCode.decode(stream); + ManageSellOfferResultCode discriminant = ManageSellOfferResultCode.decode(stream, maxDepth); decodedManageSellOfferResult.setDiscriminant(discriminant); switch (decodedManageSellOfferResult.getDiscriminant()) { case MANAGE_SELL_OFFER_SUCCESS: - decodedManageSellOfferResult.success = ManageOfferSuccessResult.decode(stream); + decodedManageSellOfferResult.success = ManageOfferSuccessResult.decode(stream, maxDepth); break; case MANAGE_SELL_OFFER_MALFORMED: case MANAGE_SELL_OFFER_SELL_NO_TRUST: @@ -86,10 +91,16 @@ public static ManageSellOfferResult decode(XdrDataInputStream stream) throws IOE case MANAGE_SELL_OFFER_NOT_FOUND: case MANAGE_SELL_OFFER_LOW_RESERVE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedManageSellOfferResult; } + public static ManageSellOfferResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ManageSellOfferResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -98,6 +109,7 @@ public static ManageSellOfferResult fromXdrBase64(String xdr) throws IOException public static ManageSellOfferResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java index fcc8387fd..83ee5ff76 100644 --- a/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/ManageSellOfferResultCode.java @@ -64,7 +64,9 @@ public int getValue() { return value; } - public static ManageSellOfferResultCode decode(XdrDataInputStream stream) throws IOException { + public static ManageSellOfferResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -98,6 +100,10 @@ public static ManageSellOfferResultCode decode(XdrDataInputStream stream) throws } } + public static ManageSellOfferResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -110,6 +116,7 @@ public static ManageSellOfferResultCode fromXdrBase64(String xdr) throws IOExcep public static ManageSellOfferResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Memo.java b/src/main/java/org/stellar/sdk/xdr/Memo.java index 233503354..6bfc67988 100644 --- a/src/main/java/org/stellar/sdk/xdr/Memo.java +++ b/src/main/java/org/stellar/sdk/xdr/Memo.java @@ -47,6 +47,10 @@ public void encode(XdrDataOutputStream stream) throws IOException { case MEMO_NONE: break; case MEMO_TEXT: + int textSize = text.getBytes().length; + if (textSize > 28) { + throw new IOException("text size " + textSize + " exceeds max size 28"); + } text.encode(stream); break; case MEMO_ID: @@ -61,29 +65,39 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static Memo decode(XdrDataInputStream stream) throws IOException { + public static Memo decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Memo decodedMemo = new Memo(); - MemoType discriminant = MemoType.decode(stream); + MemoType discriminant = MemoType.decode(stream, maxDepth); decodedMemo.setDiscriminant(discriminant); switch (decodedMemo.getDiscriminant()) { case MEMO_NONE: break; case MEMO_TEXT: - decodedMemo.text = XdrString.decode(stream, 28); + decodedMemo.text = XdrString.decode(stream, maxDepth, 28); break; case MEMO_ID: - decodedMemo.id = Uint64.decode(stream); + decodedMemo.id = Uint64.decode(stream, maxDepth); break; case MEMO_HASH: - decodedMemo.hash = Hash.decode(stream); + decodedMemo.hash = Hash.decode(stream, maxDepth); break; case MEMO_RETURN: - decodedMemo.retHash = Hash.decode(stream); + decodedMemo.retHash = Hash.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedMemo; } + public static Memo decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Memo fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -92,6 +106,7 @@ public static Memo fromXdrBase64(String xdr) throws IOException { public static Memo fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/MemoType.java b/src/main/java/org/stellar/sdk/xdr/MemoType.java index a6ff17a76..6734c861e 100644 --- a/src/main/java/org/stellar/sdk/xdr/MemoType.java +++ b/src/main/java/org/stellar/sdk/xdr/MemoType.java @@ -38,7 +38,8 @@ public int getValue() { return value; } - public static MemoType decode(XdrDataInputStream stream) throws IOException { + public static MemoType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -56,6 +57,10 @@ public static MemoType decode(XdrDataInputStream stream) throws IOException { } } + public static MemoType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -68,6 +73,7 @@ public static MemoType fromXdrBase64(String xdr) throws IOException { public static MemoType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/MessageType.java b/src/main/java/org/stellar/sdk/xdr/MessageType.java index 8198bce63..61b1b50af 100644 --- a/src/main/java/org/stellar/sdk/xdr/MessageType.java +++ b/src/main/java/org/stellar/sdk/xdr/MessageType.java @@ -84,7 +84,8 @@ public int getValue() { return value; } - public static MessageType decode(XdrDataInputStream stream) throws IOException { + public static MessageType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -134,6 +135,10 @@ public static MessageType decode(XdrDataInputStream stream) throws IOException { } } + public static MessageType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -146,6 +151,7 @@ public static MessageType fromXdrBase64(String xdr) throws IOException { public static MessageType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java b/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java index d8a3bdc78..39abbb8c1 100644 --- a/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java +++ b/src/main/java/org/stellar/sdk/xdr/MuxedAccount.java @@ -49,21 +49,31 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static MuxedAccount decode(XdrDataInputStream stream) throws IOException { + public static MuxedAccount decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; MuxedAccount decodedMuxedAccount = new MuxedAccount(); - CryptoKeyType discriminant = CryptoKeyType.decode(stream); + CryptoKeyType discriminant = CryptoKeyType.decode(stream, maxDepth); decodedMuxedAccount.setDiscriminant(discriminant); switch (decodedMuxedAccount.getDiscriminant()) { case KEY_TYPE_ED25519: - decodedMuxedAccount.ed25519 = Uint256.decode(stream); + decodedMuxedAccount.ed25519 = Uint256.decode(stream, maxDepth); break; case KEY_TYPE_MUXED_ED25519: - decodedMuxedAccount.med25519 = MuxedAccountMed25519.decode(stream); + decodedMuxedAccount.med25519 = MuxedAccountMed25519.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedMuxedAccount; } + public static MuxedAccount decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static MuxedAccount fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -72,6 +82,7 @@ public static MuxedAccount fromXdrBase64(String xdr) throws IOException { public static MuxedAccount fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -99,13 +110,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ed25519.encode(stream); } - public static MuxedAccountMed25519 decode(XdrDataInputStream stream) throws IOException { + public static MuxedAccountMed25519 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; MuxedAccountMed25519 decodedMuxedAccountMed25519 = new MuxedAccountMed25519(); - decodedMuxedAccountMed25519.id = Uint64.decode(stream); - decodedMuxedAccountMed25519.ed25519 = Uint256.decode(stream); + decodedMuxedAccountMed25519.id = Uint64.decode(stream, maxDepth); + decodedMuxedAccountMed25519.ed25519 = Uint256.decode(stream, maxDepth); return decodedMuxedAccountMed25519; } + public static MuxedAccountMed25519 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static MuxedAccountMed25519 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -114,6 +134,7 @@ public static MuxedAccountMed25519 fromXdrBase64(String xdr) throws IOException public static MuxedAccountMed25519 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/MuxedEd25519Account.java b/src/main/java/org/stellar/sdk/xdr/MuxedEd25519Account.java index 98590cae6..b762bca1d 100644 --- a/src/main/java/org/stellar/sdk/xdr/MuxedEd25519Account.java +++ b/src/main/java/org/stellar/sdk/xdr/MuxedEd25519Account.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ed25519.encode(stream); } - public static MuxedEd25519Account decode(XdrDataInputStream stream) throws IOException { + public static MuxedEd25519Account decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; MuxedEd25519Account decodedMuxedEd25519Account = new MuxedEd25519Account(); - decodedMuxedEd25519Account.id = Uint64.decode(stream); - decodedMuxedEd25519Account.ed25519 = Uint256.decode(stream); + decodedMuxedEd25519Account.id = Uint64.decode(stream, maxDepth); + decodedMuxedEd25519Account.ed25519 = Uint256.decode(stream, maxDepth); return decodedMuxedEd25519Account; } + public static MuxedEd25519Account decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static MuxedEd25519Account fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static MuxedEd25519Account fromXdrBase64(String xdr) throws IOException { public static MuxedEd25519Account fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/NodeID.java b/src/main/java/org/stellar/sdk/xdr/NodeID.java index 44fc80123..f9ef4b8eb 100644 --- a/src/main/java/org/stellar/sdk/xdr/NodeID.java +++ b/src/main/java/org/stellar/sdk/xdr/NodeID.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { NodeID.encode(stream); } - public static NodeID decode(XdrDataInputStream stream) throws IOException { + public static NodeID decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; NodeID decodedNodeID = new NodeID(); - decodedNodeID.NodeID = PublicKey.decode(stream); + decodedNodeID.NodeID = PublicKey.decode(stream, maxDepth); return decodedNodeID; } + public static NodeID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static NodeID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static NodeID fromXdrBase64(String xdr) throws IOException { public static NodeID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OfferEntry.java b/src/main/java/org/stellar/sdk/xdr/OfferEntry.java index 3be7461d7..d2d7a4ed8 100644 --- a/src/main/java/org/stellar/sdk/xdr/OfferEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/OfferEntry.java @@ -66,19 +66,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static OfferEntry decode(XdrDataInputStream stream) throws IOException { + public static OfferEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OfferEntry decodedOfferEntry = new OfferEntry(); - decodedOfferEntry.sellerID = AccountID.decode(stream); - decodedOfferEntry.offerID = Int64.decode(stream); - decodedOfferEntry.selling = Asset.decode(stream); - decodedOfferEntry.buying = Asset.decode(stream); - decodedOfferEntry.amount = Int64.decode(stream); - decodedOfferEntry.price = Price.decode(stream); - decodedOfferEntry.flags = Uint32.decode(stream); - decodedOfferEntry.ext = OfferEntryExt.decode(stream); + decodedOfferEntry.sellerID = AccountID.decode(stream, maxDepth); + decodedOfferEntry.offerID = Int64.decode(stream, maxDepth); + decodedOfferEntry.selling = Asset.decode(stream, maxDepth); + decodedOfferEntry.buying = Asset.decode(stream, maxDepth); + decodedOfferEntry.amount = Int64.decode(stream, maxDepth); + decodedOfferEntry.price = Price.decode(stream, maxDepth); + decodedOfferEntry.flags = Uint32.decode(stream, maxDepth); + decodedOfferEntry.ext = OfferEntryExt.decode(stream, maxDepth); return decodedOfferEntry; } + public static OfferEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OfferEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -87,6 +95,7 @@ public static OfferEntry fromXdrBase64(String xdr) throws IOException { public static OfferEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -116,17 +125,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static OfferEntryExt decode(XdrDataInputStream stream) throws IOException { + public static OfferEntryExt decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OfferEntryExt decodedOfferEntryExt = new OfferEntryExt(); Integer discriminant = stream.readInt(); decodedOfferEntryExt.setDiscriminant(discriminant); switch (decodedOfferEntryExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedOfferEntryExt; } + public static OfferEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OfferEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -135,6 +154,7 @@ public static OfferEntryExt fromXdrBase64(String xdr) throws IOException { public static OfferEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OfferEntryFlags.java b/src/main/java/org/stellar/sdk/xdr/OfferEntryFlags.java index b34031caa..416632281 100644 --- a/src/main/java/org/stellar/sdk/xdr/OfferEntryFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/OfferEntryFlags.java @@ -32,7 +32,8 @@ public int getValue() { return value; } - public static OfferEntryFlags decode(XdrDataInputStream stream) throws IOException { + public static OfferEntryFlags decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -42,6 +43,10 @@ public static OfferEntryFlags decode(XdrDataInputStream stream) throws IOExcepti } } + public static OfferEntryFlags decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -54,6 +59,7 @@ public static OfferEntryFlags fromXdrBase64(String xdr) throws IOException { public static OfferEntryFlags fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Operation.java b/src/main/java/org/stellar/sdk/xdr/Operation.java index daeda1eca..d8a15fed7 100644 --- a/src/main/java/org/stellar/sdk/xdr/Operation.java +++ b/src/main/java/org/stellar/sdk/xdr/Operation.java @@ -101,16 +101,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { body.encode(stream); } - public static Operation decode(XdrDataInputStream stream) throws IOException { + public static Operation decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Operation decodedOperation = new Operation(); - int sourceAccountPresent = stream.readInt(); - if (sourceAccountPresent != 0) { - decodedOperation.sourceAccount = MuxedAccount.decode(stream); + boolean sourceAccountPresent = stream.readXdrBoolean(); + if (sourceAccountPresent) { + decodedOperation.sourceAccount = MuxedAccount.decode(stream, maxDepth); } - decodedOperation.body = OperationBody.decode(stream); + decodedOperation.body = OperationBody.decode(stream, maxDepth); return decodedOperation; } + public static Operation decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Operation fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -119,6 +127,7 @@ public static Operation fromXdrBase64(String xdr) throws IOException { public static Operation fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -302,97 +311,113 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static OperationBody decode(XdrDataInputStream stream) throws IOException { + public static OperationBody decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OperationBody decodedOperationBody = new OperationBody(); - OperationType discriminant = OperationType.decode(stream); + OperationType discriminant = OperationType.decode(stream, maxDepth); decodedOperationBody.setDiscriminant(discriminant); switch (decodedOperationBody.getDiscriminant()) { case CREATE_ACCOUNT: - decodedOperationBody.createAccountOp = CreateAccountOp.decode(stream); + decodedOperationBody.createAccountOp = CreateAccountOp.decode(stream, maxDepth); break; case PAYMENT: - decodedOperationBody.paymentOp = PaymentOp.decode(stream); + decodedOperationBody.paymentOp = PaymentOp.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_RECEIVE: decodedOperationBody.pathPaymentStrictReceiveOp = - PathPaymentStrictReceiveOp.decode(stream); + PathPaymentStrictReceiveOp.decode(stream, maxDepth); break; case MANAGE_SELL_OFFER: - decodedOperationBody.manageSellOfferOp = ManageSellOfferOp.decode(stream); + decodedOperationBody.manageSellOfferOp = ManageSellOfferOp.decode(stream, maxDepth); break; case CREATE_PASSIVE_SELL_OFFER: - decodedOperationBody.createPassiveSellOfferOp = CreatePassiveSellOfferOp.decode(stream); + decodedOperationBody.createPassiveSellOfferOp = + CreatePassiveSellOfferOp.decode(stream, maxDepth); break; case SET_OPTIONS: - decodedOperationBody.setOptionsOp = SetOptionsOp.decode(stream); + decodedOperationBody.setOptionsOp = SetOptionsOp.decode(stream, maxDepth); break; case CHANGE_TRUST: - decodedOperationBody.changeTrustOp = ChangeTrustOp.decode(stream); + decodedOperationBody.changeTrustOp = ChangeTrustOp.decode(stream, maxDepth); break; case ALLOW_TRUST: - decodedOperationBody.allowTrustOp = AllowTrustOp.decode(stream); + decodedOperationBody.allowTrustOp = AllowTrustOp.decode(stream, maxDepth); break; case ACCOUNT_MERGE: - decodedOperationBody.destination = MuxedAccount.decode(stream); + decodedOperationBody.destination = MuxedAccount.decode(stream, maxDepth); break; case INFLATION: break; case MANAGE_DATA: - decodedOperationBody.manageDataOp = ManageDataOp.decode(stream); + decodedOperationBody.manageDataOp = ManageDataOp.decode(stream, maxDepth); break; case BUMP_SEQUENCE: - decodedOperationBody.bumpSequenceOp = BumpSequenceOp.decode(stream); + decodedOperationBody.bumpSequenceOp = BumpSequenceOp.decode(stream, maxDepth); break; case MANAGE_BUY_OFFER: - decodedOperationBody.manageBuyOfferOp = ManageBuyOfferOp.decode(stream); + decodedOperationBody.manageBuyOfferOp = ManageBuyOfferOp.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_SEND: - decodedOperationBody.pathPaymentStrictSendOp = PathPaymentStrictSendOp.decode(stream); + decodedOperationBody.pathPaymentStrictSendOp = + PathPaymentStrictSendOp.decode(stream, maxDepth); break; case CREATE_CLAIMABLE_BALANCE: - decodedOperationBody.createClaimableBalanceOp = CreateClaimableBalanceOp.decode(stream); + decodedOperationBody.createClaimableBalanceOp = + CreateClaimableBalanceOp.decode(stream, maxDepth); break; case CLAIM_CLAIMABLE_BALANCE: - decodedOperationBody.claimClaimableBalanceOp = ClaimClaimableBalanceOp.decode(stream); + decodedOperationBody.claimClaimableBalanceOp = + ClaimClaimableBalanceOp.decode(stream, maxDepth); break; case BEGIN_SPONSORING_FUTURE_RESERVES: decodedOperationBody.beginSponsoringFutureReservesOp = - BeginSponsoringFutureReservesOp.decode(stream); + BeginSponsoringFutureReservesOp.decode(stream, maxDepth); break; case END_SPONSORING_FUTURE_RESERVES: break; case REVOKE_SPONSORSHIP: - decodedOperationBody.revokeSponsorshipOp = RevokeSponsorshipOp.decode(stream); + decodedOperationBody.revokeSponsorshipOp = RevokeSponsorshipOp.decode(stream, maxDepth); break; case CLAWBACK: - decodedOperationBody.clawbackOp = ClawbackOp.decode(stream); + decodedOperationBody.clawbackOp = ClawbackOp.decode(stream, maxDepth); break; case CLAWBACK_CLAIMABLE_BALANCE: decodedOperationBody.clawbackClaimableBalanceOp = - ClawbackClaimableBalanceOp.decode(stream); + ClawbackClaimableBalanceOp.decode(stream, maxDepth); break; case SET_TRUST_LINE_FLAGS: - decodedOperationBody.setTrustLineFlagsOp = SetTrustLineFlagsOp.decode(stream); + decodedOperationBody.setTrustLineFlagsOp = SetTrustLineFlagsOp.decode(stream, maxDepth); break; case LIQUIDITY_POOL_DEPOSIT: - decodedOperationBody.liquidityPoolDepositOp = LiquidityPoolDepositOp.decode(stream); + decodedOperationBody.liquidityPoolDepositOp = + LiquidityPoolDepositOp.decode(stream, maxDepth); break; case LIQUIDITY_POOL_WITHDRAW: - decodedOperationBody.liquidityPoolWithdrawOp = LiquidityPoolWithdrawOp.decode(stream); + decodedOperationBody.liquidityPoolWithdrawOp = + LiquidityPoolWithdrawOp.decode(stream, maxDepth); break; case INVOKE_HOST_FUNCTION: - decodedOperationBody.invokeHostFunctionOp = InvokeHostFunctionOp.decode(stream); + decodedOperationBody.invokeHostFunctionOp = InvokeHostFunctionOp.decode(stream, maxDepth); break; case EXTEND_FOOTPRINT_TTL: - decodedOperationBody.extendFootprintTTLOp = ExtendFootprintTTLOp.decode(stream); + decodedOperationBody.extendFootprintTTLOp = ExtendFootprintTTLOp.decode(stream, maxDepth); break; case RESTORE_FOOTPRINT: - decodedOperationBody.restoreFootprintOp = RestoreFootprintOp.decode(stream); + decodedOperationBody.restoreFootprintOp = RestoreFootprintOp.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedOperationBody; } + public static OperationBody decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OperationBody fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -401,6 +426,7 @@ public static OperationBody fromXdrBase64(String xdr) throws IOException { public static OperationBody fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationMeta.java b/src/main/java/org/stellar/sdk/xdr/OperationMeta.java index 9b91c7a0d..d43d4a20d 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationMeta.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { changes.encode(stream); } - public static OperationMeta decode(XdrDataInputStream stream) throws IOException { + public static OperationMeta decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OperationMeta decodedOperationMeta = new OperationMeta(); - decodedOperationMeta.changes = LedgerEntryChanges.decode(stream); + decodedOperationMeta.changes = LedgerEntryChanges.decode(stream, maxDepth); return decodedOperationMeta; } + public static OperationMeta decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OperationMeta fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static OperationMeta fromXdrBase64(String xdr) throws IOException { public static OperationMeta fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationMetaV2.java b/src/main/java/org/stellar/sdk/xdr/OperationMetaV2.java index a4b2db637..8e8c9653f 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationMetaV2.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationMetaV2.java @@ -44,18 +44,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static OperationMetaV2 decode(XdrDataInputStream stream) throws IOException { + public static OperationMetaV2 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OperationMetaV2 decodedOperationMetaV2 = new OperationMetaV2(); - decodedOperationMetaV2.ext = ExtensionPoint.decode(stream); - decodedOperationMetaV2.changes = LedgerEntryChanges.decode(stream); + decodedOperationMetaV2.ext = ExtensionPoint.decode(stream, maxDepth); + decodedOperationMetaV2.changes = LedgerEntryChanges.decode(stream, maxDepth); int eventsSize = stream.readInt(); + if (eventsSize < 0) { + throw new IOException("events size " + eventsSize + " is negative"); + } + int eventsRemainingInputLen = stream.getRemainingInputLen(); + if (eventsRemainingInputLen >= 0 && eventsRemainingInputLen < eventsSize) { + throw new IOException( + "events size " + + eventsSize + + " exceeds remaining input length " + + eventsRemainingInputLen); + } decodedOperationMetaV2.events = new ContractEvent[eventsSize]; for (int i = 0; i < eventsSize; i++) { - decodedOperationMetaV2.events[i] = ContractEvent.decode(stream); + decodedOperationMetaV2.events[i] = ContractEvent.decode(stream, maxDepth); } return decodedOperationMetaV2; } + public static OperationMetaV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OperationMetaV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -64,6 +83,7 @@ public static OperationMetaV2 fromXdrBase64(String xdr) throws IOException { public static OperationMetaV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationResult.java b/src/main/java/org/stellar/sdk/xdr/OperationResult.java index 6ee51ec6a..8f120d299 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationResult.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationResult.java @@ -110,13 +110,17 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static OperationResult decode(XdrDataInputStream stream) throws IOException { + public static OperationResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OperationResult decodedOperationResult = new OperationResult(); - OperationResultCode discriminant = OperationResultCode.decode(stream); + OperationResultCode discriminant = OperationResultCode.decode(stream, maxDepth); decodedOperationResult.setDiscriminant(discriminant); switch (decodedOperationResult.getDiscriminant()) { case opINNER: - decodedOperationResult.tr = OperationResultTr.decode(stream); + decodedOperationResult.tr = OperationResultTr.decode(stream, maxDepth); break; case opBAD_AUTH: case opNO_ACCOUNT: @@ -125,10 +129,16 @@ public static OperationResult decode(XdrDataInputStream stream) throws IOExcepti case opEXCEEDED_WORK_LIMIT: case opTOO_MANY_SPONSORING: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedOperationResult; } + public static OperationResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OperationResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -137,6 +147,7 @@ public static OperationResult fromXdrBase64(String xdr) throws IOException { public static OperationResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -324,108 +335,125 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static OperationResultTr decode(XdrDataInputStream stream) throws IOException { + public static OperationResultTr decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; OperationResultTr decodedOperationResultTr = new OperationResultTr(); - OperationType discriminant = OperationType.decode(stream); + OperationType discriminant = OperationType.decode(stream, maxDepth); decodedOperationResultTr.setDiscriminant(discriminant); switch (decodedOperationResultTr.getDiscriminant()) { case CREATE_ACCOUNT: - decodedOperationResultTr.createAccountResult = CreateAccountResult.decode(stream); + decodedOperationResultTr.createAccountResult = + CreateAccountResult.decode(stream, maxDepth); break; case PAYMENT: - decodedOperationResultTr.paymentResult = PaymentResult.decode(stream); + decodedOperationResultTr.paymentResult = PaymentResult.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_RECEIVE: decodedOperationResultTr.pathPaymentStrictReceiveResult = - PathPaymentStrictReceiveResult.decode(stream); + PathPaymentStrictReceiveResult.decode(stream, maxDepth); break; case MANAGE_SELL_OFFER: - decodedOperationResultTr.manageSellOfferResult = ManageSellOfferResult.decode(stream); + decodedOperationResultTr.manageSellOfferResult = + ManageSellOfferResult.decode(stream, maxDepth); break; case CREATE_PASSIVE_SELL_OFFER: decodedOperationResultTr.createPassiveSellOfferResult = - ManageSellOfferResult.decode(stream); + ManageSellOfferResult.decode(stream, maxDepth); break; case SET_OPTIONS: - decodedOperationResultTr.setOptionsResult = SetOptionsResult.decode(stream); + decodedOperationResultTr.setOptionsResult = SetOptionsResult.decode(stream, maxDepth); break; case CHANGE_TRUST: - decodedOperationResultTr.changeTrustResult = ChangeTrustResult.decode(stream); + decodedOperationResultTr.changeTrustResult = ChangeTrustResult.decode(stream, maxDepth); break; case ALLOW_TRUST: - decodedOperationResultTr.allowTrustResult = AllowTrustResult.decode(stream); + decodedOperationResultTr.allowTrustResult = AllowTrustResult.decode(stream, maxDepth); break; case ACCOUNT_MERGE: - decodedOperationResultTr.accountMergeResult = AccountMergeResult.decode(stream); + decodedOperationResultTr.accountMergeResult = AccountMergeResult.decode(stream, maxDepth); break; case INFLATION: - decodedOperationResultTr.inflationResult = InflationResult.decode(stream); + decodedOperationResultTr.inflationResult = InflationResult.decode(stream, maxDepth); break; case MANAGE_DATA: - decodedOperationResultTr.manageDataResult = ManageDataResult.decode(stream); + decodedOperationResultTr.manageDataResult = ManageDataResult.decode(stream, maxDepth); break; case BUMP_SEQUENCE: - decodedOperationResultTr.bumpSeqResult = BumpSequenceResult.decode(stream); + decodedOperationResultTr.bumpSeqResult = BumpSequenceResult.decode(stream, maxDepth); break; case MANAGE_BUY_OFFER: - decodedOperationResultTr.manageBuyOfferResult = ManageBuyOfferResult.decode(stream); + decodedOperationResultTr.manageBuyOfferResult = + ManageBuyOfferResult.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_SEND: decodedOperationResultTr.pathPaymentStrictSendResult = - PathPaymentStrictSendResult.decode(stream); + PathPaymentStrictSendResult.decode(stream, maxDepth); break; case CREATE_CLAIMABLE_BALANCE: decodedOperationResultTr.createClaimableBalanceResult = - CreateClaimableBalanceResult.decode(stream); + CreateClaimableBalanceResult.decode(stream, maxDepth); break; case CLAIM_CLAIMABLE_BALANCE: decodedOperationResultTr.claimClaimableBalanceResult = - ClaimClaimableBalanceResult.decode(stream); + ClaimClaimableBalanceResult.decode(stream, maxDepth); break; case BEGIN_SPONSORING_FUTURE_RESERVES: decodedOperationResultTr.beginSponsoringFutureReservesResult = - BeginSponsoringFutureReservesResult.decode(stream); + BeginSponsoringFutureReservesResult.decode(stream, maxDepth); break; case END_SPONSORING_FUTURE_RESERVES: decodedOperationResultTr.endSponsoringFutureReservesResult = - EndSponsoringFutureReservesResult.decode(stream); + EndSponsoringFutureReservesResult.decode(stream, maxDepth); break; case REVOKE_SPONSORSHIP: - decodedOperationResultTr.revokeSponsorshipResult = RevokeSponsorshipResult.decode(stream); + decodedOperationResultTr.revokeSponsorshipResult = + RevokeSponsorshipResult.decode(stream, maxDepth); break; case CLAWBACK: - decodedOperationResultTr.clawbackResult = ClawbackResult.decode(stream); + decodedOperationResultTr.clawbackResult = ClawbackResult.decode(stream, maxDepth); break; case CLAWBACK_CLAIMABLE_BALANCE: decodedOperationResultTr.clawbackClaimableBalanceResult = - ClawbackClaimableBalanceResult.decode(stream); + ClawbackClaimableBalanceResult.decode(stream, maxDepth); break; case SET_TRUST_LINE_FLAGS: - decodedOperationResultTr.setTrustLineFlagsResult = SetTrustLineFlagsResult.decode(stream); + decodedOperationResultTr.setTrustLineFlagsResult = + SetTrustLineFlagsResult.decode(stream, maxDepth); break; case LIQUIDITY_POOL_DEPOSIT: decodedOperationResultTr.liquidityPoolDepositResult = - LiquidityPoolDepositResult.decode(stream); + LiquidityPoolDepositResult.decode(stream, maxDepth); break; case LIQUIDITY_POOL_WITHDRAW: decodedOperationResultTr.liquidityPoolWithdrawResult = - LiquidityPoolWithdrawResult.decode(stream); + LiquidityPoolWithdrawResult.decode(stream, maxDepth); break; case INVOKE_HOST_FUNCTION: decodedOperationResultTr.invokeHostFunctionResult = - InvokeHostFunctionResult.decode(stream); + InvokeHostFunctionResult.decode(stream, maxDepth); break; case EXTEND_FOOTPRINT_TTL: decodedOperationResultTr.extendFootprintTTLResult = - ExtendFootprintTTLResult.decode(stream); + ExtendFootprintTTLResult.decode(stream, maxDepth); break; case RESTORE_FOOTPRINT: - decodedOperationResultTr.restoreFootprintResult = RestoreFootprintResult.decode(stream); + decodedOperationResultTr.restoreFootprintResult = + RestoreFootprintResult.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedOperationResultTr; } + public static OperationResultTr decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static OperationResultTr fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -434,6 +462,7 @@ public static OperationResultTr fromXdrBase64(String xdr) throws IOException { public static OperationResultTr fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java b/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java index c9cb59ab7..89f3963d8 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationResultCode.java @@ -43,7 +43,9 @@ public int getValue() { return value; } - public static OperationResultCode decode(XdrDataInputStream stream) throws IOException { + public static OperationResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -65,6 +67,10 @@ public static OperationResultCode decode(XdrDataInputStream stream) throws IOExc } } + public static OperationResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -77,6 +83,7 @@ public static OperationResultCode fromXdrBase64(String xdr) throws IOException { public static OperationResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/OperationType.java b/src/main/java/org/stellar/sdk/xdr/OperationType.java index d3b81d325..db49a183e 100644 --- a/src/main/java/org/stellar/sdk/xdr/OperationType.java +++ b/src/main/java/org/stellar/sdk/xdr/OperationType.java @@ -82,7 +82,8 @@ public int getValue() { return value; } - public static OperationType decode(XdrDataInputStream stream) throws IOException { + public static OperationType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -144,6 +145,10 @@ public static OperationType decode(XdrDataInputStream stream) throws IOException } } + public static OperationType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -156,6 +161,7 @@ public static OperationType fromXdrBase64(String xdr) throws IOException { public static OperationType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ParallelTxExecutionStage.java b/src/main/java/org/stellar/sdk/xdr/ParallelTxExecutionStage.java index cdf3beeac..9ea41f196 100644 --- a/src/main/java/org/stellar/sdk/xdr/ParallelTxExecutionStage.java +++ b/src/main/java/org/stellar/sdk/xdr/ParallelTxExecutionStage.java @@ -31,18 +31,40 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ParallelTxExecutionStage decode(XdrDataInputStream stream) throws IOException { + public static ParallelTxExecutionStage decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ParallelTxExecutionStage decodedParallelTxExecutionStage = new ParallelTxExecutionStage(); int ParallelTxExecutionStageSize = stream.readInt(); + if (ParallelTxExecutionStageSize < 0) { + throw new IOException( + "ParallelTxExecutionStage size " + ParallelTxExecutionStageSize + " is negative"); + } + int ParallelTxExecutionStageRemainingInputLen = stream.getRemainingInputLen(); + if (ParallelTxExecutionStageRemainingInputLen >= 0 + && ParallelTxExecutionStageRemainingInputLen < ParallelTxExecutionStageSize) { + throw new IOException( + "ParallelTxExecutionStage size " + + ParallelTxExecutionStageSize + + " exceeds remaining input length " + + ParallelTxExecutionStageRemainingInputLen); + } decodedParallelTxExecutionStage.ParallelTxExecutionStage = new DependentTxCluster[ParallelTxExecutionStageSize]; for (int i = 0; i < ParallelTxExecutionStageSize; i++) { decodedParallelTxExecutionStage.ParallelTxExecutionStage[i] = - DependentTxCluster.decode(stream); + DependentTxCluster.decode(stream, maxDepth); } return decodedParallelTxExecutionStage; } + public static ParallelTxExecutionStage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ParallelTxExecutionStage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -51,6 +73,7 @@ public static ParallelTxExecutionStage fromXdrBase64(String xdr) throws IOExcept public static ParallelTxExecutionStage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ParallelTxsComponent.java b/src/main/java/org/stellar/sdk/xdr/ParallelTxsComponent.java index efd1ede4a..6cbe2b7e7 100644 --- a/src/main/java/org/stellar/sdk/xdr/ParallelTxsComponent.java +++ b/src/main/java/org/stellar/sdk/xdr/ParallelTxsComponent.java @@ -47,20 +47,42 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static ParallelTxsComponent decode(XdrDataInputStream stream) throws IOException { + public static ParallelTxsComponent decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ParallelTxsComponent decodedParallelTxsComponent = new ParallelTxsComponent(); - int baseFeePresent = stream.readInt(); - if (baseFeePresent != 0) { - decodedParallelTxsComponent.baseFee = Int64.decode(stream); + boolean baseFeePresent = stream.readXdrBoolean(); + if (baseFeePresent) { + decodedParallelTxsComponent.baseFee = Int64.decode(stream, maxDepth); } int executionStagesSize = stream.readInt(); + if (executionStagesSize < 0) { + throw new IOException("executionStages size " + executionStagesSize + " is negative"); + } + int executionStagesRemainingInputLen = stream.getRemainingInputLen(); + if (executionStagesRemainingInputLen >= 0 + && executionStagesRemainingInputLen < executionStagesSize) { + throw new IOException( + "executionStages size " + + executionStagesSize + + " exceeds remaining input length " + + executionStagesRemainingInputLen); + } decodedParallelTxsComponent.executionStages = new ParallelTxExecutionStage[executionStagesSize]; for (int i = 0; i < executionStagesSize; i++) { - decodedParallelTxsComponent.executionStages[i] = ParallelTxExecutionStage.decode(stream); + decodedParallelTxsComponent.executionStages[i] = + ParallelTxExecutionStage.decode(stream, maxDepth); } return decodedParallelTxsComponent; } + public static ParallelTxsComponent decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ParallelTxsComponent fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -69,6 +91,7 @@ public static ParallelTxsComponent fromXdrBase64(String xdr) throws IOException public static ParallelTxsComponent fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java index 8e0a3ed9a..7ae9bbf89 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveOp.java @@ -49,27 +49,50 @@ public void encode(XdrDataOutputStream stream) throws IOException { destAsset.encode(stream); destAmount.encode(stream); int pathSize = getPath().length; + if (pathSize > 5) { + throw new IOException("path size " + pathSize + " exceeds max size 5"); + } stream.writeInt(pathSize); for (int i = 0; i < pathSize; i++) { path[i].encode(stream); } } - public static PathPaymentStrictReceiveOp decode(XdrDataInputStream stream) throws IOException { + public static PathPaymentStrictReceiveOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PathPaymentStrictReceiveOp decodedPathPaymentStrictReceiveOp = new PathPaymentStrictReceiveOp(); - decodedPathPaymentStrictReceiveOp.sendAsset = Asset.decode(stream); - decodedPathPaymentStrictReceiveOp.sendMax = Int64.decode(stream); - decodedPathPaymentStrictReceiveOp.destination = MuxedAccount.decode(stream); - decodedPathPaymentStrictReceiveOp.destAsset = Asset.decode(stream); - decodedPathPaymentStrictReceiveOp.destAmount = Int64.decode(stream); + decodedPathPaymentStrictReceiveOp.sendAsset = Asset.decode(stream, maxDepth); + decodedPathPaymentStrictReceiveOp.sendMax = Int64.decode(stream, maxDepth); + decodedPathPaymentStrictReceiveOp.destination = MuxedAccount.decode(stream, maxDepth); + decodedPathPaymentStrictReceiveOp.destAsset = Asset.decode(stream, maxDepth); + decodedPathPaymentStrictReceiveOp.destAmount = Int64.decode(stream, maxDepth); int pathSize = stream.readInt(); + if (pathSize < 0) { + throw new IOException("path size " + pathSize + " is negative"); + } + if (pathSize > 5) { + throw new IOException("path size " + pathSize + " exceeds max size 5"); + } + int pathRemainingInputLen = stream.getRemainingInputLen(); + if (pathRemainingInputLen >= 0 && pathRemainingInputLen < pathSize) { + throw new IOException( + "path size " + pathSize + " exceeds remaining input length " + pathRemainingInputLen); + } decodedPathPaymentStrictReceiveOp.path = new Asset[pathSize]; for (int i = 0; i < pathSize; i++) { - decodedPathPaymentStrictReceiveOp.path[i] = Asset.decode(stream); + decodedPathPaymentStrictReceiveOp.path[i] = Asset.decode(stream, maxDepth); } return decodedPathPaymentStrictReceiveOp; } + public static PathPaymentStrictReceiveOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PathPaymentStrictReceiveOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -78,6 +101,7 @@ public static PathPaymentStrictReceiveOp fromXdrBase64(String xdr) throws IOExce public static PathPaymentStrictReceiveOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResult.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResult.java index 8b26715e8..0ac476ef8 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResult.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResult.java @@ -76,17 +76,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PathPaymentStrictReceiveResult decode(XdrDataInputStream stream) + public static PathPaymentStrictReceiveResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PathPaymentStrictReceiveResult decodedPathPaymentStrictReceiveResult = new PathPaymentStrictReceiveResult(); PathPaymentStrictReceiveResultCode discriminant = - PathPaymentStrictReceiveResultCode.decode(stream); + PathPaymentStrictReceiveResultCode.decode(stream, maxDepth); decodedPathPaymentStrictReceiveResult.setDiscriminant(discriminant); switch (decodedPathPaymentStrictReceiveResult.getDiscriminant()) { case PATH_PAYMENT_STRICT_RECEIVE_SUCCESS: decodedPathPaymentStrictReceiveResult.success = - PathPaymentStrictReceiveResultSuccess.decode(stream); + PathPaymentStrictReceiveResultSuccess.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_RECEIVE_MALFORMED: case PATH_PAYMENT_STRICT_RECEIVE_UNDERFUNDED: @@ -98,16 +102,23 @@ public static PathPaymentStrictReceiveResult decode(XdrDataInputStream stream) case PATH_PAYMENT_STRICT_RECEIVE_LINE_FULL: break; case PATH_PAYMENT_STRICT_RECEIVE_NO_ISSUER: - decodedPathPaymentStrictReceiveResult.noIssuer = Asset.decode(stream); + decodedPathPaymentStrictReceiveResult.noIssuer = Asset.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_RECEIVE_TOO_FEW_OFFERS: case PATH_PAYMENT_STRICT_RECEIVE_OFFER_CROSS_SELF: case PATH_PAYMENT_STRICT_RECEIVE_OVER_SENDMAX: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPathPaymentStrictReceiveResult; } + public static PathPaymentStrictReceiveResult decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PathPaymentStrictReceiveResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -116,6 +127,7 @@ public static PathPaymentStrictReceiveResult fromXdrBase64(String xdr) throws IO public static PathPaymentStrictReceiveResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -147,19 +159,40 @@ public void encode(XdrDataOutputStream stream) throws IOException { last.encode(stream); } - public static PathPaymentStrictReceiveResultSuccess decode(XdrDataInputStream stream) - throws IOException { + public static PathPaymentStrictReceiveResultSuccess decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PathPaymentStrictReceiveResultSuccess decodedPathPaymentStrictReceiveResultSuccess = new PathPaymentStrictReceiveResultSuccess(); int offersSize = stream.readInt(); + if (offersSize < 0) { + throw new IOException("offers size " + offersSize + " is negative"); + } + int offersRemainingInputLen = stream.getRemainingInputLen(); + if (offersRemainingInputLen >= 0 && offersRemainingInputLen < offersSize) { + throw new IOException( + "offers size " + + offersSize + + " exceeds remaining input length " + + offersRemainingInputLen); + } decodedPathPaymentStrictReceiveResultSuccess.offers = new ClaimAtom[offersSize]; for (int i = 0; i < offersSize; i++) { - decodedPathPaymentStrictReceiveResultSuccess.offers[i] = ClaimAtom.decode(stream); + decodedPathPaymentStrictReceiveResultSuccess.offers[i] = ClaimAtom.decode(stream, maxDepth); } - decodedPathPaymentStrictReceiveResultSuccess.last = SimplePaymentResult.decode(stream); + decodedPathPaymentStrictReceiveResultSuccess.last = + SimplePaymentResult.decode(stream, maxDepth); return decodedPathPaymentStrictReceiveResultSuccess; } + public static PathPaymentStrictReceiveResultSuccess decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PathPaymentStrictReceiveResultSuccess fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -170,6 +203,7 @@ public static PathPaymentStrictReceiveResultSuccess fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java index 5a91ad937..8fa066823 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictReceiveResultCode.java @@ -66,8 +66,9 @@ public int getValue() { return value; } - public static PathPaymentStrictReceiveResultCode decode(XdrDataInputStream stream) + public static PathPaymentStrictReceiveResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -101,6 +102,11 @@ public static PathPaymentStrictReceiveResultCode decode(XdrDataInputStream strea } } + public static PathPaymentStrictReceiveResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -113,6 +119,7 @@ public static PathPaymentStrictReceiveResultCode fromXdrBase64(String xdr) throw public static PathPaymentStrictReceiveResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java index ff6c8369f..277565b85 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendOp.java @@ -49,27 +49,50 @@ public void encode(XdrDataOutputStream stream) throws IOException { destAsset.encode(stream); destMin.encode(stream); int pathSize = getPath().length; + if (pathSize > 5) { + throw new IOException("path size " + pathSize + " exceeds max size 5"); + } stream.writeInt(pathSize); for (int i = 0; i < pathSize; i++) { path[i].encode(stream); } } - public static PathPaymentStrictSendOp decode(XdrDataInputStream stream) throws IOException { + public static PathPaymentStrictSendOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PathPaymentStrictSendOp decodedPathPaymentStrictSendOp = new PathPaymentStrictSendOp(); - decodedPathPaymentStrictSendOp.sendAsset = Asset.decode(stream); - decodedPathPaymentStrictSendOp.sendAmount = Int64.decode(stream); - decodedPathPaymentStrictSendOp.destination = MuxedAccount.decode(stream); - decodedPathPaymentStrictSendOp.destAsset = Asset.decode(stream); - decodedPathPaymentStrictSendOp.destMin = Int64.decode(stream); + decodedPathPaymentStrictSendOp.sendAsset = Asset.decode(stream, maxDepth); + decodedPathPaymentStrictSendOp.sendAmount = Int64.decode(stream, maxDepth); + decodedPathPaymentStrictSendOp.destination = MuxedAccount.decode(stream, maxDepth); + decodedPathPaymentStrictSendOp.destAsset = Asset.decode(stream, maxDepth); + decodedPathPaymentStrictSendOp.destMin = Int64.decode(stream, maxDepth); int pathSize = stream.readInt(); + if (pathSize < 0) { + throw new IOException("path size " + pathSize + " is negative"); + } + if (pathSize > 5) { + throw new IOException("path size " + pathSize + " exceeds max size 5"); + } + int pathRemainingInputLen = stream.getRemainingInputLen(); + if (pathRemainingInputLen >= 0 && pathRemainingInputLen < pathSize) { + throw new IOException( + "path size " + pathSize + " exceeds remaining input length " + pathRemainingInputLen); + } decodedPathPaymentStrictSendOp.path = new Asset[pathSize]; for (int i = 0; i < pathSize; i++) { - decodedPathPaymentStrictSendOp.path[i] = Asset.decode(stream); + decodedPathPaymentStrictSendOp.path[i] = Asset.decode(stream, maxDepth); } return decodedPathPaymentStrictSendOp; } + public static PathPaymentStrictSendOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PathPaymentStrictSendOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -78,6 +101,7 @@ public static PathPaymentStrictSendOp fromXdrBase64(String xdr) throws IOExcepti public static PathPaymentStrictSendOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResult.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResult.java index 5babc6abf..234d1571f 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResult.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResult.java @@ -75,15 +75,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PathPaymentStrictSendResult decode(XdrDataInputStream stream) throws IOException { + public static PathPaymentStrictSendResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PathPaymentStrictSendResult decodedPathPaymentStrictSendResult = new PathPaymentStrictSendResult(); - PathPaymentStrictSendResultCode discriminant = PathPaymentStrictSendResultCode.decode(stream); + PathPaymentStrictSendResultCode discriminant = + PathPaymentStrictSendResultCode.decode(stream, maxDepth); decodedPathPaymentStrictSendResult.setDiscriminant(discriminant); switch (decodedPathPaymentStrictSendResult.getDiscriminant()) { case PATH_PAYMENT_STRICT_SEND_SUCCESS: decodedPathPaymentStrictSendResult.success = - PathPaymentStrictSendResultSuccess.decode(stream); + PathPaymentStrictSendResultSuccess.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_SEND_MALFORMED: case PATH_PAYMENT_STRICT_SEND_UNDERFUNDED: @@ -95,16 +101,22 @@ public static PathPaymentStrictSendResult decode(XdrDataInputStream stream) thro case PATH_PAYMENT_STRICT_SEND_LINE_FULL: break; case PATH_PAYMENT_STRICT_SEND_NO_ISSUER: - decodedPathPaymentStrictSendResult.noIssuer = Asset.decode(stream); + decodedPathPaymentStrictSendResult.noIssuer = Asset.decode(stream, maxDepth); break; case PATH_PAYMENT_STRICT_SEND_TOO_FEW_OFFERS: case PATH_PAYMENT_STRICT_SEND_OFFER_CROSS_SELF: case PATH_PAYMENT_STRICT_SEND_UNDER_DESTMIN: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPathPaymentStrictSendResult; } + public static PathPaymentStrictSendResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PathPaymentStrictSendResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -113,6 +125,7 @@ public static PathPaymentStrictSendResult fromXdrBase64(String xdr) throws IOExc public static PathPaymentStrictSendResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -144,19 +157,39 @@ public void encode(XdrDataOutputStream stream) throws IOException { last.encode(stream); } - public static PathPaymentStrictSendResultSuccess decode(XdrDataInputStream stream) + public static PathPaymentStrictSendResultSuccess decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PathPaymentStrictSendResultSuccess decodedPathPaymentStrictSendResultSuccess = new PathPaymentStrictSendResultSuccess(); int offersSize = stream.readInt(); + if (offersSize < 0) { + throw new IOException("offers size " + offersSize + " is negative"); + } + int offersRemainingInputLen = stream.getRemainingInputLen(); + if (offersRemainingInputLen >= 0 && offersRemainingInputLen < offersSize) { + throw new IOException( + "offers size " + + offersSize + + " exceeds remaining input length " + + offersRemainingInputLen); + } decodedPathPaymentStrictSendResultSuccess.offers = new ClaimAtom[offersSize]; for (int i = 0; i < offersSize; i++) { - decodedPathPaymentStrictSendResultSuccess.offers[i] = ClaimAtom.decode(stream); + decodedPathPaymentStrictSendResultSuccess.offers[i] = ClaimAtom.decode(stream, maxDepth); } - decodedPathPaymentStrictSendResultSuccess.last = SimplePaymentResult.decode(stream); + decodedPathPaymentStrictSendResultSuccess.last = SimplePaymentResult.decode(stream, maxDepth); return decodedPathPaymentStrictSendResultSuccess; } + public static PathPaymentStrictSendResultSuccess decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PathPaymentStrictSendResultSuccess fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -166,6 +199,7 @@ public static PathPaymentStrictSendResultSuccess fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java index 7faa4e596..a24d00ceb 100644 --- a/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PathPaymentStrictSendResultCode.java @@ -65,8 +65,9 @@ public int getValue() { return value; } - public static PathPaymentStrictSendResultCode decode(XdrDataInputStream stream) + public static PathPaymentStrictSendResultCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -100,6 +101,11 @@ public static PathPaymentStrictSendResultCode decode(XdrDataInputStream stream) } } + public static PathPaymentStrictSendResultCode decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -112,6 +118,7 @@ public static PathPaymentStrictSendResultCode fromXdrBase64(String xdr) throws I public static PathPaymentStrictSendResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PaymentOp.java b/src/main/java/org/stellar/sdk/xdr/PaymentOp.java index cfc621b77..7dd837567 100644 --- a/src/main/java/org/stellar/sdk/xdr/PaymentOp.java +++ b/src/main/java/org/stellar/sdk/xdr/PaymentOp.java @@ -38,14 +38,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { amount.encode(stream); } - public static PaymentOp decode(XdrDataInputStream stream) throws IOException { + public static PaymentOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PaymentOp decodedPaymentOp = new PaymentOp(); - decodedPaymentOp.destination = MuxedAccount.decode(stream); - decodedPaymentOp.asset = Asset.decode(stream); - decodedPaymentOp.amount = Int64.decode(stream); + decodedPaymentOp.destination = MuxedAccount.decode(stream, maxDepth); + decodedPaymentOp.asset = Asset.decode(stream, maxDepth); + decodedPaymentOp.amount = Int64.decode(stream, maxDepth); return decodedPaymentOp; } + public static PaymentOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PaymentOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +62,7 @@ public static PaymentOp fromXdrBase64(String xdr) throws IOException { public static PaymentOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PaymentResult.java b/src/main/java/org/stellar/sdk/xdr/PaymentResult.java index 2ab90909f..85b1a75cc 100644 --- a/src/main/java/org/stellar/sdk/xdr/PaymentResult.java +++ b/src/main/java/org/stellar/sdk/xdr/PaymentResult.java @@ -57,9 +57,13 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PaymentResult decode(XdrDataInputStream stream) throws IOException { + public static PaymentResult decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PaymentResult decodedPaymentResult = new PaymentResult(); - PaymentResultCode discriminant = PaymentResultCode.decode(stream); + PaymentResultCode discriminant = PaymentResultCode.decode(stream, maxDepth); decodedPaymentResult.setDiscriminant(discriminant); switch (decodedPaymentResult.getDiscriminant()) { case PAYMENT_SUCCESS: @@ -74,10 +78,16 @@ public static PaymentResult decode(XdrDataInputStream stream) throws IOException case PAYMENT_LINE_FULL: case PAYMENT_NO_ISSUER: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPaymentResult; } + public static PaymentResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PaymentResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -86,6 +96,7 @@ public static PaymentResult fromXdrBase64(String xdr) throws IOException { public static PaymentResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PaymentResultCode.java b/src/main/java/org/stellar/sdk/xdr/PaymentResultCode.java index e9ae60eb1..3a1289284 100644 --- a/src/main/java/org/stellar/sdk/xdr/PaymentResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/PaymentResultCode.java @@ -51,7 +51,9 @@ public int getValue() { return value; } - public static PaymentResultCode decode(XdrDataInputStream stream) throws IOException { + public static PaymentResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -79,6 +81,10 @@ public static PaymentResultCode decode(XdrDataInputStream stream) throws IOExcep } } + public static PaymentResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -91,6 +97,7 @@ public static PaymentResultCode fromXdrBase64(String xdr) throws IOException { public static PaymentResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PeerAddress.java b/src/main/java/org/stellar/sdk/xdr/PeerAddress.java index 4d4d1623a..75b846aad 100644 --- a/src/main/java/org/stellar/sdk/xdr/PeerAddress.java +++ b/src/main/java/org/stellar/sdk/xdr/PeerAddress.java @@ -45,14 +45,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { numFailures.encode(stream); } - public static PeerAddress decode(XdrDataInputStream stream) throws IOException { + public static PeerAddress decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PeerAddress decodedPeerAddress = new PeerAddress(); - decodedPeerAddress.ip = PeerAddressIp.decode(stream); - decodedPeerAddress.port = Uint32.decode(stream); - decodedPeerAddress.numFailures = Uint32.decode(stream); + decodedPeerAddress.ip = PeerAddressIp.decode(stream, maxDepth); + decodedPeerAddress.port = Uint32.decode(stream, maxDepth); + decodedPeerAddress.numFailures = Uint32.decode(stream, maxDepth); return decodedPeerAddress; } + public static PeerAddress decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PeerAddress fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -61,6 +69,7 @@ public static PeerAddress fromXdrBase64(String xdr) throws IOException { public static PeerAddress fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -91,34 +100,50 @@ public void encode(XdrDataOutputStream stream) throws IOException { switch (discriminant) { case IPv4: int ipv4Size = ipv4.length; + if (ipv4Size != 4) { + throw new IOException("ipv4 size " + ipv4Size + " does not match fixed size 4"); + } stream.write(getIpv4(), 0, ipv4Size); break; case IPv6: int ipv6Size = ipv6.length; + if (ipv6Size != 16) { + throw new IOException("ipv6 size " + ipv6Size + " does not match fixed size 16"); + } stream.write(getIpv6(), 0, ipv6Size); break; } } - public static PeerAddressIp decode(XdrDataInputStream stream) throws IOException { + public static PeerAddressIp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PeerAddressIp decodedPeerAddressIp = new PeerAddressIp(); - IPAddrType discriminant = IPAddrType.decode(stream); + IPAddrType discriminant = IPAddrType.decode(stream, maxDepth); decodedPeerAddressIp.setDiscriminant(discriminant); switch (decodedPeerAddressIp.getDiscriminant()) { case IPv4: int ipv4Size = 4; decodedPeerAddressIp.ipv4 = new byte[ipv4Size]; - stream.read(decodedPeerAddressIp.ipv4, 0, ipv4Size); + stream.readPaddedData(decodedPeerAddressIp.ipv4, 0, ipv4Size); break; case IPv6: int ipv6Size = 16; decodedPeerAddressIp.ipv6 = new byte[ipv6Size]; - stream.read(decodedPeerAddressIp.ipv6, 0, ipv6Size); + stream.readPaddedData(decodedPeerAddressIp.ipv6, 0, ipv6Size); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPeerAddressIp; } + public static PeerAddressIp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PeerAddressIp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -127,6 +152,7 @@ public static PeerAddressIp fromXdrBase64(String xdr) throws IOException { public static PeerAddressIp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PeerStats.java b/src/main/java/org/stellar/sdk/xdr/PeerStats.java index af3997c84..c52ab9a52 100644 --- a/src/main/java/org/stellar/sdk/xdr/PeerStats.java +++ b/src/main/java/org/stellar/sdk/xdr/PeerStats.java @@ -60,6 +60,10 @@ public class PeerStats implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { id.encode(stream); + int versionStrSize = versionStr.getBytes().length; + if (versionStrSize > 100) { + throw new IOException("versionStr size " + versionStrSize + " exceeds max size 100"); + } versionStr.encode(stream); messagesRead.encode(stream); messagesWritten.encode(stream); @@ -76,26 +80,34 @@ public void encode(XdrDataOutputStream stream) throws IOException { duplicateFetchMessageRecv.encode(stream); } - public static PeerStats decode(XdrDataInputStream stream) throws IOException { + public static PeerStats decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PeerStats decodedPeerStats = new PeerStats(); - decodedPeerStats.id = NodeID.decode(stream); - decodedPeerStats.versionStr = XdrString.decode(stream, 100); - decodedPeerStats.messagesRead = Uint64.decode(stream); - decodedPeerStats.messagesWritten = Uint64.decode(stream); - decodedPeerStats.bytesRead = Uint64.decode(stream); - decodedPeerStats.bytesWritten = Uint64.decode(stream); - decodedPeerStats.secondsConnected = Uint64.decode(stream); - decodedPeerStats.uniqueFloodBytesRecv = Uint64.decode(stream); - decodedPeerStats.duplicateFloodBytesRecv = Uint64.decode(stream); - decodedPeerStats.uniqueFetchBytesRecv = Uint64.decode(stream); - decodedPeerStats.duplicateFetchBytesRecv = Uint64.decode(stream); - decodedPeerStats.uniqueFloodMessageRecv = Uint64.decode(stream); - decodedPeerStats.duplicateFloodMessageRecv = Uint64.decode(stream); - decodedPeerStats.uniqueFetchMessageRecv = Uint64.decode(stream); - decodedPeerStats.duplicateFetchMessageRecv = Uint64.decode(stream); + decodedPeerStats.id = NodeID.decode(stream, maxDepth); + decodedPeerStats.versionStr = XdrString.decode(stream, maxDepth, 100); + decodedPeerStats.messagesRead = Uint64.decode(stream, maxDepth); + decodedPeerStats.messagesWritten = Uint64.decode(stream, maxDepth); + decodedPeerStats.bytesRead = Uint64.decode(stream, maxDepth); + decodedPeerStats.bytesWritten = Uint64.decode(stream, maxDepth); + decodedPeerStats.secondsConnected = Uint64.decode(stream, maxDepth); + decodedPeerStats.uniqueFloodBytesRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.duplicateFloodBytesRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.uniqueFetchBytesRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.duplicateFetchBytesRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.uniqueFloodMessageRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.duplicateFloodMessageRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.uniqueFetchMessageRecv = Uint64.decode(stream, maxDepth); + decodedPeerStats.duplicateFetchMessageRecv = Uint64.decode(stream, maxDepth); return decodedPeerStats; } + public static PeerStats decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PeerStats fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -104,6 +116,7 @@ public static PeerStats fromXdrBase64(String xdr) throws IOException { public static PeerStats fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PersistedSCPState.java b/src/main/java/org/stellar/sdk/xdr/PersistedSCPState.java index 7cf0974f8..9af3da1c3 100644 --- a/src/main/java/org/stellar/sdk/xdr/PersistedSCPState.java +++ b/src/main/java/org/stellar/sdk/xdr/PersistedSCPState.java @@ -45,21 +45,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PersistedSCPState decode(XdrDataInputStream stream) throws IOException { + public static PersistedSCPState decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PersistedSCPState decodedPersistedSCPState = new PersistedSCPState(); Integer discriminant = stream.readInt(); decodedPersistedSCPState.setDiscriminant(discriminant); switch (decodedPersistedSCPState.getDiscriminant()) { case 0: - decodedPersistedSCPState.v0 = PersistedSCPStateV0.decode(stream); + decodedPersistedSCPState.v0 = PersistedSCPStateV0.decode(stream, maxDepth); break; case 1: - decodedPersistedSCPState.v1 = PersistedSCPStateV1.decode(stream); + decodedPersistedSCPState.v1 = PersistedSCPStateV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPersistedSCPState; } + public static PersistedSCPState decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PersistedSCPState fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +79,7 @@ public static PersistedSCPState fromXdrBase64(String xdr) throws IOException { public static PersistedSCPState fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV0.java b/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV0.java index e68d20d39..2d4ad6c76 100644 --- a/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV0.java +++ b/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV0.java @@ -50,26 +50,68 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PersistedSCPStateV0 decode(XdrDataInputStream stream) throws IOException { + public static PersistedSCPStateV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PersistedSCPStateV0 decodedPersistedSCPStateV0 = new PersistedSCPStateV0(); int scpEnvelopesSize = stream.readInt(); + if (scpEnvelopesSize < 0) { + throw new IOException("scpEnvelopes size " + scpEnvelopesSize + " is negative"); + } + int scpEnvelopesRemainingInputLen = stream.getRemainingInputLen(); + if (scpEnvelopesRemainingInputLen >= 0 && scpEnvelopesRemainingInputLen < scpEnvelopesSize) { + throw new IOException( + "scpEnvelopes size " + + scpEnvelopesSize + + " exceeds remaining input length " + + scpEnvelopesRemainingInputLen); + } decodedPersistedSCPStateV0.scpEnvelopes = new SCPEnvelope[scpEnvelopesSize]; for (int i = 0; i < scpEnvelopesSize; i++) { - decodedPersistedSCPStateV0.scpEnvelopes[i] = SCPEnvelope.decode(stream); + decodedPersistedSCPStateV0.scpEnvelopes[i] = SCPEnvelope.decode(stream, maxDepth); } int quorumSetsSize = stream.readInt(); + if (quorumSetsSize < 0) { + throw new IOException("quorumSets size " + quorumSetsSize + " is negative"); + } + int quorumSetsRemainingInputLen = stream.getRemainingInputLen(); + if (quorumSetsRemainingInputLen >= 0 && quorumSetsRemainingInputLen < quorumSetsSize) { + throw new IOException( + "quorumSets size " + + quorumSetsSize + + " exceeds remaining input length " + + quorumSetsRemainingInputLen); + } decodedPersistedSCPStateV0.quorumSets = new SCPQuorumSet[quorumSetsSize]; for (int i = 0; i < quorumSetsSize; i++) { - decodedPersistedSCPStateV0.quorumSets[i] = SCPQuorumSet.decode(stream); + decodedPersistedSCPStateV0.quorumSets[i] = SCPQuorumSet.decode(stream, maxDepth); } int txSetsSize = stream.readInt(); + if (txSetsSize < 0) { + throw new IOException("txSets size " + txSetsSize + " is negative"); + } + int txSetsRemainingInputLen = stream.getRemainingInputLen(); + if (txSetsRemainingInputLen >= 0 && txSetsRemainingInputLen < txSetsSize) { + throw new IOException( + "txSets size " + + txSetsSize + + " exceeds remaining input length " + + txSetsRemainingInputLen); + } decodedPersistedSCPStateV0.txSets = new StoredTransactionSet[txSetsSize]; for (int i = 0; i < txSetsSize; i++) { - decodedPersistedSCPStateV0.txSets[i] = StoredTransactionSet.decode(stream); + decodedPersistedSCPStateV0.txSets[i] = StoredTransactionSet.decode(stream, maxDepth); } return decodedPersistedSCPStateV0; } + public static PersistedSCPStateV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PersistedSCPStateV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -78,6 +120,7 @@ public static PersistedSCPStateV0 fromXdrBase64(String xdr) throws IOException { public static PersistedSCPStateV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV1.java b/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV1.java index 952a2ae79..af8709738 100644 --- a/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV1.java +++ b/src/main/java/org/stellar/sdk/xdr/PersistedSCPStateV1.java @@ -44,21 +44,52 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PersistedSCPStateV1 decode(XdrDataInputStream stream) throws IOException { + public static PersistedSCPStateV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PersistedSCPStateV1 decodedPersistedSCPStateV1 = new PersistedSCPStateV1(); int scpEnvelopesSize = stream.readInt(); + if (scpEnvelopesSize < 0) { + throw new IOException("scpEnvelopes size " + scpEnvelopesSize + " is negative"); + } + int scpEnvelopesRemainingInputLen = stream.getRemainingInputLen(); + if (scpEnvelopesRemainingInputLen >= 0 && scpEnvelopesRemainingInputLen < scpEnvelopesSize) { + throw new IOException( + "scpEnvelopes size " + + scpEnvelopesSize + + " exceeds remaining input length " + + scpEnvelopesRemainingInputLen); + } decodedPersistedSCPStateV1.scpEnvelopes = new SCPEnvelope[scpEnvelopesSize]; for (int i = 0; i < scpEnvelopesSize; i++) { - decodedPersistedSCPStateV1.scpEnvelopes[i] = SCPEnvelope.decode(stream); + decodedPersistedSCPStateV1.scpEnvelopes[i] = SCPEnvelope.decode(stream, maxDepth); } int quorumSetsSize = stream.readInt(); + if (quorumSetsSize < 0) { + throw new IOException("quorumSets size " + quorumSetsSize + " is negative"); + } + int quorumSetsRemainingInputLen = stream.getRemainingInputLen(); + if (quorumSetsRemainingInputLen >= 0 && quorumSetsRemainingInputLen < quorumSetsSize) { + throw new IOException( + "quorumSets size " + + quorumSetsSize + + " exceeds remaining input length " + + quorumSetsRemainingInputLen); + } decodedPersistedSCPStateV1.quorumSets = new SCPQuorumSet[quorumSetsSize]; for (int i = 0; i < quorumSetsSize; i++) { - decodedPersistedSCPStateV1.quorumSets[i] = SCPQuorumSet.decode(stream); + decodedPersistedSCPStateV1.quorumSets[i] = SCPQuorumSet.decode(stream, maxDepth); } return decodedPersistedSCPStateV1; } + public static PersistedSCPStateV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PersistedSCPStateV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -67,6 +98,7 @@ public static PersistedSCPStateV1 fromXdrBase64(String xdr) throws IOException { public static PersistedSCPStateV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PoolID.java b/src/main/java/org/stellar/sdk/xdr/PoolID.java index 15727f95a..a153b5155 100644 --- a/src/main/java/org/stellar/sdk/xdr/PoolID.java +++ b/src/main/java/org/stellar/sdk/xdr/PoolID.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { PoolID.encode(stream); } - public static PoolID decode(XdrDataInputStream stream) throws IOException { + public static PoolID decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PoolID decodedPoolID = new PoolID(); - decodedPoolID.PoolID = Hash.decode(stream); + decodedPoolID.PoolID = Hash.decode(stream, maxDepth); return decodedPoolID; } + public static PoolID decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PoolID fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static PoolID fromXdrBase64(String xdr) throws IOException { public static PoolID fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PreconditionType.java b/src/main/java/org/stellar/sdk/xdr/PreconditionType.java index b75923189..9a705eb8f 100644 --- a/src/main/java/org/stellar/sdk/xdr/PreconditionType.java +++ b/src/main/java/org/stellar/sdk/xdr/PreconditionType.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static PreconditionType decode(XdrDataInputStream stream) throws IOException { + public static PreconditionType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +50,10 @@ public static PreconditionType decode(XdrDataInputStream stream) throws IOExcept } } + public static PreconditionType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static PreconditionType fromXdrBase64(String xdr) throws IOException { public static PreconditionType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Preconditions.java b/src/main/java/org/stellar/sdk/xdr/Preconditions.java index d8cf70b36..37d886ab9 100644 --- a/src/main/java/org/stellar/sdk/xdr/Preconditions.java +++ b/src/main/java/org/stellar/sdk/xdr/Preconditions.java @@ -49,23 +49,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static Preconditions decode(XdrDataInputStream stream) throws IOException { + public static Preconditions decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Preconditions decodedPreconditions = new Preconditions(); - PreconditionType discriminant = PreconditionType.decode(stream); + PreconditionType discriminant = PreconditionType.decode(stream, maxDepth); decodedPreconditions.setDiscriminant(discriminant); switch (decodedPreconditions.getDiscriminant()) { case PRECOND_NONE: break; case PRECOND_TIME: - decodedPreconditions.timeBounds = TimeBounds.decode(stream); + decodedPreconditions.timeBounds = TimeBounds.decode(stream, maxDepth); break; case PRECOND_V2: - decodedPreconditions.v2 = PreconditionsV2.decode(stream); + decodedPreconditions.v2 = PreconditionsV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPreconditions; } + public static Preconditions decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Preconditions fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -74,6 +84,7 @@ public static Preconditions fromXdrBase64(String xdr) throws IOException { public static Preconditions fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PreconditionsV2.java b/src/main/java/org/stellar/sdk/xdr/PreconditionsV2.java index 17f1f55ed..8aad3a098 100644 --- a/src/main/java/org/stellar/sdk/xdr/PreconditionsV2.java +++ b/src/main/java/org/stellar/sdk/xdr/PreconditionsV2.java @@ -83,36 +83,61 @@ public void encode(XdrDataOutputStream stream) throws IOException { minSeqAge.encode(stream); minSeqLedgerGap.encode(stream); int extraSignersSize = getExtraSigners().length; + if (extraSignersSize > 2) { + throw new IOException("extraSigners size " + extraSignersSize + " exceeds max size 2"); + } stream.writeInt(extraSignersSize); for (int i = 0; i < extraSignersSize; i++) { extraSigners[i].encode(stream); } } - public static PreconditionsV2 decode(XdrDataInputStream stream) throws IOException { + public static PreconditionsV2 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PreconditionsV2 decodedPreconditionsV2 = new PreconditionsV2(); - int timeBoundsPresent = stream.readInt(); - if (timeBoundsPresent != 0) { - decodedPreconditionsV2.timeBounds = TimeBounds.decode(stream); + boolean timeBoundsPresent = stream.readXdrBoolean(); + if (timeBoundsPresent) { + decodedPreconditionsV2.timeBounds = TimeBounds.decode(stream, maxDepth); } - int ledgerBoundsPresent = stream.readInt(); - if (ledgerBoundsPresent != 0) { - decodedPreconditionsV2.ledgerBounds = LedgerBounds.decode(stream); + boolean ledgerBoundsPresent = stream.readXdrBoolean(); + if (ledgerBoundsPresent) { + decodedPreconditionsV2.ledgerBounds = LedgerBounds.decode(stream, maxDepth); } - int minSeqNumPresent = stream.readInt(); - if (minSeqNumPresent != 0) { - decodedPreconditionsV2.minSeqNum = SequenceNumber.decode(stream); + boolean minSeqNumPresent = stream.readXdrBoolean(); + if (minSeqNumPresent) { + decodedPreconditionsV2.minSeqNum = SequenceNumber.decode(stream, maxDepth); } - decodedPreconditionsV2.minSeqAge = Duration.decode(stream); - decodedPreconditionsV2.minSeqLedgerGap = Uint32.decode(stream); + decodedPreconditionsV2.minSeqAge = Duration.decode(stream, maxDepth); + decodedPreconditionsV2.minSeqLedgerGap = Uint32.decode(stream, maxDepth); int extraSignersSize = stream.readInt(); + if (extraSignersSize < 0) { + throw new IOException("extraSigners size " + extraSignersSize + " is negative"); + } + if (extraSignersSize > 2) { + throw new IOException("extraSigners size " + extraSignersSize + " exceeds max size 2"); + } + int extraSignersRemainingInputLen = stream.getRemainingInputLen(); + if (extraSignersRemainingInputLen >= 0 && extraSignersRemainingInputLen < extraSignersSize) { + throw new IOException( + "extraSigners size " + + extraSignersSize + + " exceeds remaining input length " + + extraSignersRemainingInputLen); + } decodedPreconditionsV2.extraSigners = new SignerKey[extraSignersSize]; for (int i = 0; i < extraSignersSize; i++) { - decodedPreconditionsV2.extraSigners[i] = SignerKey.decode(stream); + decodedPreconditionsV2.extraSigners[i] = SignerKey.decode(stream, maxDepth); } return decodedPreconditionsV2; } + public static PreconditionsV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PreconditionsV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -121,6 +146,7 @@ public static PreconditionsV2 fromXdrBase64(String xdr) throws IOException { public static PreconditionsV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Price.java b/src/main/java/org/stellar/sdk/xdr/Price.java index 45103908b..76cb34357 100644 --- a/src/main/java/org/stellar/sdk/xdr/Price.java +++ b/src/main/java/org/stellar/sdk/xdr/Price.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { d.encode(stream); } - public static Price decode(XdrDataInputStream stream) throws IOException { + public static Price decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Price decodedPrice = new Price(); - decodedPrice.n = Int32.decode(stream); - decodedPrice.d = Int32.decode(stream); + decodedPrice.n = Int32.decode(stream, maxDepth); + decodedPrice.d = Int32.decode(stream, maxDepth); return decodedPrice; } + public static Price decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Price fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static Price fromXdrBase64(String xdr) throws IOException { public static Price fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PublicKey.java b/src/main/java/org/stellar/sdk/xdr/PublicKey.java index ad193b31a..117b8549b 100644 --- a/src/main/java/org/stellar/sdk/xdr/PublicKey.java +++ b/src/main/java/org/stellar/sdk/xdr/PublicKey.java @@ -39,18 +39,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static PublicKey decode(XdrDataInputStream stream) throws IOException { + public static PublicKey decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; PublicKey decodedPublicKey = new PublicKey(); - PublicKeyType discriminant = PublicKeyType.decode(stream); + PublicKeyType discriminant = PublicKeyType.decode(stream, maxDepth); decodedPublicKey.setDiscriminant(discriminant); switch (decodedPublicKey.getDiscriminant()) { case PUBLIC_KEY_TYPE_ED25519: - decodedPublicKey.ed25519 = Uint256.decode(stream); + decodedPublicKey.ed25519 = Uint256.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedPublicKey; } + public static PublicKey decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static PublicKey fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +69,7 @@ public static PublicKey fromXdrBase64(String xdr) throws IOException { public static PublicKey fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/PublicKeyType.java b/src/main/java/org/stellar/sdk/xdr/PublicKeyType.java index a6d0a5d91..a98410184 100644 --- a/src/main/java/org/stellar/sdk/xdr/PublicKeyType.java +++ b/src/main/java/org/stellar/sdk/xdr/PublicKeyType.java @@ -30,7 +30,8 @@ public int getValue() { return value; } - public static PublicKeyType decode(XdrDataInputStream stream) throws IOException { + public static PublicKeyType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -40,6 +41,10 @@ public static PublicKeyType decode(XdrDataInputStream stream) throws IOException } } + public static PublicKeyType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +57,7 @@ public static PublicKeyType fromXdrBase64(String xdr) throws IOException { public static PublicKeyType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RestoreFootprintOp.java b/src/main/java/org/stellar/sdk/xdr/RestoreFootprintOp.java index 06e746c0a..cf6921e7f 100644 --- a/src/main/java/org/stellar/sdk/xdr/RestoreFootprintOp.java +++ b/src/main/java/org/stellar/sdk/xdr/RestoreFootprintOp.java @@ -32,12 +32,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static RestoreFootprintOp decode(XdrDataInputStream stream) throws IOException { + public static RestoreFootprintOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; RestoreFootprintOp decodedRestoreFootprintOp = new RestoreFootprintOp(); - decodedRestoreFootprintOp.ext = ExtensionPoint.decode(stream); + decodedRestoreFootprintOp.ext = ExtensionPoint.decode(stream, maxDepth); return decodedRestoreFootprintOp; } + public static RestoreFootprintOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static RestoreFootprintOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +55,7 @@ public static RestoreFootprintOp fromXdrBase64(String xdr) throws IOException { public static RestoreFootprintOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResult.java b/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResult.java index 84ab88704..7bf9e0264 100644 --- a/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResult.java +++ b/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResult.java @@ -45,9 +45,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static RestoreFootprintResult decode(XdrDataInputStream stream) throws IOException { + public static RestoreFootprintResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; RestoreFootprintResult decodedRestoreFootprintResult = new RestoreFootprintResult(); - RestoreFootprintResultCode discriminant = RestoreFootprintResultCode.decode(stream); + RestoreFootprintResultCode discriminant = RestoreFootprintResultCode.decode(stream, maxDepth); decodedRestoreFootprintResult.setDiscriminant(discriminant); switch (decodedRestoreFootprintResult.getDiscriminant()) { case RESTORE_FOOTPRINT_SUCCESS: @@ -56,10 +61,16 @@ public static RestoreFootprintResult decode(XdrDataInputStream stream) throws IO case RESTORE_FOOTPRINT_RESOURCE_LIMIT_EXCEEDED: case RESTORE_FOOTPRINT_INSUFFICIENT_REFUNDABLE_FEE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedRestoreFootprintResult; } + public static RestoreFootprintResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static RestoreFootprintResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +79,7 @@ public static RestoreFootprintResult fromXdrBase64(String xdr) throws IOExceptio public static RestoreFootprintResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResultCode.java b/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResultCode.java index 34e5e70fd..a9d6826ad 100644 --- a/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/RestoreFootprintResultCode.java @@ -39,7 +39,9 @@ public int getValue() { return value; } - public static RestoreFootprintResultCode decode(XdrDataInputStream stream) throws IOException { + public static RestoreFootprintResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -55,6 +57,10 @@ public static RestoreFootprintResultCode decode(XdrDataInputStream stream) throw } } + public static RestoreFootprintResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -67,6 +73,7 @@ public static RestoreFootprintResultCode fromXdrBase64(String xdr) throws IOExce public static RestoreFootprintResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipOp.java b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipOp.java index d21176fd9..c624aaed1 100644 --- a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipOp.java +++ b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipOp.java @@ -49,21 +49,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static RevokeSponsorshipOp decode(XdrDataInputStream stream) throws IOException { + public static RevokeSponsorshipOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; RevokeSponsorshipOp decodedRevokeSponsorshipOp = new RevokeSponsorshipOp(); - RevokeSponsorshipType discriminant = RevokeSponsorshipType.decode(stream); + RevokeSponsorshipType discriminant = RevokeSponsorshipType.decode(stream, maxDepth); decodedRevokeSponsorshipOp.setDiscriminant(discriminant); switch (decodedRevokeSponsorshipOp.getDiscriminant()) { case REVOKE_SPONSORSHIP_LEDGER_ENTRY: - decodedRevokeSponsorshipOp.ledgerKey = LedgerKey.decode(stream); + decodedRevokeSponsorshipOp.ledgerKey = LedgerKey.decode(stream, maxDepth); break; case REVOKE_SPONSORSHIP_SIGNER: - decodedRevokeSponsorshipOp.signer = RevokeSponsorshipOpSigner.decode(stream); + decodedRevokeSponsorshipOp.signer = RevokeSponsorshipOpSigner.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedRevokeSponsorshipOp; } + public static RevokeSponsorshipOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static RevokeSponsorshipOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -72,6 +83,7 @@ public static RevokeSponsorshipOp fromXdrBase64(String xdr) throws IOException { public static RevokeSponsorshipOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -99,13 +111,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { signerKey.encode(stream); } - public static RevokeSponsorshipOpSigner decode(XdrDataInputStream stream) throws IOException { + public static RevokeSponsorshipOpSigner decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; RevokeSponsorshipOpSigner decodedRevokeSponsorshipOpSigner = new RevokeSponsorshipOpSigner(); - decodedRevokeSponsorshipOpSigner.accountID = AccountID.decode(stream); - decodedRevokeSponsorshipOpSigner.signerKey = SignerKey.decode(stream); + decodedRevokeSponsorshipOpSigner.accountID = AccountID.decode(stream, maxDepth); + decodedRevokeSponsorshipOpSigner.signerKey = SignerKey.decode(stream, maxDepth); return decodedRevokeSponsorshipOpSigner; } + public static RevokeSponsorshipOpSigner decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static RevokeSponsorshipOpSigner fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -114,6 +135,7 @@ public static RevokeSponsorshipOpSigner fromXdrBase64(String xdr) throws IOExcep public static RevokeSponsorshipOpSigner fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResult.java b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResult.java index f7fa298a2..adac75dbb 100644 --- a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResult.java +++ b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResult.java @@ -49,9 +49,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static RevokeSponsorshipResult decode(XdrDataInputStream stream) throws IOException { + public static RevokeSponsorshipResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; RevokeSponsorshipResult decodedRevokeSponsorshipResult = new RevokeSponsorshipResult(); - RevokeSponsorshipResultCode discriminant = RevokeSponsorshipResultCode.decode(stream); + RevokeSponsorshipResultCode discriminant = RevokeSponsorshipResultCode.decode(stream, maxDepth); decodedRevokeSponsorshipResult.setDiscriminant(discriminant); switch (decodedRevokeSponsorshipResult.getDiscriminant()) { case REVOKE_SPONSORSHIP_SUCCESS: @@ -62,10 +67,16 @@ public static RevokeSponsorshipResult decode(XdrDataInputStream stream) throws I case REVOKE_SPONSORSHIP_ONLY_TRANSFERABLE: case REVOKE_SPONSORSHIP_MALFORMED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedRevokeSponsorshipResult; } + public static RevokeSponsorshipResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static RevokeSponsorshipResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -74,6 +85,7 @@ public static RevokeSponsorshipResult fromXdrBase64(String xdr) throws IOExcepti public static RevokeSponsorshipResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResultCode.java b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResultCode.java index 7ff7a35f3..a7c580397 100644 --- a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipResultCode.java @@ -43,7 +43,9 @@ public int getValue() { return value; } - public static RevokeSponsorshipResultCode decode(XdrDataInputStream stream) throws IOException { + public static RevokeSponsorshipResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -63,6 +65,10 @@ public static RevokeSponsorshipResultCode decode(XdrDataInputStream stream) thro } } + public static RevokeSponsorshipResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -75,6 +81,7 @@ public static RevokeSponsorshipResultCode fromXdrBase64(String xdr) throws IOExc public static RevokeSponsorshipResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipType.java b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipType.java index 6aec7960c..ded324e15 100644 --- a/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipType.java +++ b/src/main/java/org/stellar/sdk/xdr/RevokeSponsorshipType.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static RevokeSponsorshipType decode(XdrDataInputStream stream) throws IOException { + public static RevokeSponsorshipType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static RevokeSponsorshipType decode(XdrDataInputStream stream) throws IOE } } + public static RevokeSponsorshipType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static RevokeSponsorshipType fromXdrBase64(String xdr) throws IOException public static RevokeSponsorshipType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCAddress.java b/src/main/java/org/stellar/sdk/xdr/SCAddress.java index 16c98ecfb..c2fffa1c3 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCAddress.java +++ b/src/main/java/org/stellar/sdk/xdr/SCAddress.java @@ -63,30 +63,40 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCAddress decode(XdrDataInputStream stream) throws IOException { + public static SCAddress decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCAddress decodedSCAddress = new SCAddress(); - SCAddressType discriminant = SCAddressType.decode(stream); + SCAddressType discriminant = SCAddressType.decode(stream, maxDepth); decodedSCAddress.setDiscriminant(discriminant); switch (decodedSCAddress.getDiscriminant()) { case SC_ADDRESS_TYPE_ACCOUNT: - decodedSCAddress.accountId = AccountID.decode(stream); + decodedSCAddress.accountId = AccountID.decode(stream, maxDepth); break; case SC_ADDRESS_TYPE_CONTRACT: - decodedSCAddress.contractId = ContractID.decode(stream); + decodedSCAddress.contractId = ContractID.decode(stream, maxDepth); break; case SC_ADDRESS_TYPE_MUXED_ACCOUNT: - decodedSCAddress.muxedAccount = MuxedEd25519Account.decode(stream); + decodedSCAddress.muxedAccount = MuxedEd25519Account.decode(stream, maxDepth); break; case SC_ADDRESS_TYPE_CLAIMABLE_BALANCE: - decodedSCAddress.claimableBalanceId = ClaimableBalanceID.decode(stream); + decodedSCAddress.claimableBalanceId = ClaimableBalanceID.decode(stream, maxDepth); break; case SC_ADDRESS_TYPE_LIQUIDITY_POOL: - decodedSCAddress.liquidityPoolId = PoolID.decode(stream); + decodedSCAddress.liquidityPoolId = PoolID.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCAddress; } + public static SCAddress decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCAddress fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -95,6 +105,7 @@ public static SCAddress fromXdrBase64(String xdr) throws IOException { public static SCAddress fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCAddressType.java b/src/main/java/org/stellar/sdk/xdr/SCAddressType.java index 5c7cadabc..1a7a10852 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCAddressType.java +++ b/src/main/java/org/stellar/sdk/xdr/SCAddressType.java @@ -38,7 +38,8 @@ public int getValue() { return value; } - public static SCAddressType decode(XdrDataInputStream stream) throws IOException { + public static SCAddressType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -56,6 +57,10 @@ public static SCAddressType decode(XdrDataInputStream stream) throws IOException } } + public static SCAddressType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -68,6 +73,7 @@ public static SCAddressType fromXdrBase64(String xdr) throws IOException { public static SCAddressType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCBytes.java b/src/main/java/org/stellar/sdk/xdr/SCBytes.java index 97f1bc6da..377a9fcdf 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCBytes.java +++ b/src/main/java/org/stellar/sdk/xdr/SCBytes.java @@ -29,14 +29,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(getSCBytes(), 0, SCBytesSize); } - public static SCBytes decode(XdrDataInputStream stream) throws IOException { + public static SCBytes decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCBytes decodedSCBytes = new SCBytes(); int SCBytesSize = stream.readInt(); + if (SCBytesSize < 0) { + throw new IOException("SCBytes size " + SCBytesSize + " is negative"); + } + int SCBytesRemainingInputLen = stream.getRemainingInputLen(); + if (SCBytesRemainingInputLen >= 0 && SCBytesRemainingInputLen < SCBytesSize) { + throw new IOException( + "SCBytes size " + + SCBytesSize + + " exceeds remaining input length " + + SCBytesRemainingInputLen); + } decodedSCBytes.SCBytes = new byte[SCBytesSize]; - stream.read(decodedSCBytes.SCBytes, 0, SCBytesSize); + stream.readPaddedData(decodedSCBytes.SCBytes, 0, SCBytesSize); return decodedSCBytes; } + public static SCBytes decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCBytes fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +64,7 @@ public static SCBytes fromXdrBase64(String xdr) throws IOException { public static SCBytes fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCContractInstance.java b/src/main/java/org/stellar/sdk/xdr/SCContractInstance.java index 4acb49a0a..b1a7edc74 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCContractInstance.java +++ b/src/main/java/org/stellar/sdk/xdr/SCContractInstance.java @@ -39,16 +39,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCContractInstance decode(XdrDataInputStream stream) throws IOException { + public static SCContractInstance decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCContractInstance decodedSCContractInstance = new SCContractInstance(); - decodedSCContractInstance.executable = ContractExecutable.decode(stream); - int storagePresent = stream.readInt(); - if (storagePresent != 0) { - decodedSCContractInstance.storage = SCMap.decode(stream); + decodedSCContractInstance.executable = ContractExecutable.decode(stream, maxDepth); + boolean storagePresent = stream.readXdrBoolean(); + if (storagePresent) { + decodedSCContractInstance.storage = SCMap.decode(stream, maxDepth); } return decodedSCContractInstance; } + public static SCContractInstance decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCContractInstance fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -57,6 +66,7 @@ public static SCContractInstance fromXdrBase64(String xdr) throws IOException { public static SCContractInstance fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCEnvMetaEntry.java b/src/main/java/org/stellar/sdk/xdr/SCEnvMetaEntry.java index ae112b266..faef60763 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCEnvMetaEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/SCEnvMetaEntry.java @@ -42,18 +42,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCEnvMetaEntry decode(XdrDataInputStream stream) throws IOException { + public static SCEnvMetaEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCEnvMetaEntry decodedSCEnvMetaEntry = new SCEnvMetaEntry(); - SCEnvMetaKind discriminant = SCEnvMetaKind.decode(stream); + SCEnvMetaKind discriminant = SCEnvMetaKind.decode(stream, maxDepth); decodedSCEnvMetaEntry.setDiscriminant(discriminant); switch (decodedSCEnvMetaEntry.getDiscriminant()) { case SC_ENV_META_KIND_INTERFACE_VERSION: - decodedSCEnvMetaEntry.interfaceVersion = SCEnvMetaEntryInterfaceVersion.decode(stream); + decodedSCEnvMetaEntry.interfaceVersion = + SCEnvMetaEntryInterfaceVersion.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCEnvMetaEntry; } + public static SCEnvMetaEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCEnvMetaEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +73,7 @@ public static SCEnvMetaEntry fromXdrBase64(String xdr) throws IOException { public static SCEnvMetaEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -88,15 +100,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { preRelease.encode(stream); } - public static SCEnvMetaEntryInterfaceVersion decode(XdrDataInputStream stream) + public static SCEnvMetaEntryInterfaceVersion decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCEnvMetaEntryInterfaceVersion decodedSCEnvMetaEntryInterfaceVersion = new SCEnvMetaEntryInterfaceVersion(); - decodedSCEnvMetaEntryInterfaceVersion.protocol = Uint32.decode(stream); - decodedSCEnvMetaEntryInterfaceVersion.preRelease = Uint32.decode(stream); + decodedSCEnvMetaEntryInterfaceVersion.protocol = Uint32.decode(stream, maxDepth); + decodedSCEnvMetaEntryInterfaceVersion.preRelease = Uint32.decode(stream, maxDepth); return decodedSCEnvMetaEntryInterfaceVersion; } + public static SCEnvMetaEntryInterfaceVersion decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCEnvMetaEntryInterfaceVersion fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -105,6 +126,7 @@ public static SCEnvMetaEntryInterfaceVersion fromXdrBase64(String xdr) throws IO public static SCEnvMetaEntryInterfaceVersion fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCEnvMetaKind.java b/src/main/java/org/stellar/sdk/xdr/SCEnvMetaKind.java index 3803e2bda..41b760daf 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCEnvMetaKind.java +++ b/src/main/java/org/stellar/sdk/xdr/SCEnvMetaKind.java @@ -30,7 +30,8 @@ public int getValue() { return value; } - public static SCEnvMetaKind decode(XdrDataInputStream stream) throws IOException { + public static SCEnvMetaKind decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -40,6 +41,10 @@ public static SCEnvMetaKind decode(XdrDataInputStream stream) throws IOException } } + public static SCEnvMetaKind decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +57,7 @@ public static SCEnvMetaKind fromXdrBase64(String xdr) throws IOException { public static SCEnvMetaKind fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCError.java b/src/main/java/org/stellar/sdk/xdr/SCError.java index d5968f401..99f45c2ef 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCError.java +++ b/src/main/java/org/stellar/sdk/xdr/SCError.java @@ -61,13 +61,17 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCError decode(XdrDataInputStream stream) throws IOException { + public static SCError decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCError decodedSCError = new SCError(); - SCErrorType discriminant = SCErrorType.decode(stream); + SCErrorType discriminant = SCErrorType.decode(stream, maxDepth); decodedSCError.setDiscriminant(discriminant); switch (decodedSCError.getDiscriminant()) { case SCE_CONTRACT: - decodedSCError.contractCode = Uint32.decode(stream); + decodedSCError.contractCode = Uint32.decode(stream, maxDepth); break; case SCE_WASM_VM: case SCE_CONTEXT: @@ -78,12 +82,18 @@ public static SCError decode(XdrDataInputStream stream) throws IOException { case SCE_BUDGET: case SCE_VALUE: case SCE_AUTH: - decodedSCError.code = SCErrorCode.decode(stream); + decodedSCError.code = SCErrorCode.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCError; } + public static SCError decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCError fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -92,6 +102,7 @@ public static SCError fromXdrBase64(String xdr) throws IOException { public static SCError fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCErrorCode.java b/src/main/java/org/stellar/sdk/xdr/SCErrorCode.java index 36495ffdd..d743209dc 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCErrorCode.java +++ b/src/main/java/org/stellar/sdk/xdr/SCErrorCode.java @@ -48,7 +48,8 @@ public int getValue() { return value; } - public static SCErrorCode decode(XdrDataInputStream stream) throws IOException { + public static SCErrorCode decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -76,6 +77,10 @@ public static SCErrorCode decode(XdrDataInputStream stream) throws IOException { } } + public static SCErrorCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -88,6 +93,7 @@ public static SCErrorCode fromXdrBase64(String xdr) throws IOException { public static SCErrorCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCErrorType.java b/src/main/java/org/stellar/sdk/xdr/SCErrorType.java index 150a69980..6963ee3f5 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCErrorType.java +++ b/src/main/java/org/stellar/sdk/xdr/SCErrorType.java @@ -48,7 +48,8 @@ public int getValue() { return value; } - public static SCErrorType decode(XdrDataInputStream stream) throws IOException { + public static SCErrorType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -76,6 +77,10 @@ public static SCErrorType decode(XdrDataInputStream stream) throws IOException { } } + public static SCErrorType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -88,6 +93,7 @@ public static SCErrorType fromXdrBase64(String xdr) throws IOException { public static SCErrorType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCMap.java b/src/main/java/org/stellar/sdk/xdr/SCMap.java index 80d07ae8d..1c76fc8d6 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCMap.java +++ b/src/main/java/org/stellar/sdk/xdr/SCMap.java @@ -31,16 +31,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCMap decode(XdrDataInputStream stream) throws IOException { + public static SCMap decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCMap decodedSCMap = new SCMap(); int SCMapSize = stream.readInt(); + if (SCMapSize < 0) { + throw new IOException("SCMap size " + SCMapSize + " is negative"); + } + int SCMapRemainingInputLen = stream.getRemainingInputLen(); + if (SCMapRemainingInputLen >= 0 && SCMapRemainingInputLen < SCMapSize) { + throw new IOException( + "SCMap size " + SCMapSize + " exceeds remaining input length " + SCMapRemainingInputLen); + } decodedSCMap.SCMap = new SCMapEntry[SCMapSize]; for (int i = 0; i < SCMapSize; i++) { - decodedSCMap.SCMap[i] = SCMapEntry.decode(stream); + decodedSCMap.SCMap[i] = SCMapEntry.decode(stream, maxDepth); } return decodedSCMap; } + public static SCMap decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCMap fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +65,7 @@ public static SCMap fromXdrBase64(String xdr) throws IOException { public static SCMap fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCMapEntry.java b/src/main/java/org/stellar/sdk/xdr/SCMapEntry.java index 506fe1716..f0ac5f259 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCMapEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/SCMapEntry.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { val.encode(stream); } - public static SCMapEntry decode(XdrDataInputStream stream) throws IOException { + public static SCMapEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCMapEntry decodedSCMapEntry = new SCMapEntry(); - decodedSCMapEntry.key = SCVal.decode(stream); - decodedSCMapEntry.val = SCVal.decode(stream); + decodedSCMapEntry.key = SCVal.decode(stream, maxDepth); + decodedSCMapEntry.val = SCVal.decode(stream, maxDepth); return decodedSCMapEntry; } + public static SCMapEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCMapEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static SCMapEntry fromXdrBase64(String xdr) throws IOException { public static SCMapEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCMetaEntry.java b/src/main/java/org/stellar/sdk/xdr/SCMetaEntry.java index 4ad1dfaa4..08b507fe6 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCMetaEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/SCMetaEntry.java @@ -39,18 +39,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCMetaEntry decode(XdrDataInputStream stream) throws IOException { + public static SCMetaEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCMetaEntry decodedSCMetaEntry = new SCMetaEntry(); - SCMetaKind discriminant = SCMetaKind.decode(stream); + SCMetaKind discriminant = SCMetaKind.decode(stream, maxDepth); decodedSCMetaEntry.setDiscriminant(discriminant); switch (decodedSCMetaEntry.getDiscriminant()) { case SC_META_V0: - decodedSCMetaEntry.v0 = SCMetaV0.decode(stream); + decodedSCMetaEntry.v0 = SCMetaV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCMetaEntry; } + public static SCMetaEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCMetaEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +69,7 @@ public static SCMetaEntry fromXdrBase64(String xdr) throws IOException { public static SCMetaEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCMetaKind.java b/src/main/java/org/stellar/sdk/xdr/SCMetaKind.java index eeb20bc1a..ef70628ca 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCMetaKind.java +++ b/src/main/java/org/stellar/sdk/xdr/SCMetaKind.java @@ -30,7 +30,8 @@ public int getValue() { return value; } - public static SCMetaKind decode(XdrDataInputStream stream) throws IOException { + public static SCMetaKind decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -40,6 +41,10 @@ public static SCMetaKind decode(XdrDataInputStream stream) throws IOException { } } + public static SCMetaKind decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +57,7 @@ public static SCMetaKind fromXdrBase64(String xdr) throws IOException { public static SCMetaKind fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCMetaV0.java b/src/main/java/org/stellar/sdk/xdr/SCMetaV0.java index e112d2bf6..f9aa7aa37 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCMetaV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCMetaV0.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { val.encode(stream); } - public static SCMetaV0 decode(XdrDataInputStream stream) throws IOException { + public static SCMetaV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCMetaV0 decodedSCMetaV0 = new SCMetaV0(); - decodedSCMetaV0.key = XdrString.decode(stream, Integer.MAX_VALUE); - decodedSCMetaV0.val = XdrString.decode(stream, Integer.MAX_VALUE); + decodedSCMetaV0.key = XdrString.decode(stream, maxDepth, Integer.MAX_VALUE); + decodedSCMetaV0.val = XdrString.decode(stream, maxDepth, Integer.MAX_VALUE); return decodedSCMetaV0; } + public static SCMetaV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCMetaV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static SCMetaV0 fromXdrBase64(String xdr) throws IOException { public static SCMetaV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCNonceKey.java b/src/main/java/org/stellar/sdk/xdr/SCNonceKey.java index 0488817a2..e4d13cc41 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCNonceKey.java +++ b/src/main/java/org/stellar/sdk/xdr/SCNonceKey.java @@ -31,12 +31,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { nonce.encode(stream); } - public static SCNonceKey decode(XdrDataInputStream stream) throws IOException { + public static SCNonceKey decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCNonceKey decodedSCNonceKey = new SCNonceKey(); - decodedSCNonceKey.nonce = Int64.decode(stream); + decodedSCNonceKey.nonce = Int64.decode(stream, maxDepth); return decodedSCNonceKey; } + public static SCNonceKey decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCNonceKey fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +53,7 @@ public static SCNonceKey fromXdrBase64(String xdr) throws IOException { public static SCNonceKey fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPBallot.java b/src/main/java/org/stellar/sdk/xdr/SCPBallot.java index 242672404..7b091769a 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPBallot.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPBallot.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { value.encode(stream); } - public static SCPBallot decode(XdrDataInputStream stream) throws IOException { + public static SCPBallot decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPBallot decodedSCPBallot = new SCPBallot(); - decodedSCPBallot.counter = Uint32.decode(stream); - decodedSCPBallot.value = Value.decode(stream); + decodedSCPBallot.counter = Uint32.decode(stream, maxDepth); + decodedSCPBallot.value = Value.decode(stream, maxDepth); return decodedSCPBallot; } + public static SCPBallot decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPBallot fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static SCPBallot fromXdrBase64(String xdr) throws IOException { public static SCPBallot fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPEnvelope.java b/src/main/java/org/stellar/sdk/xdr/SCPEnvelope.java index e8827ef16..39022189b 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPEnvelope.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPEnvelope.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { signature.encode(stream); } - public static SCPEnvelope decode(XdrDataInputStream stream) throws IOException { + public static SCPEnvelope decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPEnvelope decodedSCPEnvelope = new SCPEnvelope(); - decodedSCPEnvelope.statement = SCPStatement.decode(stream); - decodedSCPEnvelope.signature = Signature.decode(stream); + decodedSCPEnvelope.statement = SCPStatement.decode(stream, maxDepth); + decodedSCPEnvelope.signature = Signature.decode(stream, maxDepth); return decodedSCPEnvelope; } + public static SCPEnvelope decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPEnvelope fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static SCPEnvelope fromXdrBase64(String xdr) throws IOException { public static SCPEnvelope fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntry.java b/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntry.java index b29299815..d2ff18836 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntry.java @@ -39,18 +39,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCPHistoryEntry decode(XdrDataInputStream stream) throws IOException { + public static SCPHistoryEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPHistoryEntry decodedSCPHistoryEntry = new SCPHistoryEntry(); Integer discriminant = stream.readInt(); decodedSCPHistoryEntry.setDiscriminant(discriminant); switch (decodedSCPHistoryEntry.getDiscriminant()) { case 0: - decodedSCPHistoryEntry.v0 = SCPHistoryEntryV0.decode(stream); + decodedSCPHistoryEntry.v0 = SCPHistoryEntryV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCPHistoryEntry; } + public static SCPHistoryEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPHistoryEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +69,7 @@ public static SCPHistoryEntry fromXdrBase64(String xdr) throws IOException { public static SCPHistoryEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntryV0.java b/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntryV0.java index a03457691..c0abf6fba 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntryV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPHistoryEntryV0.java @@ -39,17 +39,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { ledgerMessages.encode(stream); } - public static SCPHistoryEntryV0 decode(XdrDataInputStream stream) throws IOException { + public static SCPHistoryEntryV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPHistoryEntryV0 decodedSCPHistoryEntryV0 = new SCPHistoryEntryV0(); int quorumSetsSize = stream.readInt(); + if (quorumSetsSize < 0) { + throw new IOException("quorumSets size " + quorumSetsSize + " is negative"); + } + int quorumSetsRemainingInputLen = stream.getRemainingInputLen(); + if (quorumSetsRemainingInputLen >= 0 && quorumSetsRemainingInputLen < quorumSetsSize) { + throw new IOException( + "quorumSets size " + + quorumSetsSize + + " exceeds remaining input length " + + quorumSetsRemainingInputLen); + } decodedSCPHistoryEntryV0.quorumSets = new SCPQuorumSet[quorumSetsSize]; for (int i = 0; i < quorumSetsSize; i++) { - decodedSCPHistoryEntryV0.quorumSets[i] = SCPQuorumSet.decode(stream); + decodedSCPHistoryEntryV0.quorumSets[i] = SCPQuorumSet.decode(stream, maxDepth); } - decodedSCPHistoryEntryV0.ledgerMessages = LedgerSCPMessages.decode(stream); + decodedSCPHistoryEntryV0.ledgerMessages = LedgerSCPMessages.decode(stream, maxDepth); return decodedSCPHistoryEntryV0; } + public static SCPHistoryEntryV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPHistoryEntryV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +78,7 @@ public static SCPHistoryEntryV0 fromXdrBase64(String xdr) throws IOException { public static SCPHistoryEntryV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPNomination.java b/src/main/java/org/stellar/sdk/xdr/SCPNomination.java index c44fd614f..f00d0f1fb 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPNomination.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPNomination.java @@ -46,22 +46,49 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCPNomination decode(XdrDataInputStream stream) throws IOException { + public static SCPNomination decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPNomination decodedSCPNomination = new SCPNomination(); - decodedSCPNomination.quorumSetHash = Hash.decode(stream); + decodedSCPNomination.quorumSetHash = Hash.decode(stream, maxDepth); int votesSize = stream.readInt(); + if (votesSize < 0) { + throw new IOException("votes size " + votesSize + " is negative"); + } + int votesRemainingInputLen = stream.getRemainingInputLen(); + if (votesRemainingInputLen >= 0 && votesRemainingInputLen < votesSize) { + throw new IOException( + "votes size " + votesSize + " exceeds remaining input length " + votesRemainingInputLen); + } decodedSCPNomination.votes = new Value[votesSize]; for (int i = 0; i < votesSize; i++) { - decodedSCPNomination.votes[i] = Value.decode(stream); + decodedSCPNomination.votes[i] = Value.decode(stream, maxDepth); } int acceptedSize = stream.readInt(); + if (acceptedSize < 0) { + throw new IOException("accepted size " + acceptedSize + " is negative"); + } + int acceptedRemainingInputLen = stream.getRemainingInputLen(); + if (acceptedRemainingInputLen >= 0 && acceptedRemainingInputLen < acceptedSize) { + throw new IOException( + "accepted size " + + acceptedSize + + " exceeds remaining input length " + + acceptedRemainingInputLen); + } decodedSCPNomination.accepted = new Value[acceptedSize]; for (int i = 0; i < acceptedSize; i++) { - decodedSCPNomination.accepted[i] = Value.decode(stream); + decodedSCPNomination.accepted[i] = Value.decode(stream, maxDepth); } return decodedSCPNomination; } + public static SCPNomination decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPNomination fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -70,6 +97,7 @@ public static SCPNomination fromXdrBase64(String xdr) throws IOException { public static SCPNomination fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPQuorumSet.java b/src/main/java/org/stellar/sdk/xdr/SCPQuorumSet.java index 18eac8198..601f2d482 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPQuorumSet.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPQuorumSet.java @@ -46,22 +46,52 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCPQuorumSet decode(XdrDataInputStream stream) throws IOException { + public static SCPQuorumSet decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPQuorumSet decodedSCPQuorumSet = new SCPQuorumSet(); - decodedSCPQuorumSet.threshold = Uint32.decode(stream); + decodedSCPQuorumSet.threshold = Uint32.decode(stream, maxDepth); int validatorsSize = stream.readInt(); + if (validatorsSize < 0) { + throw new IOException("validators size " + validatorsSize + " is negative"); + } + int validatorsRemainingInputLen = stream.getRemainingInputLen(); + if (validatorsRemainingInputLen >= 0 && validatorsRemainingInputLen < validatorsSize) { + throw new IOException( + "validators size " + + validatorsSize + + " exceeds remaining input length " + + validatorsRemainingInputLen); + } decodedSCPQuorumSet.validators = new NodeID[validatorsSize]; for (int i = 0; i < validatorsSize; i++) { - decodedSCPQuorumSet.validators[i] = NodeID.decode(stream); + decodedSCPQuorumSet.validators[i] = NodeID.decode(stream, maxDepth); } int innerSetsSize = stream.readInt(); + if (innerSetsSize < 0) { + throw new IOException("innerSets size " + innerSetsSize + " is negative"); + } + int innerSetsRemainingInputLen = stream.getRemainingInputLen(); + if (innerSetsRemainingInputLen >= 0 && innerSetsRemainingInputLen < innerSetsSize) { + throw new IOException( + "innerSets size " + + innerSetsSize + + " exceeds remaining input length " + + innerSetsRemainingInputLen); + } decodedSCPQuorumSet.innerSets = new SCPQuorumSet[innerSetsSize]; for (int i = 0; i < innerSetsSize; i++) { - decodedSCPQuorumSet.innerSets[i] = SCPQuorumSet.decode(stream); + decodedSCPQuorumSet.innerSets[i] = SCPQuorumSet.decode(stream, maxDepth); } return decodedSCPQuorumSet; } + public static SCPQuorumSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPQuorumSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -70,6 +100,7 @@ public static SCPQuorumSet fromXdrBase64(String xdr) throws IOException { public static SCPQuorumSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPStatement.java b/src/main/java/org/stellar/sdk/xdr/SCPStatement.java index 5f246ffb1..7040e7a29 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPStatement.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPStatement.java @@ -70,14 +70,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { pledges.encode(stream); } - public static SCPStatement decode(XdrDataInputStream stream) throws IOException { + public static SCPStatement decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPStatement decodedSCPStatement = new SCPStatement(); - decodedSCPStatement.nodeID = NodeID.decode(stream); - decodedSCPStatement.slotIndex = Uint64.decode(stream); - decodedSCPStatement.pledges = SCPStatementPledges.decode(stream); + decodedSCPStatement.nodeID = NodeID.decode(stream, maxDepth); + decodedSCPStatement.slotIndex = Uint64.decode(stream, maxDepth); + decodedSCPStatement.pledges = SCPStatementPledges.decode(stream, maxDepth); return decodedSCPStatement; } + public static SCPStatement decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPStatement fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -86,6 +94,7 @@ public static SCPStatement fromXdrBase64(String xdr) throws IOException { public static SCPStatement fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -155,27 +164,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCPStatementPledges decode(XdrDataInputStream stream) throws IOException { + public static SCPStatementPledges decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPStatementPledges decodedSCPStatementPledges = new SCPStatementPledges(); - SCPStatementType discriminant = SCPStatementType.decode(stream); + SCPStatementType discriminant = SCPStatementType.decode(stream, maxDepth); decodedSCPStatementPledges.setDiscriminant(discriminant); switch (decodedSCPStatementPledges.getDiscriminant()) { case SCP_ST_PREPARE: - decodedSCPStatementPledges.prepare = SCPStatementPrepare.decode(stream); + decodedSCPStatementPledges.prepare = SCPStatementPrepare.decode(stream, maxDepth); break; case SCP_ST_CONFIRM: - decodedSCPStatementPledges.confirm = SCPStatementConfirm.decode(stream); + decodedSCPStatementPledges.confirm = SCPStatementConfirm.decode(stream, maxDepth); break; case SCP_ST_EXTERNALIZE: - decodedSCPStatementPledges.externalize = SCPStatementExternalize.decode(stream); + decodedSCPStatementPledges.externalize = SCPStatementExternalize.decode(stream, maxDepth); break; case SCP_ST_NOMINATE: - decodedSCPStatementPledges.nominate = SCPNomination.decode(stream); + decodedSCPStatementPledges.nominate = SCPNomination.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCPStatementPledges; } + public static SCPStatementPledges decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPStatementPledges fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -184,6 +204,7 @@ public static SCPStatementPledges fromXdrBase64(String xdr) throws IOException { public static SCPStatementPledges fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -233,23 +254,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { nH.encode(stream); } - public static SCPStatementPrepare decode(XdrDataInputStream stream) throws IOException { + public static SCPStatementPrepare decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPStatementPrepare decodedSCPStatementPrepare = new SCPStatementPrepare(); - decodedSCPStatementPrepare.quorumSetHash = Hash.decode(stream); - decodedSCPStatementPrepare.ballot = SCPBallot.decode(stream); - int preparedPresent = stream.readInt(); - if (preparedPresent != 0) { - decodedSCPStatementPrepare.prepared = SCPBallot.decode(stream); + decodedSCPStatementPrepare.quorumSetHash = Hash.decode(stream, maxDepth); + decodedSCPStatementPrepare.ballot = SCPBallot.decode(stream, maxDepth); + boolean preparedPresent = stream.readXdrBoolean(); + if (preparedPresent) { + decodedSCPStatementPrepare.prepared = SCPBallot.decode(stream, maxDepth); } - int preparedPrimePresent = stream.readInt(); - if (preparedPrimePresent != 0) { - decodedSCPStatementPrepare.preparedPrime = SCPBallot.decode(stream); + boolean preparedPrimePresent = stream.readXdrBoolean(); + if (preparedPrimePresent) { + decodedSCPStatementPrepare.preparedPrime = SCPBallot.decode(stream, maxDepth); } - decodedSCPStatementPrepare.nC = Uint32.decode(stream); - decodedSCPStatementPrepare.nH = Uint32.decode(stream); + decodedSCPStatementPrepare.nC = Uint32.decode(stream, maxDepth); + decodedSCPStatementPrepare.nH = Uint32.decode(stream, maxDepth); return decodedSCPStatementPrepare; } + public static SCPStatementPrepare decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPStatementPrepare fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -258,6 +288,7 @@ public static SCPStatementPrepare fromXdrBase64(String xdr) throws IOException { public static SCPStatementPrepare fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -295,16 +326,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { quorumSetHash.encode(stream); } - public static SCPStatementConfirm decode(XdrDataInputStream stream) throws IOException { + public static SCPStatementConfirm decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPStatementConfirm decodedSCPStatementConfirm = new SCPStatementConfirm(); - decodedSCPStatementConfirm.ballot = SCPBallot.decode(stream); - decodedSCPStatementConfirm.nPrepared = Uint32.decode(stream); - decodedSCPStatementConfirm.nCommit = Uint32.decode(stream); - decodedSCPStatementConfirm.nH = Uint32.decode(stream); - decodedSCPStatementConfirm.quorumSetHash = Hash.decode(stream); + decodedSCPStatementConfirm.ballot = SCPBallot.decode(stream, maxDepth); + decodedSCPStatementConfirm.nPrepared = Uint32.decode(stream, maxDepth); + decodedSCPStatementConfirm.nCommit = Uint32.decode(stream, maxDepth); + decodedSCPStatementConfirm.nH = Uint32.decode(stream, maxDepth); + decodedSCPStatementConfirm.quorumSetHash = Hash.decode(stream, maxDepth); return decodedSCPStatementConfirm; } + public static SCPStatementConfirm decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPStatementConfirm fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -313,6 +353,7 @@ public static SCPStatementConfirm fromXdrBase64(String xdr) throws IOException { public static SCPStatementConfirm fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -344,14 +385,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { commitQuorumSetHash.encode(stream); } - public static SCPStatementExternalize decode(XdrDataInputStream stream) throws IOException { + public static SCPStatementExternalize decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCPStatementExternalize decodedSCPStatementExternalize = new SCPStatementExternalize(); - decodedSCPStatementExternalize.commit = SCPBallot.decode(stream); - decodedSCPStatementExternalize.nH = Uint32.decode(stream); - decodedSCPStatementExternalize.commitQuorumSetHash = Hash.decode(stream); + decodedSCPStatementExternalize.commit = SCPBallot.decode(stream, maxDepth); + decodedSCPStatementExternalize.nH = Uint32.decode(stream, maxDepth); + decodedSCPStatementExternalize.commitQuorumSetHash = Hash.decode(stream, maxDepth); return decodedSCPStatementExternalize; } + public static SCPStatementExternalize decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCPStatementExternalize fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -360,6 +410,7 @@ public static SCPStatementExternalize fromXdrBase64(String xdr) throws IOExcepti public static SCPStatementExternalize fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCPStatementType.java b/src/main/java/org/stellar/sdk/xdr/SCPStatementType.java index 977685672..a2242f8ed 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCPStatementType.java +++ b/src/main/java/org/stellar/sdk/xdr/SCPStatementType.java @@ -36,7 +36,9 @@ public int getValue() { return value; } - public static SCPStatementType decode(XdrDataInputStream stream) throws IOException { + public static SCPStatementType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -52,6 +54,10 @@ public static SCPStatementType decode(XdrDataInputStream stream) throws IOExcept } } + public static SCPStatementType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -64,6 +70,7 @@ public static SCPStatementType fromXdrBase64(String xdr) throws IOException { public static SCPStatementType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecEntry.java b/src/main/java/org/stellar/sdk/xdr/SCSpecEntry.java index ea51b4063..84484fdf0 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecEntry.java @@ -69,33 +69,43 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCSpecEntry decode(XdrDataInputStream stream) throws IOException { + public static SCSpecEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecEntry decodedSCSpecEntry = new SCSpecEntry(); - SCSpecEntryKind discriminant = SCSpecEntryKind.decode(stream); + SCSpecEntryKind discriminant = SCSpecEntryKind.decode(stream, maxDepth); decodedSCSpecEntry.setDiscriminant(discriminant); switch (decodedSCSpecEntry.getDiscriminant()) { case SC_SPEC_ENTRY_FUNCTION_V0: - decodedSCSpecEntry.functionV0 = SCSpecFunctionV0.decode(stream); + decodedSCSpecEntry.functionV0 = SCSpecFunctionV0.decode(stream, maxDepth); break; case SC_SPEC_ENTRY_UDT_STRUCT_V0: - decodedSCSpecEntry.udtStructV0 = SCSpecUDTStructV0.decode(stream); + decodedSCSpecEntry.udtStructV0 = SCSpecUDTStructV0.decode(stream, maxDepth); break; case SC_SPEC_ENTRY_UDT_UNION_V0: - decodedSCSpecEntry.udtUnionV0 = SCSpecUDTUnionV0.decode(stream); + decodedSCSpecEntry.udtUnionV0 = SCSpecUDTUnionV0.decode(stream, maxDepth); break; case SC_SPEC_ENTRY_UDT_ENUM_V0: - decodedSCSpecEntry.udtEnumV0 = SCSpecUDTEnumV0.decode(stream); + decodedSCSpecEntry.udtEnumV0 = SCSpecUDTEnumV0.decode(stream, maxDepth); break; case SC_SPEC_ENTRY_UDT_ERROR_ENUM_V0: - decodedSCSpecEntry.udtErrorEnumV0 = SCSpecUDTErrorEnumV0.decode(stream); + decodedSCSpecEntry.udtErrorEnumV0 = SCSpecUDTErrorEnumV0.decode(stream, maxDepth); break; case SC_SPEC_ENTRY_EVENT_V0: - decodedSCSpecEntry.eventV0 = SCSpecEventV0.decode(stream); + decodedSCSpecEntry.eventV0 = SCSpecEventV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCSpecEntry; } + public static SCSpecEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -104,6 +114,7 @@ public static SCSpecEntry fromXdrBase64(String xdr) throws IOException { public static SCSpecEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecEntryKind.java b/src/main/java/org/stellar/sdk/xdr/SCSpecEntryKind.java index 44ef51270..7a489f454 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecEntryKind.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecEntryKind.java @@ -40,7 +40,8 @@ public int getValue() { return value; } - public static SCSpecEntryKind decode(XdrDataInputStream stream) throws IOException { + public static SCSpecEntryKind decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -60,6 +61,10 @@ public static SCSpecEntryKind decode(XdrDataInputStream stream) throws IOExcepti } } + public static SCSpecEntryKind decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -72,6 +77,7 @@ public static SCSpecEntryKind fromXdrBase64(String xdr) throws IOException { public static SCSpecEntryKind fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecEventDataFormat.java b/src/main/java/org/stellar/sdk/xdr/SCSpecEventDataFormat.java index a600123d4..ea4ea17d2 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecEventDataFormat.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecEventDataFormat.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static SCSpecEventDataFormat decode(XdrDataInputStream stream) throws IOException { + public static SCSpecEventDataFormat decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +50,10 @@ public static SCSpecEventDataFormat decode(XdrDataInputStream stream) throws IOE } } + public static SCSpecEventDataFormat decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static SCSpecEventDataFormat fromXdrBase64(String xdr) throws IOException public static SCSpecEventDataFormat fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamLocationV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamLocationV0.java index 65e12857e..f9f4e2f8d 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamLocationV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamLocationV0.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static SCSpecEventParamLocationV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecEventParamLocationV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static SCSpecEventParamLocationV0 decode(XdrDataInputStream stream) throw } } + public static SCSpecEventParamLocationV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static SCSpecEventParamLocationV0 fromXdrBase64(String xdr) throws IOExce public static SCSpecEventParamLocationV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamV0.java index 2fd8e437b..24fcc7515 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecEventParamV0.java @@ -35,21 +35,38 @@ public class SCSpecEventParamV0 implements XdrElement { private SCSpecEventParamLocationV0 location; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 30) { + throw new IOException("name size " + nameSize + " exceeds max size 30"); + } name.encode(stream); type.encode(stream); location.encode(stream); } - public static SCSpecEventParamV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecEventParamV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecEventParamV0 decodedSCSpecEventParamV0 = new SCSpecEventParamV0(); - decodedSCSpecEventParamV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecEventParamV0.name = XdrString.decode(stream, 30); - decodedSCSpecEventParamV0.type = SCSpecTypeDef.decode(stream); - decodedSCSpecEventParamV0.location = SCSpecEventParamLocationV0.decode(stream); + decodedSCSpecEventParamV0.doc = XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecEventParamV0.name = XdrString.decode(stream, maxDepth, 30); + decodedSCSpecEventParamV0.type = SCSpecTypeDef.decode(stream, maxDepth); + decodedSCSpecEventParamV0.location = SCSpecEventParamLocationV0.decode(stream, maxDepth); return decodedSCSpecEventParamV0; } + public static SCSpecEventParamV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecEventParamV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +75,7 @@ public static SCSpecEventParamV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecEventParamV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecEventV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecEventV0.java index e2564e443..9d83db2c1 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecEventV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecEventV0.java @@ -39,15 +39,29 @@ public class SCSpecEventV0 implements XdrElement { private SCSpecEventDataFormat dataFormat; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int libSize = lib.getBytes().length; + if (libSize > 80) { + throw new IOException("lib size " + libSize + " exceeds max size 80"); + } lib.encode(stream); name.encode(stream); int prefixTopicsSize = getPrefixTopics().length; + if (prefixTopicsSize > 2) { + throw new IOException("prefixTopics size " + prefixTopicsSize + " exceeds max size 2"); + } stream.writeInt(prefixTopicsSize); for (int i = 0; i < prefixTopicsSize; i++) { prefixTopics[i].encode(stream); } int paramsSize = getParams().length; + if (paramsSize > 50) { + throw new IOException("params size " + paramsSize + " exceeds max size 50"); + } stream.writeInt(paramsSize); for (int i = 0; i < paramsSize; i++) { params[i].encode(stream); @@ -55,25 +69,61 @@ public void encode(XdrDataOutputStream stream) throws IOException { dataFormat.encode(stream); } - public static SCSpecEventV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecEventV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecEventV0 decodedSCSpecEventV0 = new SCSpecEventV0(); - decodedSCSpecEventV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecEventV0.lib = XdrString.decode(stream, 80); - decodedSCSpecEventV0.name = SCSymbol.decode(stream); + decodedSCSpecEventV0.doc = XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecEventV0.lib = XdrString.decode(stream, maxDepth, 80); + decodedSCSpecEventV0.name = SCSymbol.decode(stream, maxDepth); int prefixTopicsSize = stream.readInt(); + if (prefixTopicsSize < 0) { + throw new IOException("prefixTopics size " + prefixTopicsSize + " is negative"); + } + if (prefixTopicsSize > 2) { + throw new IOException("prefixTopics size " + prefixTopicsSize + " exceeds max size 2"); + } + int prefixTopicsRemainingInputLen = stream.getRemainingInputLen(); + if (prefixTopicsRemainingInputLen >= 0 && prefixTopicsRemainingInputLen < prefixTopicsSize) { + throw new IOException( + "prefixTopics size " + + prefixTopicsSize + + " exceeds remaining input length " + + prefixTopicsRemainingInputLen); + } decodedSCSpecEventV0.prefixTopics = new SCSymbol[prefixTopicsSize]; for (int i = 0; i < prefixTopicsSize; i++) { - decodedSCSpecEventV0.prefixTopics[i] = SCSymbol.decode(stream); + decodedSCSpecEventV0.prefixTopics[i] = SCSymbol.decode(stream, maxDepth); } int paramsSize = stream.readInt(); + if (paramsSize < 0) { + throw new IOException("params size " + paramsSize + " is negative"); + } + if (paramsSize > 50) { + throw new IOException("params size " + paramsSize + " exceeds max size 50"); + } + int paramsRemainingInputLen = stream.getRemainingInputLen(); + if (paramsRemainingInputLen >= 0 && paramsRemainingInputLen < paramsSize) { + throw new IOException( + "params size " + + paramsSize + + " exceeds remaining input length " + + paramsRemainingInputLen); + } decodedSCSpecEventV0.params = new SCSpecEventParamV0[paramsSize]; for (int i = 0; i < paramsSize; i++) { - decodedSCSpecEventV0.params[i] = SCSpecEventParamV0.decode(stream); + decodedSCSpecEventV0.params[i] = SCSpecEventParamV0.decode(stream, maxDepth); } - decodedSCSpecEventV0.dataFormat = SCSpecEventDataFormat.decode(stream); + decodedSCSpecEventV0.dataFormat = SCSpecEventDataFormat.decode(stream, maxDepth); return decodedSCSpecEventV0; } + public static SCSpecEventV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecEventV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -82,6 +132,7 @@ public static SCSpecEventV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecEventV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionInputV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionInputV0.java index 66ddddc14..b542ca706 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionInputV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionInputV0.java @@ -33,19 +33,37 @@ public class SCSpecFunctionInputV0 implements XdrElement { private SCSpecTypeDef type; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 30) { + throw new IOException("name size " + nameSize + " exceeds max size 30"); + } name.encode(stream); type.encode(stream); } - public static SCSpecFunctionInputV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecFunctionInputV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecFunctionInputV0 decodedSCSpecFunctionInputV0 = new SCSpecFunctionInputV0(); - decodedSCSpecFunctionInputV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecFunctionInputV0.name = XdrString.decode(stream, 30); - decodedSCSpecFunctionInputV0.type = SCSpecTypeDef.decode(stream); + decodedSCSpecFunctionInputV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecFunctionInputV0.name = XdrString.decode(stream, maxDepth, 30); + decodedSCSpecFunctionInputV0.type = SCSpecTypeDef.decode(stream, maxDepth); return decodedSCSpecFunctionInputV0; } + public static SCSpecFunctionInputV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecFunctionInputV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +72,7 @@ public static SCSpecFunctionInputV0 fromXdrBase64(String xdr) throws IOException public static SCSpecFunctionInputV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionV0.java index cfe341ec0..18bf9abe3 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecFunctionV0.java @@ -35,37 +35,84 @@ public class SCSpecFunctionV0 implements XdrElement { private SCSpecTypeDef[] outputs; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); name.encode(stream); int inputsSize = getInputs().length; + if (inputsSize > 10) { + throw new IOException("inputs size " + inputsSize + " exceeds max size 10"); + } stream.writeInt(inputsSize); for (int i = 0; i < inputsSize; i++) { inputs[i].encode(stream); } int outputsSize = getOutputs().length; + if (outputsSize > 1) { + throw new IOException("outputs size " + outputsSize + " exceeds max size 1"); + } stream.writeInt(outputsSize); for (int i = 0; i < outputsSize; i++) { outputs[i].encode(stream); } } - public static SCSpecFunctionV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecFunctionV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecFunctionV0 decodedSCSpecFunctionV0 = new SCSpecFunctionV0(); - decodedSCSpecFunctionV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecFunctionV0.name = SCSymbol.decode(stream); + decodedSCSpecFunctionV0.doc = XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecFunctionV0.name = SCSymbol.decode(stream, maxDepth); int inputsSize = stream.readInt(); + if (inputsSize < 0) { + throw new IOException("inputs size " + inputsSize + " is negative"); + } + if (inputsSize > 10) { + throw new IOException("inputs size " + inputsSize + " exceeds max size 10"); + } + int inputsRemainingInputLen = stream.getRemainingInputLen(); + if (inputsRemainingInputLen >= 0 && inputsRemainingInputLen < inputsSize) { + throw new IOException( + "inputs size " + + inputsSize + + " exceeds remaining input length " + + inputsRemainingInputLen); + } decodedSCSpecFunctionV0.inputs = new SCSpecFunctionInputV0[inputsSize]; for (int i = 0; i < inputsSize; i++) { - decodedSCSpecFunctionV0.inputs[i] = SCSpecFunctionInputV0.decode(stream); + decodedSCSpecFunctionV0.inputs[i] = SCSpecFunctionInputV0.decode(stream, maxDepth); } int outputsSize = stream.readInt(); + if (outputsSize < 0) { + throw new IOException("outputs size " + outputsSize + " is negative"); + } + if (outputsSize > 1) { + throw new IOException("outputs size " + outputsSize + " exceeds max size 1"); + } + int outputsRemainingInputLen = stream.getRemainingInputLen(); + if (outputsRemainingInputLen >= 0 && outputsRemainingInputLen < outputsSize) { + throw new IOException( + "outputs size " + + outputsSize + + " exceeds remaining input length " + + outputsRemainingInputLen); + } decodedSCSpecFunctionV0.outputs = new SCSpecTypeDef[outputsSize]; for (int i = 0; i < outputsSize; i++) { - decodedSCSpecFunctionV0.outputs[i] = SCSpecTypeDef.decode(stream); + decodedSCSpecFunctionV0.outputs[i] = SCSpecTypeDef.decode(stream, maxDepth); } return decodedSCSpecFunctionV0; } + public static SCSpecFunctionV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecFunctionV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -74,6 +121,7 @@ public static SCSpecFunctionV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecFunctionV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecType.java b/src/main/java/org/stellar/sdk/xdr/SCSpecType.java index d9e0cc1bc..151349616 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecType.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecType.java @@ -86,7 +86,8 @@ public int getValue() { return value; } - public static SCSpecType decode(XdrDataInputStream stream) throws IOException { + public static SCSpecType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -146,6 +147,10 @@ public static SCSpecType decode(XdrDataInputStream stream) throws IOException { } } + public static SCSpecType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -158,6 +163,7 @@ public static SCSpecType fromXdrBase64(String xdr) throws IOException { public static SCSpecType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeBytesN.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeBytesN.java index ecb0378d4..4fca0b5e3 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeBytesN.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeBytesN.java @@ -32,12 +32,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { n.encode(stream); } - public static SCSpecTypeBytesN decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeBytesN decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeBytesN decodedSCSpecTypeBytesN = new SCSpecTypeBytesN(); - decodedSCSpecTypeBytesN.n = Uint32.decode(stream); + decodedSCSpecTypeBytesN.n = Uint32.decode(stream, maxDepth); return decodedSCSpecTypeBytesN; } + public static SCSpecTypeBytesN decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeBytesN fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +55,7 @@ public static SCSpecTypeBytesN fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeBytesN fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeDef.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeDef.java index 8d7c96028..09ba8142f 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeDef.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeDef.java @@ -115,9 +115,13 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCSpecTypeDef decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeDef decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeDef decodedSCSpecTypeDef = new SCSpecTypeDef(); - SCSpecType discriminant = SCSpecType.decode(stream); + SCSpecType discriminant = SCSpecType.decode(stream, maxDepth); decodedSCSpecTypeDef.setDiscriminant(discriminant); switch (decodedSCSpecTypeDef.getDiscriminant()) { case SC_SPEC_TYPE_VAL: @@ -141,30 +145,36 @@ public static SCSpecTypeDef decode(XdrDataInputStream stream) throws IOException case SC_SPEC_TYPE_MUXED_ADDRESS: break; case SC_SPEC_TYPE_OPTION: - decodedSCSpecTypeDef.option = SCSpecTypeOption.decode(stream); + decodedSCSpecTypeDef.option = SCSpecTypeOption.decode(stream, maxDepth); break; case SC_SPEC_TYPE_RESULT: - decodedSCSpecTypeDef.result = SCSpecTypeResult.decode(stream); + decodedSCSpecTypeDef.result = SCSpecTypeResult.decode(stream, maxDepth); break; case SC_SPEC_TYPE_VEC: - decodedSCSpecTypeDef.vec = SCSpecTypeVec.decode(stream); + decodedSCSpecTypeDef.vec = SCSpecTypeVec.decode(stream, maxDepth); break; case SC_SPEC_TYPE_MAP: - decodedSCSpecTypeDef.map = SCSpecTypeMap.decode(stream); + decodedSCSpecTypeDef.map = SCSpecTypeMap.decode(stream, maxDepth); break; case SC_SPEC_TYPE_TUPLE: - decodedSCSpecTypeDef.tuple = SCSpecTypeTuple.decode(stream); + decodedSCSpecTypeDef.tuple = SCSpecTypeTuple.decode(stream, maxDepth); break; case SC_SPEC_TYPE_BYTES_N: - decodedSCSpecTypeDef.bytesN = SCSpecTypeBytesN.decode(stream); + decodedSCSpecTypeDef.bytesN = SCSpecTypeBytesN.decode(stream, maxDepth); break; case SC_SPEC_TYPE_UDT: - decodedSCSpecTypeDef.udt = SCSpecTypeUDT.decode(stream); + decodedSCSpecTypeDef.udt = SCSpecTypeUDT.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCSpecTypeDef; } + public static SCSpecTypeDef decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeDef fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -173,6 +183,7 @@ public static SCSpecTypeDef fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeDef fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeMap.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeMap.java index d5e565c33..da7a4c38d 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeMap.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeMap.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { valueType.encode(stream); } - public static SCSpecTypeMap decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeMap decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeMap decodedSCSpecTypeMap = new SCSpecTypeMap(); - decodedSCSpecTypeMap.keyType = SCSpecTypeDef.decode(stream); - decodedSCSpecTypeMap.valueType = SCSpecTypeDef.decode(stream); + decodedSCSpecTypeMap.keyType = SCSpecTypeDef.decode(stream, maxDepth); + decodedSCSpecTypeMap.valueType = SCSpecTypeDef.decode(stream, maxDepth); return decodedSCSpecTypeMap; } + public static SCSpecTypeMap decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeMap fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static SCSpecTypeMap fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeMap fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeOption.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeOption.java index 491c0373d..0f54c0d08 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeOption.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeOption.java @@ -32,12 +32,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { valueType.encode(stream); } - public static SCSpecTypeOption decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeOption decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeOption decodedSCSpecTypeOption = new SCSpecTypeOption(); - decodedSCSpecTypeOption.valueType = SCSpecTypeDef.decode(stream); + decodedSCSpecTypeOption.valueType = SCSpecTypeDef.decode(stream, maxDepth); return decodedSCSpecTypeOption; } + public static SCSpecTypeOption decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeOption fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +55,7 @@ public static SCSpecTypeOption fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeOption fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeResult.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeResult.java index dd8112201..d7edd9290 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeResult.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeResult.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { errorType.encode(stream); } - public static SCSpecTypeResult decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeResult decodedSCSpecTypeResult = new SCSpecTypeResult(); - decodedSCSpecTypeResult.okType = SCSpecTypeDef.decode(stream); - decodedSCSpecTypeResult.errorType = SCSpecTypeDef.decode(stream); + decodedSCSpecTypeResult.okType = SCSpecTypeDef.decode(stream, maxDepth); + decodedSCSpecTypeResult.errorType = SCSpecTypeDef.decode(stream, maxDepth); return decodedSCSpecTypeResult; } + public static SCSpecTypeResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static SCSpecTypeResult fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeTuple.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeTuple.java index c613f24d4..0d5f21462 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeTuple.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeTuple.java @@ -30,22 +30,47 @@ public class SCSpecTypeTuple implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int valueTypesSize = getValueTypes().length; + if (valueTypesSize > 12) { + throw new IOException("valueTypes size " + valueTypesSize + " exceeds max size 12"); + } stream.writeInt(valueTypesSize); for (int i = 0; i < valueTypesSize; i++) { valueTypes[i].encode(stream); } } - public static SCSpecTypeTuple decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeTuple decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeTuple decodedSCSpecTypeTuple = new SCSpecTypeTuple(); int valueTypesSize = stream.readInt(); + if (valueTypesSize < 0) { + throw new IOException("valueTypes size " + valueTypesSize + " is negative"); + } + if (valueTypesSize > 12) { + throw new IOException("valueTypes size " + valueTypesSize + " exceeds max size 12"); + } + int valueTypesRemainingInputLen = stream.getRemainingInputLen(); + if (valueTypesRemainingInputLen >= 0 && valueTypesRemainingInputLen < valueTypesSize) { + throw new IOException( + "valueTypes size " + + valueTypesSize + + " exceeds remaining input length " + + valueTypesRemainingInputLen); + } decodedSCSpecTypeTuple.valueTypes = new SCSpecTypeDef[valueTypesSize]; for (int i = 0; i < valueTypesSize; i++) { - decodedSCSpecTypeTuple.valueTypes[i] = SCSpecTypeDef.decode(stream); + decodedSCSpecTypeTuple.valueTypes[i] = SCSpecTypeDef.decode(stream, maxDepth); } return decodedSCSpecTypeTuple; } + public static SCSpecTypeTuple decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeTuple fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +79,7 @@ public static SCSpecTypeTuple fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeTuple fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeUDT.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeUDT.java index 0699f6097..5759576f7 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeUDT.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeUDT.java @@ -29,15 +29,27 @@ public class SCSpecTypeUDT implements XdrElement { private XdrString name; public void encode(XdrDataOutputStream stream) throws IOException { + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); } - public static SCSpecTypeUDT decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeUDT decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeUDT decodedSCSpecTypeUDT = new SCSpecTypeUDT(); - decodedSCSpecTypeUDT.name = XdrString.decode(stream, 60); + decodedSCSpecTypeUDT.name = XdrString.decode(stream, maxDepth, 60); return decodedSCSpecTypeUDT; } + public static SCSpecTypeUDT decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeUDT fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +58,7 @@ public static SCSpecTypeUDT fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeUDT fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeVec.java b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeVec.java index 8dc2149bb..3d3ca8407 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecTypeVec.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecTypeVec.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { elementType.encode(stream); } - public static SCSpecTypeVec decode(XdrDataInputStream stream) throws IOException { + public static SCSpecTypeVec decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecTypeVec decodedSCSpecTypeVec = new SCSpecTypeVec(); - decodedSCSpecTypeVec.elementType = SCSpecTypeDef.decode(stream); + decodedSCSpecTypeVec.elementType = SCSpecTypeDef.decode(stream, maxDepth); return decodedSCSpecTypeVec; } + public static SCSpecTypeVec decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecTypeVec fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static SCSpecTypeVec fromXdrBase64(String xdr) throws IOException { public static SCSpecTypeVec fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumCaseV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumCaseV0.java index 3bf7e36b2..9495ffc80 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumCaseV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumCaseV0.java @@ -33,19 +33,37 @@ public class SCSpecUDTEnumCaseV0 implements XdrElement { private Uint32 value; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); value.encode(stream); } - public static SCSpecUDTEnumCaseV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTEnumCaseV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTEnumCaseV0 decodedSCSpecUDTEnumCaseV0 = new SCSpecUDTEnumCaseV0(); - decodedSCSpecUDTEnumCaseV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTEnumCaseV0.name = XdrString.decode(stream, 60); - decodedSCSpecUDTEnumCaseV0.value = Uint32.decode(stream); + decodedSCSpecUDTEnumCaseV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTEnumCaseV0.name = XdrString.decode(stream, maxDepth, 60); + decodedSCSpecUDTEnumCaseV0.value = Uint32.decode(stream, maxDepth); return decodedSCSpecUDTEnumCaseV0; } + public static SCSpecUDTEnumCaseV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTEnumCaseV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +72,7 @@ public static SCSpecUDTEnumCaseV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecUDTEnumCaseV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumV0.java index 78d5182b1..5ec14ec0b 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTEnumV0.java @@ -35,29 +35,63 @@ public class SCSpecUDTEnumV0 implements XdrElement { private SCSpecUDTEnumCaseV0[] cases; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int libSize = lib.getBytes().length; + if (libSize > 80) { + throw new IOException("lib size " + libSize + " exceeds max size 80"); + } lib.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); int casesSize = getCases().length; + if (casesSize > 50) { + throw new IOException("cases size " + casesSize + " exceeds max size 50"); + } stream.writeInt(casesSize); for (int i = 0; i < casesSize; i++) { cases[i].encode(stream); } } - public static SCSpecUDTEnumV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTEnumV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTEnumV0 decodedSCSpecUDTEnumV0 = new SCSpecUDTEnumV0(); - decodedSCSpecUDTEnumV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTEnumV0.lib = XdrString.decode(stream, 80); - decodedSCSpecUDTEnumV0.name = XdrString.decode(stream, 60); + decodedSCSpecUDTEnumV0.doc = XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTEnumV0.lib = XdrString.decode(stream, maxDepth, 80); + decodedSCSpecUDTEnumV0.name = XdrString.decode(stream, maxDepth, 60); int casesSize = stream.readInt(); + if (casesSize < 0) { + throw new IOException("cases size " + casesSize + " is negative"); + } + if (casesSize > 50) { + throw new IOException("cases size " + casesSize + " exceeds max size 50"); + } + int casesRemainingInputLen = stream.getRemainingInputLen(); + if (casesRemainingInputLen >= 0 && casesRemainingInputLen < casesSize) { + throw new IOException( + "cases size " + casesSize + " exceeds remaining input length " + casesRemainingInputLen); + } decodedSCSpecUDTEnumV0.cases = new SCSpecUDTEnumCaseV0[casesSize]; for (int i = 0; i < casesSize; i++) { - decodedSCSpecUDTEnumV0.cases[i] = SCSpecUDTEnumCaseV0.decode(stream); + decodedSCSpecUDTEnumV0.cases[i] = SCSpecUDTEnumCaseV0.decode(stream, maxDepth); } return decodedSCSpecUDTEnumV0; } + public static SCSpecUDTEnumV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTEnumV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +100,7 @@ public static SCSpecUDTEnumV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecUDTEnumV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumCaseV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumCaseV0.java index cd99a5ae1..2ca914a53 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumCaseV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumCaseV0.java @@ -33,19 +33,37 @@ public class SCSpecUDTErrorEnumCaseV0 implements XdrElement { private Uint32 value; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); value.encode(stream); } - public static SCSpecUDTErrorEnumCaseV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTErrorEnumCaseV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTErrorEnumCaseV0 decodedSCSpecUDTErrorEnumCaseV0 = new SCSpecUDTErrorEnumCaseV0(); - decodedSCSpecUDTErrorEnumCaseV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTErrorEnumCaseV0.name = XdrString.decode(stream, 60); - decodedSCSpecUDTErrorEnumCaseV0.value = Uint32.decode(stream); + decodedSCSpecUDTErrorEnumCaseV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTErrorEnumCaseV0.name = XdrString.decode(stream, maxDepth, 60); + decodedSCSpecUDTErrorEnumCaseV0.value = Uint32.decode(stream, maxDepth); return decodedSCSpecUDTErrorEnumCaseV0; } + public static SCSpecUDTErrorEnumCaseV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTErrorEnumCaseV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +72,7 @@ public static SCSpecUDTErrorEnumCaseV0 fromXdrBase64(String xdr) throws IOExcept public static SCSpecUDTErrorEnumCaseV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumV0.java index 61d2cb00f..6a6c6c372 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTErrorEnumV0.java @@ -35,29 +35,65 @@ public class SCSpecUDTErrorEnumV0 implements XdrElement { private SCSpecUDTErrorEnumCaseV0[] cases; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int libSize = lib.getBytes().length; + if (libSize > 80) { + throw new IOException("lib size " + libSize + " exceeds max size 80"); + } lib.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); int casesSize = getCases().length; + if (casesSize > 50) { + throw new IOException("cases size " + casesSize + " exceeds max size 50"); + } stream.writeInt(casesSize); for (int i = 0; i < casesSize; i++) { cases[i].encode(stream); } } - public static SCSpecUDTErrorEnumV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTErrorEnumV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTErrorEnumV0 decodedSCSpecUDTErrorEnumV0 = new SCSpecUDTErrorEnumV0(); - decodedSCSpecUDTErrorEnumV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTErrorEnumV0.lib = XdrString.decode(stream, 80); - decodedSCSpecUDTErrorEnumV0.name = XdrString.decode(stream, 60); + decodedSCSpecUDTErrorEnumV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTErrorEnumV0.lib = XdrString.decode(stream, maxDepth, 80); + decodedSCSpecUDTErrorEnumV0.name = XdrString.decode(stream, maxDepth, 60); int casesSize = stream.readInt(); + if (casesSize < 0) { + throw new IOException("cases size " + casesSize + " is negative"); + } + if (casesSize > 50) { + throw new IOException("cases size " + casesSize + " exceeds max size 50"); + } + int casesRemainingInputLen = stream.getRemainingInputLen(); + if (casesRemainingInputLen >= 0 && casesRemainingInputLen < casesSize) { + throw new IOException( + "cases size " + casesSize + " exceeds remaining input length " + casesRemainingInputLen); + } decodedSCSpecUDTErrorEnumV0.cases = new SCSpecUDTErrorEnumCaseV0[casesSize]; for (int i = 0; i < casesSize; i++) { - decodedSCSpecUDTErrorEnumV0.cases[i] = SCSpecUDTErrorEnumCaseV0.decode(stream); + decodedSCSpecUDTErrorEnumV0.cases[i] = SCSpecUDTErrorEnumCaseV0.decode(stream, maxDepth); } return decodedSCSpecUDTErrorEnumV0; } + public static SCSpecUDTErrorEnumV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTErrorEnumV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +102,7 @@ public static SCSpecUDTErrorEnumV0 fromXdrBase64(String xdr) throws IOException public static SCSpecUDTErrorEnumV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructFieldV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructFieldV0.java index 9c07b4944..11f80539d 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructFieldV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructFieldV0.java @@ -33,19 +33,37 @@ public class SCSpecUDTStructFieldV0 implements XdrElement { private SCSpecTypeDef type; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 30) { + throw new IOException("name size " + nameSize + " exceeds max size 30"); + } name.encode(stream); type.encode(stream); } - public static SCSpecUDTStructFieldV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTStructFieldV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTStructFieldV0 decodedSCSpecUDTStructFieldV0 = new SCSpecUDTStructFieldV0(); - decodedSCSpecUDTStructFieldV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTStructFieldV0.name = XdrString.decode(stream, 30); - decodedSCSpecUDTStructFieldV0.type = SCSpecTypeDef.decode(stream); + decodedSCSpecUDTStructFieldV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTStructFieldV0.name = XdrString.decode(stream, maxDepth, 30); + decodedSCSpecUDTStructFieldV0.type = SCSpecTypeDef.decode(stream, maxDepth); return decodedSCSpecUDTStructFieldV0; } + public static SCSpecUDTStructFieldV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTStructFieldV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +72,7 @@ public static SCSpecUDTStructFieldV0 fromXdrBase64(String xdr) throws IOExceptio public static SCSpecUDTStructFieldV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructV0.java index 1897b89f1..c744247bf 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTStructV0.java @@ -35,29 +35,67 @@ public class SCSpecUDTStructV0 implements XdrElement { private SCSpecUDTStructFieldV0[] fields; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int libSize = lib.getBytes().length; + if (libSize > 80) { + throw new IOException("lib size " + libSize + " exceeds max size 80"); + } lib.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); int fieldsSize = getFields().length; + if (fieldsSize > 40) { + throw new IOException("fields size " + fieldsSize + " exceeds max size 40"); + } stream.writeInt(fieldsSize); for (int i = 0; i < fieldsSize; i++) { fields[i].encode(stream); } } - public static SCSpecUDTStructV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTStructV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTStructV0 decodedSCSpecUDTStructV0 = new SCSpecUDTStructV0(); - decodedSCSpecUDTStructV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTStructV0.lib = XdrString.decode(stream, 80); - decodedSCSpecUDTStructV0.name = XdrString.decode(stream, 60); + decodedSCSpecUDTStructV0.doc = XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTStructV0.lib = XdrString.decode(stream, maxDepth, 80); + decodedSCSpecUDTStructV0.name = XdrString.decode(stream, maxDepth, 60); int fieldsSize = stream.readInt(); + if (fieldsSize < 0) { + throw new IOException("fields size " + fieldsSize + " is negative"); + } + if (fieldsSize > 40) { + throw new IOException("fields size " + fieldsSize + " exceeds max size 40"); + } + int fieldsRemainingInputLen = stream.getRemainingInputLen(); + if (fieldsRemainingInputLen >= 0 && fieldsRemainingInputLen < fieldsSize) { + throw new IOException( + "fields size " + + fieldsSize + + " exceeds remaining input length " + + fieldsRemainingInputLen); + } decodedSCSpecUDTStructV0.fields = new SCSpecUDTStructFieldV0[fieldsSize]; for (int i = 0; i < fieldsSize; i++) { - decodedSCSpecUDTStructV0.fields[i] = SCSpecUDTStructFieldV0.decode(stream); + decodedSCSpecUDTStructV0.fields[i] = SCSpecUDTStructFieldV0.decode(stream, maxDepth); } return decodedSCSpecUDTStructV0; } + public static SCSpecUDTStructV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTStructV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +104,7 @@ public static SCSpecUDTStructV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecUDTStructV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseTupleV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseTupleV0.java index a2faf8f78..ba6d3139c 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseTupleV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseTupleV0.java @@ -33,27 +33,59 @@ public class SCSpecUDTUnionCaseTupleV0 implements XdrElement { private SCSpecTypeDef[] type; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); int typeSize = getType().length; + if (typeSize > 12) { + throw new IOException("type size " + typeSize + " exceeds max size 12"); + } stream.writeInt(typeSize); for (int i = 0; i < typeSize; i++) { type[i].encode(stream); } } - public static SCSpecUDTUnionCaseTupleV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTUnionCaseTupleV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTUnionCaseTupleV0 decodedSCSpecUDTUnionCaseTupleV0 = new SCSpecUDTUnionCaseTupleV0(); - decodedSCSpecUDTUnionCaseTupleV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTUnionCaseTupleV0.name = XdrString.decode(stream, 60); + decodedSCSpecUDTUnionCaseTupleV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTUnionCaseTupleV0.name = XdrString.decode(stream, maxDepth, 60); int typeSize = stream.readInt(); + if (typeSize < 0) { + throw new IOException("type size " + typeSize + " is negative"); + } + if (typeSize > 12) { + throw new IOException("type size " + typeSize + " exceeds max size 12"); + } + int typeRemainingInputLen = stream.getRemainingInputLen(); + if (typeRemainingInputLen >= 0 && typeRemainingInputLen < typeSize) { + throw new IOException( + "type size " + typeSize + " exceeds remaining input length " + typeRemainingInputLen); + } decodedSCSpecUDTUnionCaseTupleV0.type = new SCSpecTypeDef[typeSize]; for (int i = 0; i < typeSize; i++) { - decodedSCSpecUDTUnionCaseTupleV0.type[i] = SCSpecTypeDef.decode(stream); + decodedSCSpecUDTUnionCaseTupleV0.type[i] = SCSpecTypeDef.decode(stream, maxDepth); } return decodedSCSpecUDTUnionCaseTupleV0; } + public static SCSpecUDTUnionCaseTupleV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTUnionCaseTupleV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +94,7 @@ public static SCSpecUDTUnionCaseTupleV0 fromXdrBase64(String xdr) throws IOExcep public static SCSpecUDTUnionCaseTupleV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0.java index 827905cdd..6e27431eb 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0.java @@ -45,21 +45,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCSpecUDTUnionCaseV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTUnionCaseV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTUnionCaseV0 decodedSCSpecUDTUnionCaseV0 = new SCSpecUDTUnionCaseV0(); - SCSpecUDTUnionCaseV0Kind discriminant = SCSpecUDTUnionCaseV0Kind.decode(stream); + SCSpecUDTUnionCaseV0Kind discriminant = SCSpecUDTUnionCaseV0Kind.decode(stream, maxDepth); decodedSCSpecUDTUnionCaseV0.setDiscriminant(discriminant); switch (decodedSCSpecUDTUnionCaseV0.getDiscriminant()) { case SC_SPEC_UDT_UNION_CASE_VOID_V0: - decodedSCSpecUDTUnionCaseV0.voidCase = SCSpecUDTUnionCaseVoidV0.decode(stream); + decodedSCSpecUDTUnionCaseV0.voidCase = SCSpecUDTUnionCaseVoidV0.decode(stream, maxDepth); break; case SC_SPEC_UDT_UNION_CASE_TUPLE_V0: - decodedSCSpecUDTUnionCaseV0.tupleCase = SCSpecUDTUnionCaseTupleV0.decode(stream); + decodedSCSpecUDTUnionCaseV0.tupleCase = SCSpecUDTUnionCaseTupleV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCSpecUDTUnionCaseV0; } + public static SCSpecUDTUnionCaseV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTUnionCaseV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +79,7 @@ public static SCSpecUDTUnionCaseV0 fromXdrBase64(String xdr) throws IOException public static SCSpecUDTUnionCaseV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0Kind.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0Kind.java index 690fb9f67..e46ad4849 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0Kind.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseV0Kind.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static SCSpecUDTUnionCaseV0Kind decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTUnionCaseV0Kind decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static SCSpecUDTUnionCaseV0Kind decode(XdrDataInputStream stream) throws } } + public static SCSpecUDTUnionCaseV0Kind decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static SCSpecUDTUnionCaseV0Kind fromXdrBase64(String xdr) throws IOExcept public static SCSpecUDTUnionCaseV0Kind fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseVoidV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseVoidV0.java index 7153e8f4d..694efbefa 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseVoidV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionCaseVoidV0.java @@ -31,17 +31,35 @@ public class SCSpecUDTUnionCaseVoidV0 implements XdrElement { private XdrString name; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); } - public static SCSpecUDTUnionCaseVoidV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTUnionCaseVoidV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTUnionCaseVoidV0 decodedSCSpecUDTUnionCaseVoidV0 = new SCSpecUDTUnionCaseVoidV0(); - decodedSCSpecUDTUnionCaseVoidV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTUnionCaseVoidV0.name = XdrString.decode(stream, 60); + decodedSCSpecUDTUnionCaseVoidV0.doc = + XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTUnionCaseVoidV0.name = XdrString.decode(stream, maxDepth, 60); return decodedSCSpecUDTUnionCaseVoidV0; } + public static SCSpecUDTUnionCaseVoidV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTUnionCaseVoidV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +68,7 @@ public static SCSpecUDTUnionCaseVoidV0 fromXdrBase64(String xdr) throws IOExcept public static SCSpecUDTUnionCaseVoidV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionV0.java b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionV0.java index f63c5a274..77c9cb81d 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSpecUDTUnionV0.java @@ -35,29 +35,64 @@ public class SCSpecUDTUnionV0 implements XdrElement { private SCSpecUDTUnionCaseV0[] cases; public void encode(XdrDataOutputStream stream) throws IOException { + int docSize = doc.getBytes().length; + if (docSize > 1024) { + throw new IOException("doc size " + docSize + " exceeds max size 1024"); + } doc.encode(stream); + int libSize = lib.getBytes().length; + if (libSize > 80) { + throw new IOException("lib size " + libSize + " exceeds max size 80"); + } lib.encode(stream); + int nameSize = name.getBytes().length; + if (nameSize > 60) { + throw new IOException("name size " + nameSize + " exceeds max size 60"); + } name.encode(stream); int casesSize = getCases().length; + if (casesSize > 50) { + throw new IOException("cases size " + casesSize + " exceeds max size 50"); + } stream.writeInt(casesSize); for (int i = 0; i < casesSize; i++) { cases[i].encode(stream); } } - public static SCSpecUDTUnionV0 decode(XdrDataInputStream stream) throws IOException { + public static SCSpecUDTUnionV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSpecUDTUnionV0 decodedSCSpecUDTUnionV0 = new SCSpecUDTUnionV0(); - decodedSCSpecUDTUnionV0.doc = XdrString.decode(stream, Constants.SC_SPEC_DOC_LIMIT); - decodedSCSpecUDTUnionV0.lib = XdrString.decode(stream, 80); - decodedSCSpecUDTUnionV0.name = XdrString.decode(stream, 60); + decodedSCSpecUDTUnionV0.doc = XdrString.decode(stream, maxDepth, Constants.SC_SPEC_DOC_LIMIT); + decodedSCSpecUDTUnionV0.lib = XdrString.decode(stream, maxDepth, 80); + decodedSCSpecUDTUnionV0.name = XdrString.decode(stream, maxDepth, 60); int casesSize = stream.readInt(); + if (casesSize < 0) { + throw new IOException("cases size " + casesSize + " is negative"); + } + if (casesSize > 50) { + throw new IOException("cases size " + casesSize + " exceeds max size 50"); + } + int casesRemainingInputLen = stream.getRemainingInputLen(); + if (casesRemainingInputLen >= 0 && casesRemainingInputLen < casesSize) { + throw new IOException( + "cases size " + casesSize + " exceeds remaining input length " + casesRemainingInputLen); + } decodedSCSpecUDTUnionV0.cases = new SCSpecUDTUnionCaseV0[casesSize]; for (int i = 0; i < casesSize; i++) { - decodedSCSpecUDTUnionV0.cases[i] = SCSpecUDTUnionCaseV0.decode(stream); + decodedSCSpecUDTUnionV0.cases[i] = SCSpecUDTUnionCaseV0.decode(stream, maxDepth); } return decodedSCSpecUDTUnionV0; } + public static SCSpecUDTUnionV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSpecUDTUnionV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -66,6 +101,7 @@ public static SCSpecUDTUnionV0 fromXdrBase64(String xdr) throws IOException { public static SCSpecUDTUnionV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCString.java b/src/main/java/org/stellar/sdk/xdr/SCString.java index d82d01d2d..afb3ba056 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCString.java +++ b/src/main/java/org/stellar/sdk/xdr/SCString.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { SCString.encode(stream); } - public static SCString decode(XdrDataInputStream stream) throws IOException { + public static SCString decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCString decodedSCString = new SCString(); - decodedSCString.SCString = XdrString.decode(stream, Integer.MAX_VALUE); + decodedSCString.SCString = XdrString.decode(stream, maxDepth, Integer.MAX_VALUE); return decodedSCString; } + public static SCString decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCString fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static SCString fromXdrBase64(String xdr) throws IOException { public static SCString fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCSymbol.java b/src/main/java/org/stellar/sdk/xdr/SCSymbol.java index d125c5baa..2ea2ea368 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCSymbol.java +++ b/src/main/java/org/stellar/sdk/xdr/SCSymbol.java @@ -24,15 +24,27 @@ public class SCSymbol implements XdrElement { private XdrString SCSymbol; public void encode(XdrDataOutputStream stream) throws IOException { + int SCSymbolSize = SCSymbol.getBytes().length; + if (SCSymbolSize > 32) { + throw new IOException("SCSymbol size " + SCSymbolSize + " exceeds max size 32"); + } SCSymbol.encode(stream); } - public static SCSymbol decode(XdrDataInputStream stream) throws IOException { + public static SCSymbol decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCSymbol decodedSCSymbol = new SCSymbol(); - decodedSCSymbol.SCSymbol = XdrString.decode(stream, Constants.SCSYMBOL_LIMIT); + decodedSCSymbol.SCSymbol = XdrString.decode(stream, maxDepth, Constants.SCSYMBOL_LIMIT); return decodedSCSymbol; } + public static SCSymbol decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCSymbol fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +53,7 @@ public static SCSymbol fromXdrBase64(String xdr) throws IOException { public static SCSymbol fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCVal.java b/src/main/java/org/stellar/sdk/xdr/SCVal.java index 1e741316a..3cb840f6a 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCVal.java +++ b/src/main/java/org/stellar/sdk/xdr/SCVal.java @@ -184,85 +184,95 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCVal decode(XdrDataInputStream stream) throws IOException { + public static SCVal decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCVal decodedSCVal = new SCVal(); - SCValType discriminant = SCValType.decode(stream); + SCValType discriminant = SCValType.decode(stream, maxDepth); decodedSCVal.setDiscriminant(discriminant); switch (decodedSCVal.getDiscriminant()) { case SCV_BOOL: - decodedSCVal.b = stream.readInt() == 1 ? true : false; + decodedSCVal.b = stream.readXdrBoolean(); break; case SCV_VOID: break; case SCV_ERROR: - decodedSCVal.error = SCError.decode(stream); + decodedSCVal.error = SCError.decode(stream, maxDepth); break; case SCV_U32: - decodedSCVal.u32 = Uint32.decode(stream); + decodedSCVal.u32 = Uint32.decode(stream, maxDepth); break; case SCV_I32: - decodedSCVal.i32 = Int32.decode(stream); + decodedSCVal.i32 = Int32.decode(stream, maxDepth); break; case SCV_U64: - decodedSCVal.u64 = Uint64.decode(stream); + decodedSCVal.u64 = Uint64.decode(stream, maxDepth); break; case SCV_I64: - decodedSCVal.i64 = Int64.decode(stream); + decodedSCVal.i64 = Int64.decode(stream, maxDepth); break; case SCV_TIMEPOINT: - decodedSCVal.timepoint = TimePoint.decode(stream); + decodedSCVal.timepoint = TimePoint.decode(stream, maxDepth); break; case SCV_DURATION: - decodedSCVal.duration = Duration.decode(stream); + decodedSCVal.duration = Duration.decode(stream, maxDepth); break; case SCV_U128: - decodedSCVal.u128 = UInt128Parts.decode(stream); + decodedSCVal.u128 = UInt128Parts.decode(stream, maxDepth); break; case SCV_I128: - decodedSCVal.i128 = Int128Parts.decode(stream); + decodedSCVal.i128 = Int128Parts.decode(stream, maxDepth); break; case SCV_U256: - decodedSCVal.u256 = UInt256Parts.decode(stream); + decodedSCVal.u256 = UInt256Parts.decode(stream, maxDepth); break; case SCV_I256: - decodedSCVal.i256 = Int256Parts.decode(stream); + decodedSCVal.i256 = Int256Parts.decode(stream, maxDepth); break; case SCV_BYTES: - decodedSCVal.bytes = SCBytes.decode(stream); + decodedSCVal.bytes = SCBytes.decode(stream, maxDepth); break; case SCV_STRING: - decodedSCVal.str = SCString.decode(stream); + decodedSCVal.str = SCString.decode(stream, maxDepth); break; case SCV_SYMBOL: - decodedSCVal.sym = SCSymbol.decode(stream); + decodedSCVal.sym = SCSymbol.decode(stream, maxDepth); break; case SCV_VEC: - int vecPresent = stream.readInt(); - if (vecPresent != 0) { - decodedSCVal.vec = SCVec.decode(stream); + boolean vecPresent = stream.readXdrBoolean(); + if (vecPresent) { + decodedSCVal.vec = SCVec.decode(stream, maxDepth); } break; case SCV_MAP: - int mapPresent = stream.readInt(); - if (mapPresent != 0) { - decodedSCVal.map = SCMap.decode(stream); + boolean mapPresent = stream.readXdrBoolean(); + if (mapPresent) { + decodedSCVal.map = SCMap.decode(stream, maxDepth); } break; case SCV_ADDRESS: - decodedSCVal.address = SCAddress.decode(stream); + decodedSCVal.address = SCAddress.decode(stream, maxDepth); break; case SCV_CONTRACT_INSTANCE: - decodedSCVal.instance = SCContractInstance.decode(stream); + decodedSCVal.instance = SCContractInstance.decode(stream, maxDepth); break; case SCV_LEDGER_KEY_CONTRACT_INSTANCE: break; case SCV_LEDGER_KEY_NONCE: - decodedSCVal.nonce_key = SCNonceKey.decode(stream); + decodedSCVal.nonce_key = SCNonceKey.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSCVal; } + public static SCVal decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCVal fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -271,6 +281,7 @@ public static SCVal fromXdrBase64(String xdr) throws IOException { public static SCVal fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCValType.java b/src/main/java/org/stellar/sdk/xdr/SCValType.java index 43cd55886..5a110908b 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCValType.java +++ b/src/main/java/org/stellar/sdk/xdr/SCValType.java @@ -101,7 +101,8 @@ public int getValue() { return value; } - public static SCValType decode(XdrDataInputStream stream) throws IOException { + public static SCValType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -153,6 +154,10 @@ public static SCValType decode(XdrDataInputStream stream) throws IOException { } } + public static SCValType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -165,6 +170,7 @@ public static SCValType fromXdrBase64(String xdr) throws IOException { public static SCValType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SCVec.java b/src/main/java/org/stellar/sdk/xdr/SCVec.java index 0fedc3b19..4839da2b6 100644 --- a/src/main/java/org/stellar/sdk/xdr/SCVec.java +++ b/src/main/java/org/stellar/sdk/xdr/SCVec.java @@ -31,16 +31,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SCVec decode(XdrDataInputStream stream) throws IOException { + public static SCVec decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SCVec decodedSCVec = new SCVec(); int SCVecSize = stream.readInt(); + if (SCVecSize < 0) { + throw new IOException("SCVec size " + SCVecSize + " is negative"); + } + int SCVecRemainingInputLen = stream.getRemainingInputLen(); + if (SCVecRemainingInputLen >= 0 && SCVecRemainingInputLen < SCVecSize) { + throw new IOException( + "SCVec size " + SCVecSize + " exceeds remaining input length " + SCVecRemainingInputLen); + } decodedSCVec.SCVec = new SCVal[SCVecSize]; for (int i = 0; i < SCVecSize; i++) { - decodedSCVec.SCVec[i] = SCVal.decode(stream); + decodedSCVec.SCVec[i] = SCVal.decode(stream, maxDepth); } return decodedSCVec; } + public static SCVec decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SCVec fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +65,7 @@ public static SCVec fromXdrBase64(String xdr) throws IOException { public static SCVec fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SendMore.java b/src/main/java/org/stellar/sdk/xdr/SendMore.java index 2b97d57fc..34cb4dc44 100644 --- a/src/main/java/org/stellar/sdk/xdr/SendMore.java +++ b/src/main/java/org/stellar/sdk/xdr/SendMore.java @@ -32,12 +32,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { numMessages.encode(stream); } - public static SendMore decode(XdrDataInputStream stream) throws IOException { + public static SendMore decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SendMore decodedSendMore = new SendMore(); - decodedSendMore.numMessages = Uint32.decode(stream); + decodedSendMore.numMessages = Uint32.decode(stream, maxDepth); return decodedSendMore; } + public static SendMore decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SendMore fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -46,6 +54,7 @@ public static SendMore fromXdrBase64(String xdr) throws IOException { public static SendMore fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SendMoreExtended.java b/src/main/java/org/stellar/sdk/xdr/SendMoreExtended.java index 537d9ac9d..e2a5ced5b 100644 --- a/src/main/java/org/stellar/sdk/xdr/SendMoreExtended.java +++ b/src/main/java/org/stellar/sdk/xdr/SendMoreExtended.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { numBytes.encode(stream); } - public static SendMoreExtended decode(XdrDataInputStream stream) throws IOException { + public static SendMoreExtended decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SendMoreExtended decodedSendMoreExtended = new SendMoreExtended(); - decodedSendMoreExtended.numMessages = Uint32.decode(stream); - decodedSendMoreExtended.numBytes = Uint32.decode(stream); + decodedSendMoreExtended.numMessages = Uint32.decode(stream, maxDepth); + decodedSendMoreExtended.numBytes = Uint32.decode(stream, maxDepth); return decodedSendMoreExtended; } + public static SendMoreExtended decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SendMoreExtended fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static SendMoreExtended fromXdrBase64(String xdr) throws IOException { public static SendMoreExtended fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SequenceNumber.java b/src/main/java/org/stellar/sdk/xdr/SequenceNumber.java index 93d8103ae..d3be8f596 100644 --- a/src/main/java/org/stellar/sdk/xdr/SequenceNumber.java +++ b/src/main/java/org/stellar/sdk/xdr/SequenceNumber.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { SequenceNumber.encode(stream); } - public static SequenceNumber decode(XdrDataInputStream stream) throws IOException { + public static SequenceNumber decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SequenceNumber decodedSequenceNumber = new SequenceNumber(); - decodedSequenceNumber.SequenceNumber = Int64.decode(stream); + decodedSequenceNumber.SequenceNumber = Int64.decode(stream, maxDepth); return decodedSequenceNumber; } + public static SequenceNumber decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SequenceNumber fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static SequenceNumber fromXdrBase64(String xdr) throws IOException { public static SequenceNumber fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SerializedBinaryFuseFilter.java b/src/main/java/org/stellar/sdk/xdr/SerializedBinaryFuseFilter.java index c8837970f..6cfd4ddcb 100644 --- a/src/main/java/org/stellar/sdk/xdr/SerializedBinaryFuseFilter.java +++ b/src/main/java/org/stellar/sdk/xdr/SerializedBinaryFuseFilter.java @@ -64,22 +64,42 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(getFingerprints(), 0, fingerprintsSize); } - public static SerializedBinaryFuseFilter decode(XdrDataInputStream stream) throws IOException { + public static SerializedBinaryFuseFilter decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SerializedBinaryFuseFilter decodedSerializedBinaryFuseFilter = new SerializedBinaryFuseFilter(); - decodedSerializedBinaryFuseFilter.type = BinaryFuseFilterType.decode(stream); - decodedSerializedBinaryFuseFilter.inputHashSeed = ShortHashSeed.decode(stream); - decodedSerializedBinaryFuseFilter.filterSeed = ShortHashSeed.decode(stream); - decodedSerializedBinaryFuseFilter.segmentLength = Uint32.decode(stream); - decodedSerializedBinaryFuseFilter.segementLengthMask = Uint32.decode(stream); - decodedSerializedBinaryFuseFilter.segmentCount = Uint32.decode(stream); - decodedSerializedBinaryFuseFilter.segmentCountLength = Uint32.decode(stream); - decodedSerializedBinaryFuseFilter.fingerprintLength = Uint32.decode(stream); + decodedSerializedBinaryFuseFilter.type = BinaryFuseFilterType.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.inputHashSeed = ShortHashSeed.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.filterSeed = ShortHashSeed.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.segmentLength = Uint32.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.segementLengthMask = Uint32.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.segmentCount = Uint32.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.segmentCountLength = Uint32.decode(stream, maxDepth); + decodedSerializedBinaryFuseFilter.fingerprintLength = Uint32.decode(stream, maxDepth); int fingerprintsSize = stream.readInt(); + if (fingerprintsSize < 0) { + throw new IOException("fingerprints size " + fingerprintsSize + " is negative"); + } + int fingerprintsRemainingInputLen = stream.getRemainingInputLen(); + if (fingerprintsRemainingInputLen >= 0 && fingerprintsRemainingInputLen < fingerprintsSize) { + throw new IOException( + "fingerprints size " + + fingerprintsSize + + " exceeds remaining input length " + + fingerprintsRemainingInputLen); + } decodedSerializedBinaryFuseFilter.fingerprints = new byte[fingerprintsSize]; - stream.read(decodedSerializedBinaryFuseFilter.fingerprints, 0, fingerprintsSize); + stream.readPaddedData(decodedSerializedBinaryFuseFilter.fingerprints, 0, fingerprintsSize); return decodedSerializedBinaryFuseFilter; } + public static SerializedBinaryFuseFilter decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SerializedBinaryFuseFilter fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -88,6 +108,7 @@ public static SerializedBinaryFuseFilter fromXdrBase64(String xdr) throws IOExce public static SerializedBinaryFuseFilter fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SetOptionsOp.java b/src/main/java/org/stellar/sdk/xdr/SetOptionsOp.java index 9b15c55a6..89a52f8e8 100644 --- a/src/main/java/org/stellar/sdk/xdr/SetOptionsOp.java +++ b/src/main/java/org/stellar/sdk/xdr/SetOptionsOp.java @@ -108,47 +108,55 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SetOptionsOp decode(XdrDataInputStream stream) throws IOException { + public static SetOptionsOp decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SetOptionsOp decodedSetOptionsOp = new SetOptionsOp(); - int inflationDestPresent = stream.readInt(); - if (inflationDestPresent != 0) { - decodedSetOptionsOp.inflationDest = AccountID.decode(stream); + boolean inflationDestPresent = stream.readXdrBoolean(); + if (inflationDestPresent) { + decodedSetOptionsOp.inflationDest = AccountID.decode(stream, maxDepth); } - int clearFlagsPresent = stream.readInt(); - if (clearFlagsPresent != 0) { - decodedSetOptionsOp.clearFlags = Uint32.decode(stream); + boolean clearFlagsPresent = stream.readXdrBoolean(); + if (clearFlagsPresent) { + decodedSetOptionsOp.clearFlags = Uint32.decode(stream, maxDepth); } - int setFlagsPresent = stream.readInt(); - if (setFlagsPresent != 0) { - decodedSetOptionsOp.setFlags = Uint32.decode(stream); + boolean setFlagsPresent = stream.readXdrBoolean(); + if (setFlagsPresent) { + decodedSetOptionsOp.setFlags = Uint32.decode(stream, maxDepth); } - int masterWeightPresent = stream.readInt(); - if (masterWeightPresent != 0) { - decodedSetOptionsOp.masterWeight = Uint32.decode(stream); + boolean masterWeightPresent = stream.readXdrBoolean(); + if (masterWeightPresent) { + decodedSetOptionsOp.masterWeight = Uint32.decode(stream, maxDepth); } - int lowThresholdPresent = stream.readInt(); - if (lowThresholdPresent != 0) { - decodedSetOptionsOp.lowThreshold = Uint32.decode(stream); + boolean lowThresholdPresent = stream.readXdrBoolean(); + if (lowThresholdPresent) { + decodedSetOptionsOp.lowThreshold = Uint32.decode(stream, maxDepth); } - int medThresholdPresent = stream.readInt(); - if (medThresholdPresent != 0) { - decodedSetOptionsOp.medThreshold = Uint32.decode(stream); + boolean medThresholdPresent = stream.readXdrBoolean(); + if (medThresholdPresent) { + decodedSetOptionsOp.medThreshold = Uint32.decode(stream, maxDepth); } - int highThresholdPresent = stream.readInt(); - if (highThresholdPresent != 0) { - decodedSetOptionsOp.highThreshold = Uint32.decode(stream); + boolean highThresholdPresent = stream.readXdrBoolean(); + if (highThresholdPresent) { + decodedSetOptionsOp.highThreshold = Uint32.decode(stream, maxDepth); } - int homeDomainPresent = stream.readInt(); - if (homeDomainPresent != 0) { - decodedSetOptionsOp.homeDomain = String32.decode(stream); + boolean homeDomainPresent = stream.readXdrBoolean(); + if (homeDomainPresent) { + decodedSetOptionsOp.homeDomain = String32.decode(stream, maxDepth); } - int signerPresent = stream.readInt(); - if (signerPresent != 0) { - decodedSetOptionsOp.signer = Signer.decode(stream); + boolean signerPresent = stream.readXdrBoolean(); + if (signerPresent) { + decodedSetOptionsOp.signer = Signer.decode(stream, maxDepth); } return decodedSetOptionsOp; } + public static SetOptionsOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SetOptionsOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -157,6 +165,7 @@ public static SetOptionsOp fromXdrBase64(String xdr) throws IOException { public static SetOptionsOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SetOptionsResult.java b/src/main/java/org/stellar/sdk/xdr/SetOptionsResult.java index 3b619a0dd..d8f1d0b21 100644 --- a/src/main/java/org/stellar/sdk/xdr/SetOptionsResult.java +++ b/src/main/java/org/stellar/sdk/xdr/SetOptionsResult.java @@ -59,9 +59,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SetOptionsResult decode(XdrDataInputStream stream) throws IOException { + public static SetOptionsResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SetOptionsResult decodedSetOptionsResult = new SetOptionsResult(); - SetOptionsResultCode discriminant = SetOptionsResultCode.decode(stream); + SetOptionsResultCode discriminant = SetOptionsResultCode.decode(stream, maxDepth); decodedSetOptionsResult.setDiscriminant(discriminant); switch (decodedSetOptionsResult.getDiscriminant()) { case SET_OPTIONS_SUCCESS: @@ -77,10 +82,16 @@ public static SetOptionsResult decode(XdrDataInputStream stream) throws IOExcept case SET_OPTIONS_INVALID_HOME_DOMAIN: case SET_OPTIONS_AUTH_REVOCABLE_REQUIRED: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSetOptionsResult; } + public static SetOptionsResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SetOptionsResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -89,6 +100,7 @@ public static SetOptionsResult fromXdrBase64(String xdr) throws IOException { public static SetOptionsResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SetOptionsResultCode.java b/src/main/java/org/stellar/sdk/xdr/SetOptionsResultCode.java index f4e34a474..918abc37f 100644 --- a/src/main/java/org/stellar/sdk/xdr/SetOptionsResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/SetOptionsResultCode.java @@ -53,7 +53,9 @@ public int getValue() { return value; } - public static SetOptionsResultCode decode(XdrDataInputStream stream) throws IOException { + public static SetOptionsResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -83,6 +85,10 @@ public static SetOptionsResultCode decode(XdrDataInputStream stream) throws IOEx } } + public static SetOptionsResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -95,6 +101,7 @@ public static SetOptionsResultCode fromXdrBase64(String xdr) throws IOException public static SetOptionsResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsOp.java b/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsOp.java index 56fac19e2..e35b5b58b 100644 --- a/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsOp.java +++ b/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsOp.java @@ -42,15 +42,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { setFlags.encode(stream); } - public static SetTrustLineFlagsOp decode(XdrDataInputStream stream) throws IOException { + public static SetTrustLineFlagsOp decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SetTrustLineFlagsOp decodedSetTrustLineFlagsOp = new SetTrustLineFlagsOp(); - decodedSetTrustLineFlagsOp.trustor = AccountID.decode(stream); - decodedSetTrustLineFlagsOp.asset = Asset.decode(stream); - decodedSetTrustLineFlagsOp.clearFlags = Uint32.decode(stream); - decodedSetTrustLineFlagsOp.setFlags = Uint32.decode(stream); + decodedSetTrustLineFlagsOp.trustor = AccountID.decode(stream, maxDepth); + decodedSetTrustLineFlagsOp.asset = Asset.decode(stream, maxDepth); + decodedSetTrustLineFlagsOp.clearFlags = Uint32.decode(stream, maxDepth); + decodedSetTrustLineFlagsOp.setFlags = Uint32.decode(stream, maxDepth); return decodedSetTrustLineFlagsOp; } + public static SetTrustLineFlagsOp decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SetTrustLineFlagsOp fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +68,7 @@ public static SetTrustLineFlagsOp fromXdrBase64(String xdr) throws IOException { public static SetTrustLineFlagsOp fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResult.java b/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResult.java index b42f5d37b..52e8cb537 100644 --- a/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResult.java +++ b/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResult.java @@ -49,9 +49,14 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SetTrustLineFlagsResult decode(XdrDataInputStream stream) throws IOException { + public static SetTrustLineFlagsResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SetTrustLineFlagsResult decodedSetTrustLineFlagsResult = new SetTrustLineFlagsResult(); - SetTrustLineFlagsResultCode discriminant = SetTrustLineFlagsResultCode.decode(stream); + SetTrustLineFlagsResultCode discriminant = SetTrustLineFlagsResultCode.decode(stream, maxDepth); decodedSetTrustLineFlagsResult.setDiscriminant(discriminant); switch (decodedSetTrustLineFlagsResult.getDiscriminant()) { case SET_TRUST_LINE_FLAGS_SUCCESS: @@ -62,10 +67,16 @@ public static SetTrustLineFlagsResult decode(XdrDataInputStream stream) throws I case SET_TRUST_LINE_FLAGS_INVALID_STATE: case SET_TRUST_LINE_FLAGS_LOW_RESERVE: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSetTrustLineFlagsResult; } + public static SetTrustLineFlagsResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SetTrustLineFlagsResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -74,6 +85,7 @@ public static SetTrustLineFlagsResult fromXdrBase64(String xdr) throws IOExcepti public static SetTrustLineFlagsResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResultCode.java b/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResultCode.java index 71e43c6c3..4a6eb9fa5 100644 --- a/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/SetTrustLineFlagsResultCode.java @@ -44,7 +44,9 @@ public int getValue() { return value; } - public static SetTrustLineFlagsResultCode decode(XdrDataInputStream stream) throws IOException { + public static SetTrustLineFlagsResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -64,6 +66,10 @@ public static SetTrustLineFlagsResultCode decode(XdrDataInputStream stream) thro } } + public static SetTrustLineFlagsResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -76,6 +82,7 @@ public static SetTrustLineFlagsResultCode fromXdrBase64(String xdr) throws IOExc public static SetTrustLineFlagsResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ShortHashSeed.java b/src/main/java/org/stellar/sdk/xdr/ShortHashSeed.java index 27b6661c2..78c15a4a4 100644 --- a/src/main/java/org/stellar/sdk/xdr/ShortHashSeed.java +++ b/src/main/java/org/stellar/sdk/xdr/ShortHashSeed.java @@ -30,17 +30,28 @@ public class ShortHashSeed implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int seedSize = seed.length; + if (seedSize != 16) { + throw new IOException("seed size " + seedSize + " does not match fixed size 16"); + } stream.write(getSeed(), 0, seedSize); } - public static ShortHashSeed decode(XdrDataInputStream stream) throws IOException { + public static ShortHashSeed decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; ShortHashSeed decodedShortHashSeed = new ShortHashSeed(); int seedSize = 16; decodedShortHashSeed.seed = new byte[seedSize]; - stream.read(decodedShortHashSeed.seed, 0, seedSize); + stream.readPaddedData(decodedShortHashSeed.seed, 0, seedSize); return decodedShortHashSeed; } + public static ShortHashSeed decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static ShortHashSeed fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +60,7 @@ public static ShortHashSeed fromXdrBase64(String xdr) throws IOException { public static ShortHashSeed fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Signature.java b/src/main/java/org/stellar/sdk/xdr/Signature.java index e6f6ad00c..1469bdde5 100644 --- a/src/main/java/org/stellar/sdk/xdr/Signature.java +++ b/src/main/java/org/stellar/sdk/xdr/Signature.java @@ -25,18 +25,43 @@ public class Signature implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int SignatureSize = Signature.length; + if (SignatureSize > 64) { + throw new IOException("Signature size " + SignatureSize + " exceeds max size 64"); + } stream.writeInt(SignatureSize); stream.write(getSignature(), 0, SignatureSize); } - public static Signature decode(XdrDataInputStream stream) throws IOException { + public static Signature decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Signature decodedSignature = new Signature(); int SignatureSize = stream.readInt(); + if (SignatureSize < 0) { + throw new IOException("Signature size " + SignatureSize + " is negative"); + } + if (SignatureSize > 64) { + throw new IOException("Signature size " + SignatureSize + " exceeds max size 64"); + } + int SignatureRemainingInputLen = stream.getRemainingInputLen(); + if (SignatureRemainingInputLen >= 0 && SignatureRemainingInputLen < SignatureSize) { + throw new IOException( + "Signature size " + + SignatureSize + + " exceeds remaining input length " + + SignatureRemainingInputLen); + } decodedSignature.Signature = new byte[SignatureSize]; - stream.read(decodedSignature.Signature, 0, SignatureSize); + stream.readPaddedData(decodedSignature.Signature, 0, SignatureSize); return decodedSignature; } + public static Signature decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Signature fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +70,7 @@ public static Signature fromXdrBase64(String xdr) throws IOException { public static Signature fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignatureHint.java b/src/main/java/org/stellar/sdk/xdr/SignatureHint.java index 9f190f6b2..de003712c 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignatureHint.java +++ b/src/main/java/org/stellar/sdk/xdr/SignatureHint.java @@ -25,17 +25,29 @@ public class SignatureHint implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int SignatureHintSize = SignatureHint.length; + if (SignatureHintSize != 4) { + throw new IOException( + "SignatureHint size " + SignatureHintSize + " does not match fixed size 4"); + } stream.write(getSignatureHint(), 0, SignatureHintSize); } - public static SignatureHint decode(XdrDataInputStream stream) throws IOException { + public static SignatureHint decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignatureHint decodedSignatureHint = new SignatureHint(); int SignatureHintSize = 4; decodedSignatureHint.SignatureHint = new byte[SignatureHintSize]; - stream.read(decodedSignatureHint.SignatureHint, 0, SignatureHintSize); + stream.readPaddedData(decodedSignatureHint.SignatureHint, 0, SignatureHintSize); return decodedSignatureHint; } + public static SignatureHint decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignatureHint fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -44,6 +56,7 @@ public static SignatureHint fromXdrBase64(String xdr) throws IOException { public static SignatureHint fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyRequestMessage.java b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyRequestMessage.java index a2f994481..69c9c900e 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyRequestMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyRequestMessage.java @@ -35,16 +35,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { request.encode(stream); } - public static SignedTimeSlicedSurveyRequestMessage decode(XdrDataInputStream stream) + public static SignedTimeSlicedSurveyRequestMessage decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignedTimeSlicedSurveyRequestMessage decodedSignedTimeSlicedSurveyRequestMessage = new SignedTimeSlicedSurveyRequestMessage(); - decodedSignedTimeSlicedSurveyRequestMessage.requestSignature = Signature.decode(stream); + decodedSignedTimeSlicedSurveyRequestMessage.requestSignature = + Signature.decode(stream, maxDepth); decodedSignedTimeSlicedSurveyRequestMessage.request = - TimeSlicedSurveyRequestMessage.decode(stream); + TimeSlicedSurveyRequestMessage.decode(stream, maxDepth); return decodedSignedTimeSlicedSurveyRequestMessage; } + public static SignedTimeSlicedSurveyRequestMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignedTimeSlicedSurveyRequestMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +64,7 @@ public static SignedTimeSlicedSurveyRequestMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyResponseMessage.java b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyResponseMessage.java index 2cd2bde37..5f825ffec 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyResponseMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyResponseMessage.java @@ -35,16 +35,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { response.encode(stream); } - public static SignedTimeSlicedSurveyResponseMessage decode(XdrDataInputStream stream) - throws IOException { + public static SignedTimeSlicedSurveyResponseMessage decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignedTimeSlicedSurveyResponseMessage decodedSignedTimeSlicedSurveyResponseMessage = new SignedTimeSlicedSurveyResponseMessage(); - decodedSignedTimeSlicedSurveyResponseMessage.responseSignature = Signature.decode(stream); + decodedSignedTimeSlicedSurveyResponseMessage.responseSignature = + Signature.decode(stream, maxDepth); decodedSignedTimeSlicedSurveyResponseMessage.response = - TimeSlicedSurveyResponseMessage.decode(stream); + TimeSlicedSurveyResponseMessage.decode(stream, maxDepth); return decodedSignedTimeSlicedSurveyResponseMessage; } + public static SignedTimeSlicedSurveyResponseMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignedTimeSlicedSurveyResponseMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +64,7 @@ public static SignedTimeSlicedSurveyResponseMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStartCollectingMessage.java b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStartCollectingMessage.java index 993c54473..aa57ea02f 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStartCollectingMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStartCollectingMessage.java @@ -35,17 +35,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { startCollecting.encode(stream); } - public static SignedTimeSlicedSurveyStartCollectingMessage decode(XdrDataInputStream stream) - throws IOException { + public static SignedTimeSlicedSurveyStartCollectingMessage decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignedTimeSlicedSurveyStartCollectingMessage decodedSignedTimeSlicedSurveyStartCollectingMessage = new SignedTimeSlicedSurveyStartCollectingMessage(); - decodedSignedTimeSlicedSurveyStartCollectingMessage.signature = Signature.decode(stream); + decodedSignedTimeSlicedSurveyStartCollectingMessage.signature = + Signature.decode(stream, maxDepth); decodedSignedTimeSlicedSurveyStartCollectingMessage.startCollecting = - TimeSlicedSurveyStartCollectingMessage.decode(stream); + TimeSlicedSurveyStartCollectingMessage.decode(stream, maxDepth); return decodedSignedTimeSlicedSurveyStartCollectingMessage; } + public static SignedTimeSlicedSurveyStartCollectingMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignedTimeSlicedSurveyStartCollectingMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -56,6 +66,7 @@ public static SignedTimeSlicedSurveyStartCollectingMessage fromXdrByteArray(byte throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStopCollectingMessage.java b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStopCollectingMessage.java index cb4ae36f2..59546ab18 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStopCollectingMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/SignedTimeSlicedSurveyStopCollectingMessage.java @@ -35,16 +35,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { stopCollecting.encode(stream); } - public static SignedTimeSlicedSurveyStopCollectingMessage decode(XdrDataInputStream stream) - throws IOException { + public static SignedTimeSlicedSurveyStopCollectingMessage decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignedTimeSlicedSurveyStopCollectingMessage decodedSignedTimeSlicedSurveyStopCollectingMessage = new SignedTimeSlicedSurveyStopCollectingMessage(); - decodedSignedTimeSlicedSurveyStopCollectingMessage.signature = Signature.decode(stream); + decodedSignedTimeSlicedSurveyStopCollectingMessage.signature = + Signature.decode(stream, maxDepth); decodedSignedTimeSlicedSurveyStopCollectingMessage.stopCollecting = - TimeSlicedSurveyStopCollectingMessage.decode(stream); + TimeSlicedSurveyStopCollectingMessage.decode(stream, maxDepth); return decodedSignedTimeSlicedSurveyStopCollectingMessage; } + public static SignedTimeSlicedSurveyStopCollectingMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignedTimeSlicedSurveyStopCollectingMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -55,6 +65,7 @@ public static SignedTimeSlicedSurveyStopCollectingMessage fromXdrByteArray(byte[ throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Signer.java b/src/main/java/org/stellar/sdk/xdr/Signer.java index f746e2a79..f7148aa50 100644 --- a/src/main/java/org/stellar/sdk/xdr/Signer.java +++ b/src/main/java/org/stellar/sdk/xdr/Signer.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { weight.encode(stream); } - public static Signer decode(XdrDataInputStream stream) throws IOException { + public static Signer decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Signer decodedSigner = new Signer(); - decodedSigner.key = SignerKey.decode(stream); - decodedSigner.weight = Uint32.decode(stream); + decodedSigner.key = SignerKey.decode(stream, maxDepth); + decodedSigner.weight = Uint32.decode(stream, maxDepth); return decodedSigner; } + public static Signer decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Signer fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static Signer fromXdrBase64(String xdr) throws IOException { public static Signer fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignerKey.java b/src/main/java/org/stellar/sdk/xdr/SignerKey.java index d9e5d2891..3e5b454d8 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignerKey.java +++ b/src/main/java/org/stellar/sdk/xdr/SignerKey.java @@ -65,27 +65,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SignerKey decode(XdrDataInputStream stream) throws IOException { + public static SignerKey decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignerKey decodedSignerKey = new SignerKey(); - SignerKeyType discriminant = SignerKeyType.decode(stream); + SignerKeyType discriminant = SignerKeyType.decode(stream, maxDepth); decodedSignerKey.setDiscriminant(discriminant); switch (decodedSignerKey.getDiscriminant()) { case SIGNER_KEY_TYPE_ED25519: - decodedSignerKey.ed25519 = Uint256.decode(stream); + decodedSignerKey.ed25519 = Uint256.decode(stream, maxDepth); break; case SIGNER_KEY_TYPE_PRE_AUTH_TX: - decodedSignerKey.preAuthTx = Uint256.decode(stream); + decodedSignerKey.preAuthTx = Uint256.decode(stream, maxDepth); break; case SIGNER_KEY_TYPE_HASH_X: - decodedSignerKey.hashX = Uint256.decode(stream); + decodedSignerKey.hashX = Uint256.decode(stream, maxDepth); break; case SIGNER_KEY_TYPE_ED25519_SIGNED_PAYLOAD: - decodedSignerKey.ed25519SignedPayload = SignerKeyEd25519SignedPayload.decode(stream); + decodedSignerKey.ed25519SignedPayload = + SignerKeyEd25519SignedPayload.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSignerKey; } + public static SignerKey decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignerKey fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -94,6 +105,7 @@ public static SignerKey fromXdrBase64(String xdr) throws IOException { public static SignerKey fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -121,21 +133,47 @@ public static class SignerKeyEd25519SignedPayload implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { ed25519.encode(stream); int payloadSize = payload.length; + if (payloadSize > 64) { + throw new IOException("payload size " + payloadSize + " exceeds max size 64"); + } stream.writeInt(payloadSize); stream.write(getPayload(), 0, payloadSize); } - public static SignerKeyEd25519SignedPayload decode(XdrDataInputStream stream) + public static SignerKeyEd25519SignedPayload decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SignerKeyEd25519SignedPayload decodedSignerKeyEd25519SignedPayload = new SignerKeyEd25519SignedPayload(); - decodedSignerKeyEd25519SignedPayload.ed25519 = Uint256.decode(stream); + decodedSignerKeyEd25519SignedPayload.ed25519 = Uint256.decode(stream, maxDepth); int payloadSize = stream.readInt(); + if (payloadSize < 0) { + throw new IOException("payload size " + payloadSize + " is negative"); + } + if (payloadSize > 64) { + throw new IOException("payload size " + payloadSize + " exceeds max size 64"); + } + int payloadRemainingInputLen = stream.getRemainingInputLen(); + if (payloadRemainingInputLen >= 0 && payloadRemainingInputLen < payloadSize) { + throw new IOException( + "payload size " + + payloadSize + + " exceeds remaining input length " + + payloadRemainingInputLen); + } decodedSignerKeyEd25519SignedPayload.payload = new byte[payloadSize]; - stream.read(decodedSignerKeyEd25519SignedPayload.payload, 0, payloadSize); + stream.readPaddedData(decodedSignerKeyEd25519SignedPayload.payload, 0, payloadSize); return decodedSignerKeyEd25519SignedPayload; } + public static SignerKeyEd25519SignedPayload decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SignerKeyEd25519SignedPayload fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -144,6 +182,7 @@ public static SignerKeyEd25519SignedPayload fromXdrBase64(String xdr) throws IOE public static SignerKeyEd25519SignedPayload fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SignerKeyType.java b/src/main/java/org/stellar/sdk/xdr/SignerKeyType.java index 6243cb60d..ab0cc8c52 100644 --- a/src/main/java/org/stellar/sdk/xdr/SignerKeyType.java +++ b/src/main/java/org/stellar/sdk/xdr/SignerKeyType.java @@ -36,7 +36,8 @@ public int getValue() { return value; } - public static SignerKeyType decode(XdrDataInputStream stream) throws IOException { + public static SignerKeyType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -52,6 +53,10 @@ public static SignerKeyType decode(XdrDataInputStream stream) throws IOException } } + public static SignerKeyType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -64,6 +69,7 @@ public static SignerKeyType fromXdrBase64(String xdr) throws IOException { public static SignerKeyType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SimplePaymentResult.java b/src/main/java/org/stellar/sdk/xdr/SimplePaymentResult.java index 2c77cb1b6..7dbcbf6ba 100644 --- a/src/main/java/org/stellar/sdk/xdr/SimplePaymentResult.java +++ b/src/main/java/org/stellar/sdk/xdr/SimplePaymentResult.java @@ -38,14 +38,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { amount.encode(stream); } - public static SimplePaymentResult decode(XdrDataInputStream stream) throws IOException { + public static SimplePaymentResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SimplePaymentResult decodedSimplePaymentResult = new SimplePaymentResult(); - decodedSimplePaymentResult.destination = AccountID.decode(stream); - decodedSimplePaymentResult.asset = Asset.decode(stream); - decodedSimplePaymentResult.amount = Int64.decode(stream); + decodedSimplePaymentResult.destination = AccountID.decode(stream, maxDepth); + decodedSimplePaymentResult.asset = Asset.decode(stream, maxDepth); + decodedSimplePaymentResult.amount = Int64.decode(stream, maxDepth); return decodedSimplePaymentResult; } + public static SimplePaymentResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SimplePaymentResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +63,7 @@ public static SimplePaymentResult fromXdrBase64(String xdr) throws IOException { public static SimplePaymentResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanAddressCredentials.java b/src/main/java/org/stellar/sdk/xdr/SorobanAddressCredentials.java index f11c1d227..3e865e63b 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanAddressCredentials.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanAddressCredentials.java @@ -41,15 +41,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { signature.encode(stream); } - public static SorobanAddressCredentials decode(XdrDataInputStream stream) throws IOException { + public static SorobanAddressCredentials decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanAddressCredentials decodedSorobanAddressCredentials = new SorobanAddressCredentials(); - decodedSorobanAddressCredentials.address = SCAddress.decode(stream); - decodedSorobanAddressCredentials.nonce = Int64.decode(stream); - decodedSorobanAddressCredentials.signatureExpirationLedger = Uint32.decode(stream); - decodedSorobanAddressCredentials.signature = SCVal.decode(stream); + decodedSorobanAddressCredentials.address = SCAddress.decode(stream, maxDepth); + decodedSorobanAddressCredentials.nonce = Int64.decode(stream, maxDepth); + decodedSorobanAddressCredentials.signatureExpirationLedger = Uint32.decode(stream, maxDepth); + decodedSorobanAddressCredentials.signature = SCVal.decode(stream, maxDepth); return decodedSorobanAddressCredentials; } + public static SorobanAddressCredentials decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanAddressCredentials fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +67,7 @@ public static SorobanAddressCredentials fromXdrBase64(String xdr) throws IOExcep public static SorobanAddressCredentials fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntries.java b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntries.java index 949b47897..5903277fe 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntries.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntries.java @@ -31,19 +31,41 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanAuthorizationEntries decode(XdrDataInputStream stream) throws IOException { + public static SorobanAuthorizationEntries decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanAuthorizationEntries decodedSorobanAuthorizationEntries = new SorobanAuthorizationEntries(); int SorobanAuthorizationEntriesSize = stream.readInt(); + if (SorobanAuthorizationEntriesSize < 0) { + throw new IOException( + "SorobanAuthorizationEntries size " + SorobanAuthorizationEntriesSize + " is negative"); + } + int SorobanAuthorizationEntriesRemainingInputLen = stream.getRemainingInputLen(); + if (SorobanAuthorizationEntriesRemainingInputLen >= 0 + && SorobanAuthorizationEntriesRemainingInputLen < SorobanAuthorizationEntriesSize) { + throw new IOException( + "SorobanAuthorizationEntries size " + + SorobanAuthorizationEntriesSize + + " exceeds remaining input length " + + SorobanAuthorizationEntriesRemainingInputLen); + } decodedSorobanAuthorizationEntries.SorobanAuthorizationEntries = new SorobanAuthorizationEntry[SorobanAuthorizationEntriesSize]; for (int i = 0; i < SorobanAuthorizationEntriesSize; i++) { decodedSorobanAuthorizationEntries.SorobanAuthorizationEntries[i] = - SorobanAuthorizationEntry.decode(stream); + SorobanAuthorizationEntry.decode(stream, maxDepth); } return decodedSorobanAuthorizationEntries; } + public static SorobanAuthorizationEntries decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanAuthorizationEntries fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -52,6 +74,7 @@ public static SorobanAuthorizationEntries fromXdrBase64(String xdr) throws IOExc public static SorobanAuthorizationEntries fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntry.java b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntry.java index 43da19c31..be6332303 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizationEntry.java @@ -35,13 +35,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { rootInvocation.encode(stream); } - public static SorobanAuthorizationEntry decode(XdrDataInputStream stream) throws IOException { + public static SorobanAuthorizationEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanAuthorizationEntry decodedSorobanAuthorizationEntry = new SorobanAuthorizationEntry(); - decodedSorobanAuthorizationEntry.credentials = SorobanCredentials.decode(stream); - decodedSorobanAuthorizationEntry.rootInvocation = SorobanAuthorizedInvocation.decode(stream); + decodedSorobanAuthorizationEntry.credentials = SorobanCredentials.decode(stream, maxDepth); + decodedSorobanAuthorizationEntry.rootInvocation = + SorobanAuthorizedInvocation.decode(stream, maxDepth); return decodedSorobanAuthorizationEntry; } + public static SorobanAuthorizationEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanAuthorizationEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +60,7 @@ public static SorobanAuthorizationEntry fromXdrBase64(String xdr) throws IOExcep public static SorobanAuthorizationEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunction.java b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunction.java index a7b5c3e33..a1441699a 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunction.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunction.java @@ -59,25 +59,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanAuthorizedFunction decode(XdrDataInputStream stream) throws IOException { + public static SorobanAuthorizedFunction decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanAuthorizedFunction decodedSorobanAuthorizedFunction = new SorobanAuthorizedFunction(); - SorobanAuthorizedFunctionType discriminant = SorobanAuthorizedFunctionType.decode(stream); + SorobanAuthorizedFunctionType discriminant = + SorobanAuthorizedFunctionType.decode(stream, maxDepth); decodedSorobanAuthorizedFunction.setDiscriminant(discriminant); switch (decodedSorobanAuthorizedFunction.getDiscriminant()) { case SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN: - decodedSorobanAuthorizedFunction.contractFn = InvokeContractArgs.decode(stream); + decodedSorobanAuthorizedFunction.contractFn = InvokeContractArgs.decode(stream, maxDepth); break; case SOROBAN_AUTHORIZED_FUNCTION_TYPE_CREATE_CONTRACT_HOST_FN: - decodedSorobanAuthorizedFunction.createContractHostFn = CreateContractArgs.decode(stream); + decodedSorobanAuthorizedFunction.createContractHostFn = + CreateContractArgs.decode(stream, maxDepth); break; case SOROBAN_AUTHORIZED_FUNCTION_TYPE_CREATE_CONTRACT_V2_HOST_FN: decodedSorobanAuthorizedFunction.createContractV2HostFn = - CreateContractArgsV2.decode(stream); + CreateContractArgsV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSorobanAuthorizedFunction; } + public static SorobanAuthorizedFunction decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanAuthorizedFunction fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -86,6 +99,7 @@ public static SorobanAuthorizedFunction fromXdrBase64(String xdr) throws IOExcep public static SorobanAuthorizedFunction fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunctionType.java b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunctionType.java index 906042719..96a7ab3b3 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunctionType.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedFunctionType.java @@ -34,7 +34,9 @@ public int getValue() { return value; } - public static SorobanAuthorizedFunctionType decode(XdrDataInputStream stream) throws IOException { + public static SorobanAuthorizedFunctionType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -48,6 +50,10 @@ public static SorobanAuthorizedFunctionType decode(XdrDataInputStream stream) th } } + public static SorobanAuthorizedFunctionType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -60,6 +66,7 @@ public static SorobanAuthorizedFunctionType fromXdrBase64(String xdr) throws IOE public static SorobanAuthorizedFunctionType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedInvocation.java b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedInvocation.java index 3a2263a8a..d8b377514 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedInvocation.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanAuthorizedInvocation.java @@ -39,20 +39,42 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanAuthorizedInvocation decode(XdrDataInputStream stream) throws IOException { + public static SorobanAuthorizedInvocation decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanAuthorizedInvocation decodedSorobanAuthorizedInvocation = new SorobanAuthorizedInvocation(); - decodedSorobanAuthorizedInvocation.function = SorobanAuthorizedFunction.decode(stream); + decodedSorobanAuthorizedInvocation.function = + SorobanAuthorizedFunction.decode(stream, maxDepth); int subInvocationsSize = stream.readInt(); + if (subInvocationsSize < 0) { + throw new IOException("subInvocations size " + subInvocationsSize + " is negative"); + } + int subInvocationsRemainingInputLen = stream.getRemainingInputLen(); + if (subInvocationsRemainingInputLen >= 0 + && subInvocationsRemainingInputLen < subInvocationsSize) { + throw new IOException( + "subInvocations size " + + subInvocationsSize + + " exceeds remaining input length " + + subInvocationsRemainingInputLen); + } decodedSorobanAuthorizedInvocation.subInvocations = new SorobanAuthorizedInvocation[subInvocationsSize]; for (int i = 0; i < subInvocationsSize; i++) { decodedSorobanAuthorizedInvocation.subInvocations[i] = - SorobanAuthorizedInvocation.decode(stream); + SorobanAuthorizedInvocation.decode(stream, maxDepth); } return decodedSorobanAuthorizedInvocation; } + public static SorobanAuthorizedInvocation decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanAuthorizedInvocation fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -61,6 +83,7 @@ public static SorobanAuthorizedInvocation fromXdrBase64(String xdr) throws IOExc public static SorobanAuthorizedInvocation fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanCredentials.java b/src/main/java/org/stellar/sdk/xdr/SorobanCredentials.java index ad91f7cf3..48adc89b3 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanCredentials.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanCredentials.java @@ -43,20 +43,31 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanCredentials decode(XdrDataInputStream stream) throws IOException { + public static SorobanCredentials decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanCredentials decodedSorobanCredentials = new SorobanCredentials(); - SorobanCredentialsType discriminant = SorobanCredentialsType.decode(stream); + SorobanCredentialsType discriminant = SorobanCredentialsType.decode(stream, maxDepth); decodedSorobanCredentials.setDiscriminant(discriminant); switch (decodedSorobanCredentials.getDiscriminant()) { case SOROBAN_CREDENTIALS_SOURCE_ACCOUNT: break; case SOROBAN_CREDENTIALS_ADDRESS: - decodedSorobanCredentials.address = SorobanAddressCredentials.decode(stream); + decodedSorobanCredentials.address = SorobanAddressCredentials.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSorobanCredentials; } + public static SorobanCredentials decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanCredentials fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +76,7 @@ public static SorobanCredentials fromXdrBase64(String xdr) throws IOException { public static SorobanCredentials fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanCredentialsType.java b/src/main/java/org/stellar/sdk/xdr/SorobanCredentialsType.java index 5c5b4b9ae..0890fd43a 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanCredentialsType.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanCredentialsType.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static SorobanCredentialsType decode(XdrDataInputStream stream) throws IOException { + public static SorobanCredentialsType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static SorobanCredentialsType decode(XdrDataInputStream stream) throws IO } } + public static SorobanCredentialsType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static SorobanCredentialsType fromXdrBase64(String xdr) throws IOExceptio public static SorobanCredentialsType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanResources.java b/src/main/java/org/stellar/sdk/xdr/SorobanResources.java index 7a089ab9b..b0ff9743e 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanResources.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanResources.java @@ -46,15 +46,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { writeBytes.encode(stream); } - public static SorobanResources decode(XdrDataInputStream stream) throws IOException { + public static SorobanResources decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanResources decodedSorobanResources = new SorobanResources(); - decodedSorobanResources.footprint = LedgerFootprint.decode(stream); - decodedSorobanResources.instructions = Uint32.decode(stream); - decodedSorobanResources.diskReadBytes = Uint32.decode(stream); - decodedSorobanResources.writeBytes = Uint32.decode(stream); + decodedSorobanResources.footprint = LedgerFootprint.decode(stream, maxDepth); + decodedSorobanResources.instructions = Uint32.decode(stream, maxDepth); + decodedSorobanResources.diskReadBytes = Uint32.decode(stream, maxDepth); + decodedSorobanResources.writeBytes = Uint32.decode(stream, maxDepth); return decodedSorobanResources; } + public static SorobanResources decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanResources fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -63,6 +72,7 @@ public static SorobanResources fromXdrBase64(String xdr) throws IOException { public static SorobanResources fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanResourcesExtV0.java b/src/main/java/org/stellar/sdk/xdr/SorobanResourcesExtV0.java index bc24a0f92..b19b2b0cd 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanResourcesExtV0.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanResourcesExtV0.java @@ -39,16 +39,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanResourcesExtV0 decode(XdrDataInputStream stream) throws IOException { + public static SorobanResourcesExtV0 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanResourcesExtV0 decodedSorobanResourcesExtV0 = new SorobanResourcesExtV0(); int archivedSorobanEntriesSize = stream.readInt(); + if (archivedSorobanEntriesSize < 0) { + throw new IOException( + "archivedSorobanEntries size " + archivedSorobanEntriesSize + " is negative"); + } + int archivedSorobanEntriesRemainingInputLen = stream.getRemainingInputLen(); + if (archivedSorobanEntriesRemainingInputLen >= 0 + && archivedSorobanEntriesRemainingInputLen < archivedSorobanEntriesSize) { + throw new IOException( + "archivedSorobanEntries size " + + archivedSorobanEntriesSize + + " exceeds remaining input length " + + archivedSorobanEntriesRemainingInputLen); + } decodedSorobanResourcesExtV0.archivedSorobanEntries = new Uint32[archivedSorobanEntriesSize]; for (int i = 0; i < archivedSorobanEntriesSize; i++) { - decodedSorobanResourcesExtV0.archivedSorobanEntries[i] = Uint32.decode(stream); + decodedSorobanResourcesExtV0.archivedSorobanEntries[i] = Uint32.decode(stream, maxDepth); } return decodedSorobanResourcesExtV0; } + public static SorobanResourcesExtV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanResourcesExtV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -57,6 +79,7 @@ public static SorobanResourcesExtV0 fromXdrBase64(String xdr) throws IOException public static SorobanResourcesExtV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionData.java b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionData.java index 3b2315e9f..6f6383fe8 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionData.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionData.java @@ -53,14 +53,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { resourceFee.encode(stream); } - public static SorobanTransactionData decode(XdrDataInputStream stream) throws IOException { + public static SorobanTransactionData decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanTransactionData decodedSorobanTransactionData = new SorobanTransactionData(); - decodedSorobanTransactionData.ext = SorobanTransactionDataExt.decode(stream); - decodedSorobanTransactionData.resources = SorobanResources.decode(stream); - decodedSorobanTransactionData.resourceFee = Int64.decode(stream); + decodedSorobanTransactionData.ext = SorobanTransactionDataExt.decode(stream, maxDepth); + decodedSorobanTransactionData.resources = SorobanResources.decode(stream, maxDepth); + decodedSorobanTransactionData.resourceFee = Int64.decode(stream, maxDepth); return decodedSorobanTransactionData; } + public static SorobanTransactionData decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanTransactionData fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -69,6 +78,7 @@ public static SorobanTransactionData fromXdrBase64(String xdr) throws IOExceptio public static SorobanTransactionData fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -104,7 +114,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanTransactionDataExt decode(XdrDataInputStream stream) throws IOException { + public static SorobanTransactionDataExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanTransactionDataExt decodedSorobanTransactionDataExt = new SorobanTransactionDataExt(); Integer discriminant = stream.readInt(); decodedSorobanTransactionDataExt.setDiscriminant(discriminant); @@ -112,12 +127,19 @@ public static SorobanTransactionDataExt decode(XdrDataInputStream stream) throws case 0: break; case 1: - decodedSorobanTransactionDataExt.resourceExt = SorobanResourcesExtV0.decode(stream); + decodedSorobanTransactionDataExt.resourceExt = + SorobanResourcesExtV0.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSorobanTransactionDataExt; } + public static SorobanTransactionDataExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanTransactionDataExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -126,6 +148,7 @@ public static SorobanTransactionDataExt fromXdrBase64(String xdr) throws IOExcep public static SorobanTransactionDataExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMeta.java b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMeta.java index 5994f3171..490ebc506 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMeta.java @@ -55,23 +55,55 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanTransactionMeta decode(XdrDataInputStream stream) throws IOException { + public static SorobanTransactionMeta decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanTransactionMeta decodedSorobanTransactionMeta = new SorobanTransactionMeta(); - decodedSorobanTransactionMeta.ext = SorobanTransactionMetaExt.decode(stream); + decodedSorobanTransactionMeta.ext = SorobanTransactionMetaExt.decode(stream, maxDepth); int eventsSize = stream.readInt(); + if (eventsSize < 0) { + throw new IOException("events size " + eventsSize + " is negative"); + } + int eventsRemainingInputLen = stream.getRemainingInputLen(); + if (eventsRemainingInputLen >= 0 && eventsRemainingInputLen < eventsSize) { + throw new IOException( + "events size " + + eventsSize + + " exceeds remaining input length " + + eventsRemainingInputLen); + } decodedSorobanTransactionMeta.events = new ContractEvent[eventsSize]; for (int i = 0; i < eventsSize; i++) { - decodedSorobanTransactionMeta.events[i] = ContractEvent.decode(stream); + decodedSorobanTransactionMeta.events[i] = ContractEvent.decode(stream, maxDepth); } - decodedSorobanTransactionMeta.returnValue = SCVal.decode(stream); + decodedSorobanTransactionMeta.returnValue = SCVal.decode(stream, maxDepth); int diagnosticEventsSize = stream.readInt(); + if (diagnosticEventsSize < 0) { + throw new IOException("diagnosticEvents size " + diagnosticEventsSize + " is negative"); + } + int diagnosticEventsRemainingInputLen = stream.getRemainingInputLen(); + if (diagnosticEventsRemainingInputLen >= 0 + && diagnosticEventsRemainingInputLen < diagnosticEventsSize) { + throw new IOException( + "diagnosticEvents size " + + diagnosticEventsSize + + " exceeds remaining input length " + + diagnosticEventsRemainingInputLen); + } decodedSorobanTransactionMeta.diagnosticEvents = new DiagnosticEvent[diagnosticEventsSize]; for (int i = 0; i < diagnosticEventsSize; i++) { - decodedSorobanTransactionMeta.diagnosticEvents[i] = DiagnosticEvent.decode(stream); + decodedSorobanTransactionMeta.diagnosticEvents[i] = DiagnosticEvent.decode(stream, maxDepth); } return decodedSorobanTransactionMeta; } + public static SorobanTransactionMeta decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanTransactionMeta fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -80,6 +112,7 @@ public static SorobanTransactionMeta fromXdrBase64(String xdr) throws IOExceptio public static SorobanTransactionMeta fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExt.java b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExt.java index a7dbb7411..cf5140a03 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExt.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExt.java @@ -43,7 +43,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanTransactionMetaExt decode(XdrDataInputStream stream) throws IOException { + public static SorobanTransactionMetaExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanTransactionMetaExt decodedSorobanTransactionMetaExt = new SorobanTransactionMetaExt(); Integer discriminant = stream.readInt(); decodedSorobanTransactionMetaExt.setDiscriminant(discriminant); @@ -51,12 +56,18 @@ public static SorobanTransactionMetaExt decode(XdrDataInputStream stream) throws case 0: break; case 1: - decodedSorobanTransactionMetaExt.v1 = SorobanTransactionMetaExtV1.decode(stream); + decodedSorobanTransactionMetaExt.v1 = SorobanTransactionMetaExtV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSorobanTransactionMetaExt; } + public static SorobanTransactionMetaExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanTransactionMetaExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -65,6 +76,7 @@ public static SorobanTransactionMetaExt fromXdrBase64(String xdr) throws IOExcep public static SorobanTransactionMetaExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExtV1.java b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExtV1.java index dd14b527a..227a6f9b7 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExtV1.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaExtV1.java @@ -67,16 +67,27 @@ public void encode(XdrDataOutputStream stream) throws IOException { rentFeeCharged.encode(stream); } - public static SorobanTransactionMetaExtV1 decode(XdrDataInputStream stream) throws IOException { + public static SorobanTransactionMetaExtV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanTransactionMetaExtV1 decodedSorobanTransactionMetaExtV1 = new SorobanTransactionMetaExtV1(); - decodedSorobanTransactionMetaExtV1.ext = ExtensionPoint.decode(stream); - decodedSorobanTransactionMetaExtV1.totalNonRefundableResourceFeeCharged = Int64.decode(stream); - decodedSorobanTransactionMetaExtV1.totalRefundableResourceFeeCharged = Int64.decode(stream); - decodedSorobanTransactionMetaExtV1.rentFeeCharged = Int64.decode(stream); + decodedSorobanTransactionMetaExtV1.ext = ExtensionPoint.decode(stream, maxDepth); + decodedSorobanTransactionMetaExtV1.totalNonRefundableResourceFeeCharged = + Int64.decode(stream, maxDepth); + decodedSorobanTransactionMetaExtV1.totalRefundableResourceFeeCharged = + Int64.decode(stream, maxDepth); + decodedSorobanTransactionMetaExtV1.rentFeeCharged = Int64.decode(stream, maxDepth); return decodedSorobanTransactionMetaExtV1; } + public static SorobanTransactionMetaExtV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanTransactionMetaExtV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -85,6 +96,7 @@ public static SorobanTransactionMetaExtV1 fromXdrBase64(String xdr) throws IOExc public static SorobanTransactionMetaExtV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaV2.java b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaV2.java index 7d1446d05..0e86a7e95 100644 --- a/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaV2.java +++ b/src/main/java/org/stellar/sdk/xdr/SorobanTransactionMetaV2.java @@ -41,16 +41,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SorobanTransactionMetaV2 decode(XdrDataInputStream stream) throws IOException { + public static SorobanTransactionMetaV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SorobanTransactionMetaV2 decodedSorobanTransactionMetaV2 = new SorobanTransactionMetaV2(); - decodedSorobanTransactionMetaV2.ext = SorobanTransactionMetaExt.decode(stream); - int returnValuePresent = stream.readInt(); - if (returnValuePresent != 0) { - decodedSorobanTransactionMetaV2.returnValue = SCVal.decode(stream); + decodedSorobanTransactionMetaV2.ext = SorobanTransactionMetaExt.decode(stream, maxDepth); + boolean returnValuePresent = stream.readXdrBoolean(); + if (returnValuePresent) { + decodedSorobanTransactionMetaV2.returnValue = SCVal.decode(stream, maxDepth); } return decodedSorobanTransactionMetaV2; } + public static SorobanTransactionMetaV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SorobanTransactionMetaV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +68,7 @@ public static SorobanTransactionMetaV2 fromXdrBase64(String xdr) throws IOExcept public static SorobanTransactionMetaV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SponsorshipDescriptor.java b/src/main/java/org/stellar/sdk/xdr/SponsorshipDescriptor.java index 697f8456b..dbe0d44e9 100644 --- a/src/main/java/org/stellar/sdk/xdr/SponsorshipDescriptor.java +++ b/src/main/java/org/stellar/sdk/xdr/SponsorshipDescriptor.java @@ -32,15 +32,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SponsorshipDescriptor decode(XdrDataInputStream stream) throws IOException { + public static SponsorshipDescriptor decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SponsorshipDescriptor decodedSponsorshipDescriptor = new SponsorshipDescriptor(); - int SponsorshipDescriptorPresent = stream.readInt(); - if (SponsorshipDescriptorPresent != 0) { - decodedSponsorshipDescriptor.SponsorshipDescriptor = AccountID.decode(stream); + boolean SponsorshipDescriptorPresent = stream.readXdrBoolean(); + if (SponsorshipDescriptorPresent) { + decodedSponsorshipDescriptor.SponsorshipDescriptor = AccountID.decode(stream, maxDepth); } return decodedSponsorshipDescriptor; } + public static SponsorshipDescriptor decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SponsorshipDescriptor fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +58,7 @@ public static SponsorshipDescriptor fromXdrBase64(String xdr) throws IOException public static SponsorshipDescriptor fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/StateArchivalSettings.java b/src/main/java/org/stellar/sdk/xdr/StateArchivalSettings.java index d71dfe7cd..030370533 100644 --- a/src/main/java/org/stellar/sdk/xdr/StateArchivalSettings.java +++ b/src/main/java/org/stellar/sdk/xdr/StateArchivalSettings.java @@ -70,21 +70,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { startingEvictionScanLevel.encode(stream); } - public static StateArchivalSettings decode(XdrDataInputStream stream) throws IOException { + public static StateArchivalSettings decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; StateArchivalSettings decodedStateArchivalSettings = new StateArchivalSettings(); - decodedStateArchivalSettings.maxEntryTTL = Uint32.decode(stream); - decodedStateArchivalSettings.minTemporaryTTL = Uint32.decode(stream); - decodedStateArchivalSettings.minPersistentTTL = Uint32.decode(stream); - decodedStateArchivalSettings.persistentRentRateDenominator = Int64.decode(stream); - decodedStateArchivalSettings.tempRentRateDenominator = Int64.decode(stream); - decodedStateArchivalSettings.maxEntriesToArchive = Uint32.decode(stream); - decodedStateArchivalSettings.liveSorobanStateSizeWindowSampleSize = Uint32.decode(stream); - decodedStateArchivalSettings.liveSorobanStateSizeWindowSamplePeriod = Uint32.decode(stream); - decodedStateArchivalSettings.evictionScanSize = Uint32.decode(stream); - decodedStateArchivalSettings.startingEvictionScanLevel = Uint32.decode(stream); + decodedStateArchivalSettings.maxEntryTTL = Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.minTemporaryTTL = Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.minPersistentTTL = Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.persistentRentRateDenominator = Int64.decode(stream, maxDepth); + decodedStateArchivalSettings.tempRentRateDenominator = Int64.decode(stream, maxDepth); + decodedStateArchivalSettings.maxEntriesToArchive = Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.liveSorobanStateSizeWindowSampleSize = + Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.liveSorobanStateSizeWindowSamplePeriod = + Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.evictionScanSize = Uint32.decode(stream, maxDepth); + decodedStateArchivalSettings.startingEvictionScanLevel = Uint32.decode(stream, maxDepth); return decodedStateArchivalSettings; } + public static StateArchivalSettings decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static StateArchivalSettings fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -93,6 +104,7 @@ public static StateArchivalSettings fromXdrBase64(String xdr) throws IOException public static StateArchivalSettings fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/StellarMessage.java b/src/main/java/org/stellar/sdk/xdr/StellarMessage.java index 439ef1c92..5bc0aaf36 100644 --- a/src/main/java/org/stellar/sdk/xdr/StellarMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/StellarMessage.java @@ -118,6 +118,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { break; case PEERS: int peersSize = getPeers().length; + if (peersSize > 100) { + throw new IOException("peers size " + peersSize + " exceeds max size 100"); + } stream.writeInt(peersSize); for (int i = 0; i < peersSize; i++) { peers[i].encode(stream); @@ -174,86 +177,110 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static StellarMessage decode(XdrDataInputStream stream) throws IOException { + public static StellarMessage decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; StellarMessage decodedStellarMessage = new StellarMessage(); - MessageType discriminant = MessageType.decode(stream); + MessageType discriminant = MessageType.decode(stream, maxDepth); decodedStellarMessage.setDiscriminant(discriminant); switch (decodedStellarMessage.getDiscriminant()) { case ERROR_MSG: - decodedStellarMessage.error = Error.decode(stream); + decodedStellarMessage.error = Error.decode(stream, maxDepth); break; case HELLO: - decodedStellarMessage.hello = Hello.decode(stream); + decodedStellarMessage.hello = Hello.decode(stream, maxDepth); break; case AUTH: - decodedStellarMessage.auth = Auth.decode(stream); + decodedStellarMessage.auth = Auth.decode(stream, maxDepth); break; case DONT_HAVE: - decodedStellarMessage.dontHave = DontHave.decode(stream); + decodedStellarMessage.dontHave = DontHave.decode(stream, maxDepth); break; case PEERS: int peersSize = stream.readInt(); + if (peersSize < 0) { + throw new IOException("peers size " + peersSize + " is negative"); + } + if (peersSize > 100) { + throw new IOException("peers size " + peersSize + " exceeds max size 100"); + } + int peersRemainingInputLen = stream.getRemainingInputLen(); + if (peersRemainingInputLen >= 0 && peersRemainingInputLen < peersSize) { + throw new IOException( + "peers size " + + peersSize + + " exceeds remaining input length " + + peersRemainingInputLen); + } decodedStellarMessage.peers = new PeerAddress[peersSize]; for (int i = 0; i < peersSize; i++) { - decodedStellarMessage.peers[i] = PeerAddress.decode(stream); + decodedStellarMessage.peers[i] = PeerAddress.decode(stream, maxDepth); } break; case GET_TX_SET: - decodedStellarMessage.txSetHash = Uint256.decode(stream); + decodedStellarMessage.txSetHash = Uint256.decode(stream, maxDepth); break; case TX_SET: - decodedStellarMessage.txSet = TransactionSet.decode(stream); + decodedStellarMessage.txSet = TransactionSet.decode(stream, maxDepth); break; case GENERALIZED_TX_SET: - decodedStellarMessage.generalizedTxSet = GeneralizedTransactionSet.decode(stream); + decodedStellarMessage.generalizedTxSet = GeneralizedTransactionSet.decode(stream, maxDepth); break; case TRANSACTION: - decodedStellarMessage.transaction = TransactionEnvelope.decode(stream); + decodedStellarMessage.transaction = TransactionEnvelope.decode(stream, maxDepth); break; case TIME_SLICED_SURVEY_REQUEST: decodedStellarMessage.signedTimeSlicedSurveyRequestMessage = - SignedTimeSlicedSurveyRequestMessage.decode(stream); + SignedTimeSlicedSurveyRequestMessage.decode(stream, maxDepth); break; case TIME_SLICED_SURVEY_RESPONSE: decodedStellarMessage.signedTimeSlicedSurveyResponseMessage = - SignedTimeSlicedSurveyResponseMessage.decode(stream); + SignedTimeSlicedSurveyResponseMessage.decode(stream, maxDepth); break; case TIME_SLICED_SURVEY_START_COLLECTING: decodedStellarMessage.signedTimeSlicedSurveyStartCollectingMessage = - SignedTimeSlicedSurveyStartCollectingMessage.decode(stream); + SignedTimeSlicedSurveyStartCollectingMessage.decode(stream, maxDepth); break; case TIME_SLICED_SURVEY_STOP_COLLECTING: decodedStellarMessage.signedTimeSlicedSurveyStopCollectingMessage = - SignedTimeSlicedSurveyStopCollectingMessage.decode(stream); + SignedTimeSlicedSurveyStopCollectingMessage.decode(stream, maxDepth); break; case GET_SCP_QUORUMSET: - decodedStellarMessage.qSetHash = Uint256.decode(stream); + decodedStellarMessage.qSetHash = Uint256.decode(stream, maxDepth); break; case SCP_QUORUMSET: - decodedStellarMessage.qSet = SCPQuorumSet.decode(stream); + decodedStellarMessage.qSet = SCPQuorumSet.decode(stream, maxDepth); break; case SCP_MESSAGE: - decodedStellarMessage.envelope = SCPEnvelope.decode(stream); + decodedStellarMessage.envelope = SCPEnvelope.decode(stream, maxDepth); break; case GET_SCP_STATE: - decodedStellarMessage.getSCPLedgerSeq = Uint32.decode(stream); + decodedStellarMessage.getSCPLedgerSeq = Uint32.decode(stream, maxDepth); break; case SEND_MORE: - decodedStellarMessage.sendMoreMessage = SendMore.decode(stream); + decodedStellarMessage.sendMoreMessage = SendMore.decode(stream, maxDepth); break; case SEND_MORE_EXTENDED: - decodedStellarMessage.sendMoreExtendedMessage = SendMoreExtended.decode(stream); + decodedStellarMessage.sendMoreExtendedMessage = SendMoreExtended.decode(stream, maxDepth); break; case FLOOD_ADVERT: - decodedStellarMessage.floodAdvert = FloodAdvert.decode(stream); + decodedStellarMessage.floodAdvert = FloodAdvert.decode(stream, maxDepth); break; case FLOOD_DEMAND: - decodedStellarMessage.floodDemand = FloodDemand.decode(stream); + decodedStellarMessage.floodDemand = FloodDemand.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedStellarMessage; } + public static StellarMessage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static StellarMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -262,6 +289,7 @@ public static StellarMessage fromXdrBase64(String xdr) throws IOException { public static StellarMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/StellarValue.java b/src/main/java/org/stellar/sdk/xdr/StellarValue.java index 182c9fee7..bf8e7c54a 100644 --- a/src/main/java/org/stellar/sdk/xdr/StellarValue.java +++ b/src/main/java/org/stellar/sdk/xdr/StellarValue.java @@ -53,6 +53,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { txSetHash.encode(stream); closeTime.encode(stream); int upgradesSize = getUpgrades().length; + if (upgradesSize > 6) { + throw new IOException("upgrades size " + upgradesSize + " exceeds max size 6"); + } stream.writeInt(upgradesSize); for (int i = 0; i < upgradesSize; i++) { upgrades[i].encode(stream); @@ -60,19 +63,41 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static StellarValue decode(XdrDataInputStream stream) throws IOException { + public static StellarValue decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; StellarValue decodedStellarValue = new StellarValue(); - decodedStellarValue.txSetHash = Hash.decode(stream); - decodedStellarValue.closeTime = TimePoint.decode(stream); + decodedStellarValue.txSetHash = Hash.decode(stream, maxDepth); + decodedStellarValue.closeTime = TimePoint.decode(stream, maxDepth); int upgradesSize = stream.readInt(); + if (upgradesSize < 0) { + throw new IOException("upgrades size " + upgradesSize + " is negative"); + } + if (upgradesSize > 6) { + throw new IOException("upgrades size " + upgradesSize + " exceeds max size 6"); + } + int upgradesRemainingInputLen = stream.getRemainingInputLen(); + if (upgradesRemainingInputLen >= 0 && upgradesRemainingInputLen < upgradesSize) { + throw new IOException( + "upgrades size " + + upgradesSize + + " exceeds remaining input length " + + upgradesRemainingInputLen); + } decodedStellarValue.upgrades = new UpgradeType[upgradesSize]; for (int i = 0; i < upgradesSize; i++) { - decodedStellarValue.upgrades[i] = UpgradeType.decode(stream); + decodedStellarValue.upgrades[i] = UpgradeType.decode(stream, maxDepth); } - decodedStellarValue.ext = StellarValueExt.decode(stream); + decodedStellarValue.ext = StellarValueExt.decode(stream, maxDepth); return decodedStellarValue; } + public static StellarValue decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static StellarValue fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -81,6 +106,7 @@ public static StellarValue fromXdrBase64(String xdr) throws IOException { public static StellarValue fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -116,20 +142,32 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static StellarValueExt decode(XdrDataInputStream stream) throws IOException { + public static StellarValueExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; StellarValueExt decodedStellarValueExt = new StellarValueExt(); - StellarValueType discriminant = StellarValueType.decode(stream); + StellarValueType discriminant = StellarValueType.decode(stream, maxDepth); decodedStellarValueExt.setDiscriminant(discriminant); switch (decodedStellarValueExt.getDiscriminant()) { case STELLAR_VALUE_BASIC: break; case STELLAR_VALUE_SIGNED: - decodedStellarValueExt.lcValueSignature = LedgerCloseValueSignature.decode(stream); + decodedStellarValueExt.lcValueSignature = + LedgerCloseValueSignature.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedStellarValueExt; } + public static StellarValueExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static StellarValueExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -138,6 +176,7 @@ public static StellarValueExt fromXdrBase64(String xdr) throws IOException { public static StellarValueExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/StellarValueType.java b/src/main/java/org/stellar/sdk/xdr/StellarValueType.java index 632923aee..ea5387118 100644 --- a/src/main/java/org/stellar/sdk/xdr/StellarValueType.java +++ b/src/main/java/org/stellar/sdk/xdr/StellarValueType.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static StellarValueType decode(XdrDataInputStream stream) throws IOException { + public static StellarValueType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -44,6 +46,10 @@ public static StellarValueType decode(XdrDataInputStream stream) throws IOExcept } } + public static StellarValueType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -56,6 +62,7 @@ public static StellarValueType fromXdrBase64(String xdr) throws IOException { public static StellarValueType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/StoredDebugTransactionSet.java b/src/main/java/org/stellar/sdk/xdr/StoredDebugTransactionSet.java index 7f5c7ae79..c5d617378 100644 --- a/src/main/java/org/stellar/sdk/xdr/StoredDebugTransactionSet.java +++ b/src/main/java/org/stellar/sdk/xdr/StoredDebugTransactionSet.java @@ -38,14 +38,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { scpValue.encode(stream); } - public static StoredDebugTransactionSet decode(XdrDataInputStream stream) throws IOException { + public static StoredDebugTransactionSet decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; StoredDebugTransactionSet decodedStoredDebugTransactionSet = new StoredDebugTransactionSet(); - decodedStoredDebugTransactionSet.txSet = StoredTransactionSet.decode(stream); - decodedStoredDebugTransactionSet.ledgerSeq = Uint32.decode(stream); - decodedStoredDebugTransactionSet.scpValue = StellarValue.decode(stream); + decodedStoredDebugTransactionSet.txSet = StoredTransactionSet.decode(stream, maxDepth); + decodedStoredDebugTransactionSet.ledgerSeq = Uint32.decode(stream, maxDepth); + decodedStoredDebugTransactionSet.scpValue = StellarValue.decode(stream, maxDepth); return decodedStoredDebugTransactionSet; } + public static StoredDebugTransactionSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static StoredDebugTransactionSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +63,7 @@ public static StoredDebugTransactionSet fromXdrBase64(String xdr) throws IOExcep public static StoredDebugTransactionSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/StoredTransactionSet.java b/src/main/java/org/stellar/sdk/xdr/StoredTransactionSet.java index 7763a5673..25dd3c1b2 100644 --- a/src/main/java/org/stellar/sdk/xdr/StoredTransactionSet.java +++ b/src/main/java/org/stellar/sdk/xdr/StoredTransactionSet.java @@ -45,21 +45,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static StoredTransactionSet decode(XdrDataInputStream stream) throws IOException { + public static StoredTransactionSet decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; StoredTransactionSet decodedStoredTransactionSet = new StoredTransactionSet(); Integer discriminant = stream.readInt(); decodedStoredTransactionSet.setDiscriminant(discriminant); switch (decodedStoredTransactionSet.getDiscriminant()) { case 0: - decodedStoredTransactionSet.txSet = TransactionSet.decode(stream); + decodedStoredTransactionSet.txSet = TransactionSet.decode(stream, maxDepth); break; case 1: - decodedStoredTransactionSet.generalizedTxSet = GeneralizedTransactionSet.decode(stream); + decodedStoredTransactionSet.generalizedTxSet = + GeneralizedTransactionSet.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedStoredTransactionSet; } + public static StoredTransactionSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static StoredTransactionSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -68,6 +80,7 @@ public static StoredTransactionSet fromXdrBase64(String xdr) throws IOException public static StoredTransactionSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/String32.java b/src/main/java/org/stellar/sdk/xdr/String32.java index 6556fba31..97ee775c4 100644 --- a/src/main/java/org/stellar/sdk/xdr/String32.java +++ b/src/main/java/org/stellar/sdk/xdr/String32.java @@ -24,15 +24,27 @@ public class String32 implements XdrElement { private XdrString string32; public void encode(XdrDataOutputStream stream) throws IOException { + int string32Size = string32.getBytes().length; + if (string32Size > 32) { + throw new IOException("string32 size " + string32Size + " exceeds max size 32"); + } string32.encode(stream); } - public static String32 decode(XdrDataInputStream stream) throws IOException { + public static String32 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; String32 decodedString32 = new String32(); - decodedString32.string32 = XdrString.decode(stream, 32); + decodedString32.string32 = XdrString.decode(stream, maxDepth, 32); return decodedString32; } + public static String32 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static String32 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +53,7 @@ public static String32 fromXdrBase64(String xdr) throws IOException { public static String32 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/String64.java b/src/main/java/org/stellar/sdk/xdr/String64.java index b658a14d8..6f598698e 100644 --- a/src/main/java/org/stellar/sdk/xdr/String64.java +++ b/src/main/java/org/stellar/sdk/xdr/String64.java @@ -24,15 +24,27 @@ public class String64 implements XdrElement { private XdrString string64; public void encode(XdrDataOutputStream stream) throws IOException { + int string64Size = string64.getBytes().length; + if (string64Size > 64) { + throw new IOException("string64 size " + string64Size + " exceeds max size 64"); + } string64.encode(stream); } - public static String64 decode(XdrDataInputStream stream) throws IOException { + public static String64 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; String64 decodedString64 = new String64(); - decodedString64.string64 = XdrString.decode(stream, 64); + decodedString64.string64 = XdrString.decode(stream, maxDepth, 64); return decodedString64; } + public static String64 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static String64 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +53,7 @@ public static String64 fromXdrBase64(String xdr) throws IOException { public static String64 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyMessageCommandType.java b/src/main/java/org/stellar/sdk/xdr/SurveyMessageCommandType.java index b6bc351d8..8935b8cf0 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyMessageCommandType.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyMessageCommandType.java @@ -30,7 +30,9 @@ public int getValue() { return value; } - public static SurveyMessageCommandType decode(XdrDataInputStream stream) throws IOException { + public static SurveyMessageCommandType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -40,6 +42,10 @@ public static SurveyMessageCommandType decode(XdrDataInputStream stream) throws } } + public static SurveyMessageCommandType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +58,7 @@ public static SurveyMessageCommandType fromXdrBase64(String xdr) throws IOExcept public static SurveyMessageCommandType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyMessageResponseType.java b/src/main/java/org/stellar/sdk/xdr/SurveyMessageResponseType.java index 483f3670f..846f1df14 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyMessageResponseType.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyMessageResponseType.java @@ -30,7 +30,9 @@ public int getValue() { return value; } - public static SurveyMessageResponseType decode(XdrDataInputStream stream) throws IOException { + public static SurveyMessageResponseType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 2: @@ -40,6 +42,10 @@ public static SurveyMessageResponseType decode(XdrDataInputStream stream) throws } } + public static SurveyMessageResponseType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -52,6 +58,7 @@ public static SurveyMessageResponseType fromXdrBase64(String xdr) throws IOExcep public static SurveyMessageResponseType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyRequestMessage.java b/src/main/java/org/stellar/sdk/xdr/SurveyRequestMessage.java index 952c33dcc..048bca4f2 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyRequestMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyRequestMessage.java @@ -44,16 +44,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { commandType.encode(stream); } - public static SurveyRequestMessage decode(XdrDataInputStream stream) throws IOException { + public static SurveyRequestMessage decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SurveyRequestMessage decodedSurveyRequestMessage = new SurveyRequestMessage(); - decodedSurveyRequestMessage.surveyorPeerID = NodeID.decode(stream); - decodedSurveyRequestMessage.surveyedPeerID = NodeID.decode(stream); - decodedSurveyRequestMessage.ledgerNum = Uint32.decode(stream); - decodedSurveyRequestMessage.encryptionKey = Curve25519Public.decode(stream); - decodedSurveyRequestMessage.commandType = SurveyMessageCommandType.decode(stream); + decodedSurveyRequestMessage.surveyorPeerID = NodeID.decode(stream, maxDepth); + decodedSurveyRequestMessage.surveyedPeerID = NodeID.decode(stream, maxDepth); + decodedSurveyRequestMessage.ledgerNum = Uint32.decode(stream, maxDepth); + decodedSurveyRequestMessage.encryptionKey = Curve25519Public.decode(stream, maxDepth); + decodedSurveyRequestMessage.commandType = SurveyMessageCommandType.decode(stream, maxDepth); return decodedSurveyRequestMessage; } + public static SurveyRequestMessage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SurveyRequestMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +71,7 @@ public static SurveyRequestMessage fromXdrBase64(String xdr) throws IOException public static SurveyRequestMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java b/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java index fcc11603b..842c96ee1 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyResponseBody.java @@ -39,18 +39,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static SurveyResponseBody decode(XdrDataInputStream stream) throws IOException { + public static SurveyResponseBody decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SurveyResponseBody decodedSurveyResponseBody = new SurveyResponseBody(); - SurveyMessageResponseType discriminant = SurveyMessageResponseType.decode(stream); + SurveyMessageResponseType discriminant = SurveyMessageResponseType.decode(stream, maxDepth); decodedSurveyResponseBody.setDiscriminant(discriminant); switch (decodedSurveyResponseBody.getDiscriminant()) { case SURVEY_TOPOLOGY_RESPONSE_V2: - decodedSurveyResponseBody.topologyResponseBodyV2 = TopologyResponseBodyV2.decode(stream); + decodedSurveyResponseBody.topologyResponseBodyV2 = + TopologyResponseBodyV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedSurveyResponseBody; } + public static SurveyResponseBody decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SurveyResponseBody fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -59,6 +71,7 @@ public static SurveyResponseBody fromXdrBase64(String xdr) throws IOException { public static SurveyResponseBody fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/SurveyResponseMessage.java b/src/main/java/org/stellar/sdk/xdr/SurveyResponseMessage.java index e3e7f04c3..8f1617743 100644 --- a/src/main/java/org/stellar/sdk/xdr/SurveyResponseMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/SurveyResponseMessage.java @@ -44,16 +44,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { encryptedBody.encode(stream); } - public static SurveyResponseMessage decode(XdrDataInputStream stream) throws IOException { + public static SurveyResponseMessage decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; SurveyResponseMessage decodedSurveyResponseMessage = new SurveyResponseMessage(); - decodedSurveyResponseMessage.surveyorPeerID = NodeID.decode(stream); - decodedSurveyResponseMessage.surveyedPeerID = NodeID.decode(stream); - decodedSurveyResponseMessage.ledgerNum = Uint32.decode(stream); - decodedSurveyResponseMessage.commandType = SurveyMessageCommandType.decode(stream); - decodedSurveyResponseMessage.encryptedBody = EncryptedBody.decode(stream); + decodedSurveyResponseMessage.surveyorPeerID = NodeID.decode(stream, maxDepth); + decodedSurveyResponseMessage.surveyedPeerID = NodeID.decode(stream, maxDepth); + decodedSurveyResponseMessage.ledgerNum = Uint32.decode(stream, maxDepth); + decodedSurveyResponseMessage.commandType = SurveyMessageCommandType.decode(stream, maxDepth); + decodedSurveyResponseMessage.encryptedBody = EncryptedBody.decode(stream, maxDepth); return decodedSurveyResponseMessage; } + public static SurveyResponseMessage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static SurveyResponseMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +71,7 @@ public static SurveyResponseMessage fromXdrBase64(String xdr) throws IOException public static SurveyResponseMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TTLEntry.java b/src/main/java/org/stellar/sdk/xdr/TTLEntry.java index 66024d744..d53e9fda6 100644 --- a/src/main/java/org/stellar/sdk/xdr/TTLEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/TTLEntry.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { liveUntilLedgerSeq.encode(stream); } - public static TTLEntry decode(XdrDataInputStream stream) throws IOException { + public static TTLEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TTLEntry decodedTTLEntry = new TTLEntry(); - decodedTTLEntry.keyHash = Hash.decode(stream); - decodedTTLEntry.liveUntilLedgerSeq = Uint32.decode(stream); + decodedTTLEntry.keyHash = Hash.decode(stream, maxDepth); + decodedTTLEntry.liveUntilLedgerSeq = Uint32.decode(stream, maxDepth); return decodedTTLEntry; } + public static TTLEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TTLEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static TTLEntry fromXdrBase64(String xdr) throws IOException { public static TTLEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/ThresholdIndexes.java b/src/main/java/org/stellar/sdk/xdr/ThresholdIndexes.java index bf411f50e..c8985f99b 100644 --- a/src/main/java/org/stellar/sdk/xdr/ThresholdIndexes.java +++ b/src/main/java/org/stellar/sdk/xdr/ThresholdIndexes.java @@ -36,7 +36,9 @@ public int getValue() { return value; } - public static ThresholdIndexes decode(XdrDataInputStream stream) throws IOException { + public static ThresholdIndexes decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -52,6 +54,10 @@ public static ThresholdIndexes decode(XdrDataInputStream stream) throws IOExcept } } + public static ThresholdIndexes decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -64,6 +70,7 @@ public static ThresholdIndexes fromXdrBase64(String xdr) throws IOException { public static ThresholdIndexes fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Thresholds.java b/src/main/java/org/stellar/sdk/xdr/Thresholds.java index 8f95cc505..53c59d402 100644 --- a/src/main/java/org/stellar/sdk/xdr/Thresholds.java +++ b/src/main/java/org/stellar/sdk/xdr/Thresholds.java @@ -25,17 +25,28 @@ public class Thresholds implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int ThresholdsSize = Thresholds.length; + if (ThresholdsSize != 4) { + throw new IOException("Thresholds size " + ThresholdsSize + " does not match fixed size 4"); + } stream.write(getThresholds(), 0, ThresholdsSize); } - public static Thresholds decode(XdrDataInputStream stream) throws IOException { + public static Thresholds decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Thresholds decodedThresholds = new Thresholds(); int ThresholdsSize = 4; decodedThresholds.Thresholds = new byte[ThresholdsSize]; - stream.read(decodedThresholds.Thresholds, 0, ThresholdsSize); + stream.readPaddedData(decodedThresholds.Thresholds, 0, ThresholdsSize); return decodedThresholds; } + public static Thresholds decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Thresholds fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -44,6 +55,7 @@ public static Thresholds fromXdrBase64(String xdr) throws IOException { public static Thresholds fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeBounds.java b/src/main/java/org/stellar/sdk/xdr/TimeBounds.java index 72d3095b5..89c7983a9 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeBounds.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeBounds.java @@ -35,13 +35,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { maxTime.encode(stream); } - public static TimeBounds decode(XdrDataInputStream stream) throws IOException { + public static TimeBounds decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeBounds decodedTimeBounds = new TimeBounds(); - decodedTimeBounds.minTime = TimePoint.decode(stream); - decodedTimeBounds.maxTime = TimePoint.decode(stream); + decodedTimeBounds.minTime = TimePoint.decode(stream, maxDepth); + decodedTimeBounds.maxTime = TimePoint.decode(stream, maxDepth); return decodedTimeBounds; } + public static TimeBounds decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeBounds fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +58,7 @@ public static TimeBounds fromXdrBase64(String xdr) throws IOException { public static TimeBounds fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimePoint.java b/src/main/java/org/stellar/sdk/xdr/TimePoint.java index e8c5aec7d..25849cf3c 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimePoint.java +++ b/src/main/java/org/stellar/sdk/xdr/TimePoint.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { TimePoint.encode(stream); } - public static TimePoint decode(XdrDataInputStream stream) throws IOException { + public static TimePoint decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimePoint decodedTimePoint = new TimePoint(); - decodedTimePoint.TimePoint = Uint64.decode(stream); + decodedTimePoint.TimePoint = Uint64.decode(stream, maxDepth); return decodedTimePoint; } + public static TimePoint decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimePoint fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static TimePoint fromXdrBase64(String xdr) throws IOException { public static TimePoint fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedNodeData.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedNodeData.java index 56dd494d1..1ae24a224 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedNodeData.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedNodeData.java @@ -65,21 +65,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { maxOutboundPeerCount.encode(stream); } - public static TimeSlicedNodeData decode(XdrDataInputStream stream) throws IOException { + public static TimeSlicedNodeData decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedNodeData decodedTimeSlicedNodeData = new TimeSlicedNodeData(); - decodedTimeSlicedNodeData.addedAuthenticatedPeers = Uint32.decode(stream); - decodedTimeSlicedNodeData.droppedAuthenticatedPeers = Uint32.decode(stream); - decodedTimeSlicedNodeData.totalInboundPeerCount = Uint32.decode(stream); - decodedTimeSlicedNodeData.totalOutboundPeerCount = Uint32.decode(stream); - decodedTimeSlicedNodeData.p75SCPFirstToSelfLatencyMs = Uint32.decode(stream); - decodedTimeSlicedNodeData.p75SCPSelfToOtherLatencyMs = Uint32.decode(stream); - decodedTimeSlicedNodeData.lostSyncCount = Uint32.decode(stream); - decodedTimeSlicedNodeData.isValidator = stream.readInt() == 1 ? true : false; - decodedTimeSlicedNodeData.maxInboundPeerCount = Uint32.decode(stream); - decodedTimeSlicedNodeData.maxOutboundPeerCount = Uint32.decode(stream); + decodedTimeSlicedNodeData.addedAuthenticatedPeers = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.droppedAuthenticatedPeers = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.totalInboundPeerCount = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.totalOutboundPeerCount = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.p75SCPFirstToSelfLatencyMs = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.p75SCPSelfToOtherLatencyMs = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.lostSyncCount = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.isValidator = stream.readXdrBoolean(); + decodedTimeSlicedNodeData.maxInboundPeerCount = Uint32.decode(stream, maxDepth); + decodedTimeSlicedNodeData.maxOutboundPeerCount = Uint32.decode(stream, maxDepth); return decodedTimeSlicedNodeData; } + public static TimeSlicedNodeData decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedNodeData fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -88,6 +97,7 @@ public static TimeSlicedNodeData fromXdrBase64(String xdr) throws IOException { public static TimeSlicedNodeData fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerData.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerData.java index f4e4018cd..18e1fb8fe 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerData.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerData.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { averageLatencyMs.encode(stream); } - public static TimeSlicedPeerData decode(XdrDataInputStream stream) throws IOException { + public static TimeSlicedPeerData decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedPeerData decodedTimeSlicedPeerData = new TimeSlicedPeerData(); - decodedTimeSlicedPeerData.peerStats = PeerStats.decode(stream); - decodedTimeSlicedPeerData.averageLatencyMs = Uint32.decode(stream); + decodedTimeSlicedPeerData.peerStats = PeerStats.decode(stream, maxDepth); + decodedTimeSlicedPeerData.averageLatencyMs = Uint32.decode(stream, maxDepth); return decodedTimeSlicedPeerData; } + public static TimeSlicedPeerData decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedPeerData fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static TimeSlicedPeerData fromXdrBase64(String xdr) throws IOException { public static TimeSlicedPeerData fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerDataList.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerDataList.java index 1059916c2..ecff6c9ad 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerDataList.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedPeerDataList.java @@ -25,23 +25,54 @@ public class TimeSlicedPeerDataList implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int TimeSlicedPeerDataListSize = getTimeSlicedPeerDataList().length; + if (TimeSlicedPeerDataListSize > 25) { + throw new IOException( + "TimeSlicedPeerDataList size " + TimeSlicedPeerDataListSize + " exceeds max size 25"); + } stream.writeInt(TimeSlicedPeerDataListSize); for (int i = 0; i < TimeSlicedPeerDataListSize; i++) { TimeSlicedPeerDataList[i].encode(stream); } } - public static TimeSlicedPeerDataList decode(XdrDataInputStream stream) throws IOException { + public static TimeSlicedPeerDataList decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedPeerDataList decodedTimeSlicedPeerDataList = new TimeSlicedPeerDataList(); int TimeSlicedPeerDataListSize = stream.readInt(); + if (TimeSlicedPeerDataListSize < 0) { + throw new IOException( + "TimeSlicedPeerDataList size " + TimeSlicedPeerDataListSize + " is negative"); + } + if (TimeSlicedPeerDataListSize > 25) { + throw new IOException( + "TimeSlicedPeerDataList size " + TimeSlicedPeerDataListSize + " exceeds max size 25"); + } + int TimeSlicedPeerDataListRemainingInputLen = stream.getRemainingInputLen(); + if (TimeSlicedPeerDataListRemainingInputLen >= 0 + && TimeSlicedPeerDataListRemainingInputLen < TimeSlicedPeerDataListSize) { + throw new IOException( + "TimeSlicedPeerDataList size " + + TimeSlicedPeerDataListSize + + " exceeds remaining input length " + + TimeSlicedPeerDataListRemainingInputLen); + } decodedTimeSlicedPeerDataList.TimeSlicedPeerDataList = new TimeSlicedPeerData[TimeSlicedPeerDataListSize]; for (int i = 0; i < TimeSlicedPeerDataListSize; i++) { - decodedTimeSlicedPeerDataList.TimeSlicedPeerDataList[i] = TimeSlicedPeerData.decode(stream); + decodedTimeSlicedPeerDataList.TimeSlicedPeerDataList[i] = + TimeSlicedPeerData.decode(stream, maxDepth); } return decodedTimeSlicedPeerDataList; } + public static TimeSlicedPeerDataList decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedPeerDataList fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +81,7 @@ public static TimeSlicedPeerDataList fromXdrBase64(String xdr) throws IOExceptio public static TimeSlicedPeerDataList fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyRequestMessage.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyRequestMessage.java index adfdfce16..2f4876376 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyRequestMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyRequestMessage.java @@ -41,17 +41,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { outboundPeersIndex.encode(stream); } - public static TimeSlicedSurveyRequestMessage decode(XdrDataInputStream stream) + public static TimeSlicedSurveyRequestMessage decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedSurveyRequestMessage decodedTimeSlicedSurveyRequestMessage = new TimeSlicedSurveyRequestMessage(); - decodedTimeSlicedSurveyRequestMessage.request = SurveyRequestMessage.decode(stream); - decodedTimeSlicedSurveyRequestMessage.nonce = Uint32.decode(stream); - decodedTimeSlicedSurveyRequestMessage.inboundPeersIndex = Uint32.decode(stream); - decodedTimeSlicedSurveyRequestMessage.outboundPeersIndex = Uint32.decode(stream); + decodedTimeSlicedSurveyRequestMessage.request = SurveyRequestMessage.decode(stream, maxDepth); + decodedTimeSlicedSurveyRequestMessage.nonce = Uint32.decode(stream, maxDepth); + decodedTimeSlicedSurveyRequestMessage.inboundPeersIndex = Uint32.decode(stream, maxDepth); + decodedTimeSlicedSurveyRequestMessage.outboundPeersIndex = Uint32.decode(stream, maxDepth); return decodedTimeSlicedSurveyRequestMessage; } + public static TimeSlicedSurveyRequestMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedSurveyRequestMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +69,7 @@ public static TimeSlicedSurveyRequestMessage fromXdrBase64(String xdr) throws IO public static TimeSlicedSurveyRequestMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyResponseMessage.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyResponseMessage.java index ff2f013b5..1032e031a 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyResponseMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyResponseMessage.java @@ -35,15 +35,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { nonce.encode(stream); } - public static TimeSlicedSurveyResponseMessage decode(XdrDataInputStream stream) + public static TimeSlicedSurveyResponseMessage decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedSurveyResponseMessage decodedTimeSlicedSurveyResponseMessage = new TimeSlicedSurveyResponseMessage(); - decodedTimeSlicedSurveyResponseMessage.response = SurveyResponseMessage.decode(stream); - decodedTimeSlicedSurveyResponseMessage.nonce = Uint32.decode(stream); + decodedTimeSlicedSurveyResponseMessage.response = + SurveyResponseMessage.decode(stream, maxDepth); + decodedTimeSlicedSurveyResponseMessage.nonce = Uint32.decode(stream, maxDepth); return decodedTimeSlicedSurveyResponseMessage; } + public static TimeSlicedSurveyResponseMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedSurveyResponseMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -52,6 +62,7 @@ public static TimeSlicedSurveyResponseMessage fromXdrBase64(String xdr) throws I public static TimeSlicedSurveyResponseMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStartCollectingMessage.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStartCollectingMessage.java index de8460599..19e00e081 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStartCollectingMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStartCollectingMessage.java @@ -38,16 +38,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { ledgerNum.encode(stream); } - public static TimeSlicedSurveyStartCollectingMessage decode(XdrDataInputStream stream) - throws IOException { + public static TimeSlicedSurveyStartCollectingMessage decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedSurveyStartCollectingMessage decodedTimeSlicedSurveyStartCollectingMessage = new TimeSlicedSurveyStartCollectingMessage(); - decodedTimeSlicedSurveyStartCollectingMessage.surveyorID = NodeID.decode(stream); - decodedTimeSlicedSurveyStartCollectingMessage.nonce = Uint32.decode(stream); - decodedTimeSlicedSurveyStartCollectingMessage.ledgerNum = Uint32.decode(stream); + decodedTimeSlicedSurveyStartCollectingMessage.surveyorID = NodeID.decode(stream, maxDepth); + decodedTimeSlicedSurveyStartCollectingMessage.nonce = Uint32.decode(stream, maxDepth); + decodedTimeSlicedSurveyStartCollectingMessage.ledgerNum = Uint32.decode(stream, maxDepth); return decodedTimeSlicedSurveyStartCollectingMessage; } + public static TimeSlicedSurveyStartCollectingMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedSurveyStartCollectingMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -58,6 +67,7 @@ public static TimeSlicedSurveyStartCollectingMessage fromXdrByteArray(byte[] xdr throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStopCollectingMessage.java b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStopCollectingMessage.java index 5f567e0d7..087f545a9 100644 --- a/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStopCollectingMessage.java +++ b/src/main/java/org/stellar/sdk/xdr/TimeSlicedSurveyStopCollectingMessage.java @@ -38,16 +38,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { ledgerNum.encode(stream); } - public static TimeSlicedSurveyStopCollectingMessage decode(XdrDataInputStream stream) - throws IOException { + public static TimeSlicedSurveyStopCollectingMessage decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TimeSlicedSurveyStopCollectingMessage decodedTimeSlicedSurveyStopCollectingMessage = new TimeSlicedSurveyStopCollectingMessage(); - decodedTimeSlicedSurveyStopCollectingMessage.surveyorID = NodeID.decode(stream); - decodedTimeSlicedSurveyStopCollectingMessage.nonce = Uint32.decode(stream); - decodedTimeSlicedSurveyStopCollectingMessage.ledgerNum = Uint32.decode(stream); + decodedTimeSlicedSurveyStopCollectingMessage.surveyorID = NodeID.decode(stream, maxDepth); + decodedTimeSlicedSurveyStopCollectingMessage.nonce = Uint32.decode(stream, maxDepth); + decodedTimeSlicedSurveyStopCollectingMessage.ledgerNum = Uint32.decode(stream, maxDepth); return decodedTimeSlicedSurveyStopCollectingMessage; } + public static TimeSlicedSurveyStopCollectingMessage decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TimeSlicedSurveyStopCollectingMessage fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -57,6 +66,7 @@ public static TimeSlicedSurveyStopCollectingMessage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TopologyResponseBodyV2.java b/src/main/java/org/stellar/sdk/xdr/TopologyResponseBodyV2.java index 6dd53f039..df2d94f51 100644 --- a/src/main/java/org/stellar/sdk/xdr/TopologyResponseBodyV2.java +++ b/src/main/java/org/stellar/sdk/xdr/TopologyResponseBodyV2.java @@ -38,14 +38,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { nodeData.encode(stream); } - public static TopologyResponseBodyV2 decode(XdrDataInputStream stream) throws IOException { + public static TopologyResponseBodyV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TopologyResponseBodyV2 decodedTopologyResponseBodyV2 = new TopologyResponseBodyV2(); - decodedTopologyResponseBodyV2.inboundPeers = TimeSlicedPeerDataList.decode(stream); - decodedTopologyResponseBodyV2.outboundPeers = TimeSlicedPeerDataList.decode(stream); - decodedTopologyResponseBodyV2.nodeData = TimeSlicedNodeData.decode(stream); + decodedTopologyResponseBodyV2.inboundPeers = TimeSlicedPeerDataList.decode(stream, maxDepth); + decodedTopologyResponseBodyV2.outboundPeers = TimeSlicedPeerDataList.decode(stream, maxDepth); + decodedTopologyResponseBodyV2.nodeData = TimeSlicedNodeData.decode(stream, maxDepth); return decodedTopologyResponseBodyV2; } + public static TopologyResponseBodyV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TopologyResponseBodyV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +63,7 @@ public static TopologyResponseBodyV2 fromXdrBase64(String xdr) throws IOExceptio public static TopologyResponseBodyV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Transaction.java b/src/main/java/org/stellar/sdk/xdr/Transaction.java index 604614927..9c1b9f5ee 100644 --- a/src/main/java/org/stellar/sdk/xdr/Transaction.java +++ b/src/main/java/org/stellar/sdk/xdr/Transaction.java @@ -64,6 +64,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { cond.encode(stream); memo.encode(stream); int operationsSize = getOperations().length; + if (operationsSize > 100) { + throw new IOException("operations size " + operationsSize + " exceeds max size 100"); + } stream.writeInt(operationsSize); for (int i = 0; i < operationsSize; i++) { operations[i].encode(stream); @@ -71,22 +74,44 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static Transaction decode(XdrDataInputStream stream) throws IOException { + public static Transaction decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Transaction decodedTransaction = new Transaction(); - decodedTransaction.sourceAccount = MuxedAccount.decode(stream); - decodedTransaction.fee = Uint32.decode(stream); - decodedTransaction.seqNum = SequenceNumber.decode(stream); - decodedTransaction.cond = Preconditions.decode(stream); - decodedTransaction.memo = Memo.decode(stream); + decodedTransaction.sourceAccount = MuxedAccount.decode(stream, maxDepth); + decodedTransaction.fee = Uint32.decode(stream, maxDepth); + decodedTransaction.seqNum = SequenceNumber.decode(stream, maxDepth); + decodedTransaction.cond = Preconditions.decode(stream, maxDepth); + decodedTransaction.memo = Memo.decode(stream, maxDepth); int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + if (operationsSize > 100) { + throw new IOException("operations size " + operationsSize + " exceeds max size 100"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransaction.operations = new Operation[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransaction.operations[i] = Operation.decode(stream); + decodedTransaction.operations[i] = Operation.decode(stream, maxDepth); } - decodedTransaction.ext = TransactionExt.decode(stream); + decodedTransaction.ext = TransactionExt.decode(stream, maxDepth); return decodedTransaction; } + public static Transaction decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Transaction fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -95,6 +120,7 @@ public static Transaction fromXdrBase64(String xdr) throws IOException { public static Transaction fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -130,7 +156,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionExt decode(XdrDataInputStream stream) throws IOException { + public static TransactionExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionExt decodedTransactionExt = new TransactionExt(); Integer discriminant = stream.readInt(); decodedTransactionExt.setDiscriminant(discriminant); @@ -138,12 +169,18 @@ public static TransactionExt decode(XdrDataInputStream stream) throws IOExceptio case 0: break; case 1: - decodedTransactionExt.sorobanData = SorobanTransactionData.decode(stream); + decodedTransactionExt.sorobanData = SorobanTransactionData.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionExt; } + public static TransactionExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -152,6 +189,7 @@ public static TransactionExt fromXdrBase64(String xdr) throws IOException { public static TransactionExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java index d4106b45a..7057c32d2 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionEnvelope.java @@ -51,24 +51,35 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionEnvelope decode(XdrDataInputStream stream) throws IOException { + public static TransactionEnvelope decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionEnvelope decodedTransactionEnvelope = new TransactionEnvelope(); - EnvelopeType discriminant = EnvelopeType.decode(stream); + EnvelopeType discriminant = EnvelopeType.decode(stream, maxDepth); decodedTransactionEnvelope.setDiscriminant(discriminant); switch (decodedTransactionEnvelope.getDiscriminant()) { case ENVELOPE_TYPE_TX_V0: - decodedTransactionEnvelope.v0 = TransactionV0Envelope.decode(stream); + decodedTransactionEnvelope.v0 = TransactionV0Envelope.decode(stream, maxDepth); break; case ENVELOPE_TYPE_TX: - decodedTransactionEnvelope.v1 = TransactionV1Envelope.decode(stream); + decodedTransactionEnvelope.v1 = TransactionV1Envelope.decode(stream, maxDepth); break; case ENVELOPE_TYPE_TX_FEE_BUMP: - decodedTransactionEnvelope.feeBump = FeeBumpTransactionEnvelope.decode(stream); + decodedTransactionEnvelope.feeBump = FeeBumpTransactionEnvelope.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionEnvelope; } + public static TransactionEnvelope decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionEnvelope fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -77,6 +88,7 @@ public static TransactionEnvelope fromXdrBase64(String xdr) throws IOException { public static TransactionEnvelope fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionEvent.java b/src/main/java/org/stellar/sdk/xdr/TransactionEvent.java index af8ea0e5d..f65eb40f4 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionEvent.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionEvent.java @@ -34,13 +34,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { event.encode(stream); } - public static TransactionEvent decode(XdrDataInputStream stream) throws IOException { + public static TransactionEvent decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionEvent decodedTransactionEvent = new TransactionEvent(); - decodedTransactionEvent.stage = TransactionEventStage.decode(stream); - decodedTransactionEvent.event = ContractEvent.decode(stream); + decodedTransactionEvent.stage = TransactionEventStage.decode(stream, maxDepth); + decodedTransactionEvent.event = ContractEvent.decode(stream, maxDepth); return decodedTransactionEvent; } + public static TransactionEvent decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionEvent fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +58,7 @@ public static TransactionEvent fromXdrBase64(String xdr) throws IOException { public static TransactionEvent fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionEventStage.java b/src/main/java/org/stellar/sdk/xdr/TransactionEventStage.java index 426bd11df..f6b8b652d 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionEventStage.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionEventStage.java @@ -39,7 +39,9 @@ public int getValue() { return value; } - public static TransactionEventStage decode(XdrDataInputStream stream) throws IOException { + public static TransactionEventStage decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -53,6 +55,10 @@ public static TransactionEventStage decode(XdrDataInputStream stream) throws IOE } } + public static TransactionEventStage decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -65,6 +71,7 @@ public static TransactionEventStage fromXdrBase64(String xdr) throws IOException public static TransactionEventStage fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionHistoryEntry.java b/src/main/java/org/stellar/sdk/xdr/TransactionHistoryEntry.java index 85aaf05b6..767e7bff7 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionHistoryEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionHistoryEntry.java @@ -47,14 +47,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TransactionHistoryEntry decode(XdrDataInputStream stream) throws IOException { + public static TransactionHistoryEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionHistoryEntry decodedTransactionHistoryEntry = new TransactionHistoryEntry(); - decodedTransactionHistoryEntry.ledgerSeq = Uint32.decode(stream); - decodedTransactionHistoryEntry.txSet = TransactionSet.decode(stream); - decodedTransactionHistoryEntry.ext = TransactionHistoryEntryExt.decode(stream); + decodedTransactionHistoryEntry.ledgerSeq = Uint32.decode(stream, maxDepth); + decodedTransactionHistoryEntry.txSet = TransactionSet.decode(stream, maxDepth); + decodedTransactionHistoryEntry.ext = TransactionHistoryEntryExt.decode(stream, maxDepth); return decodedTransactionHistoryEntry; } + public static TransactionHistoryEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionHistoryEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -63,6 +72,7 @@ public static TransactionHistoryEntry fromXdrBase64(String xdr) throws IOExcepti public static TransactionHistoryEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -98,7 +108,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionHistoryEntryExt decode(XdrDataInputStream stream) throws IOException { + public static TransactionHistoryEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionHistoryEntryExt decodedTransactionHistoryEntryExt = new TransactionHistoryEntryExt(); Integer discriminant = stream.readInt(); @@ -108,12 +123,18 @@ public static TransactionHistoryEntryExt decode(XdrDataInputStream stream) throw break; case 1: decodedTransactionHistoryEntryExt.generalizedTxSet = - GeneralizedTransactionSet.decode(stream); + GeneralizedTransactionSet.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionHistoryEntryExt; } + public static TransactionHistoryEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionHistoryEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -122,6 +143,7 @@ public static TransactionHistoryEntryExt fromXdrBase64(String xdr) throws IOExce public static TransactionHistoryEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionHistoryResultEntry.java b/src/main/java/org/stellar/sdk/xdr/TransactionHistoryResultEntry.java index b194ea0f8..8ef229274 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionHistoryResultEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionHistoryResultEntry.java @@ -45,15 +45,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TransactionHistoryResultEntry decode(XdrDataInputStream stream) throws IOException { + public static TransactionHistoryResultEntry decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionHistoryResultEntry decodedTransactionHistoryResultEntry = new TransactionHistoryResultEntry(); - decodedTransactionHistoryResultEntry.ledgerSeq = Uint32.decode(stream); - decodedTransactionHistoryResultEntry.txResultSet = TransactionResultSet.decode(stream); - decodedTransactionHistoryResultEntry.ext = TransactionHistoryResultEntryExt.decode(stream); + decodedTransactionHistoryResultEntry.ledgerSeq = Uint32.decode(stream, maxDepth); + decodedTransactionHistoryResultEntry.txResultSet = + TransactionResultSet.decode(stream, maxDepth); + decodedTransactionHistoryResultEntry.ext = + TransactionHistoryResultEntryExt.decode(stream, maxDepth); return decodedTransactionHistoryResultEntry; } + public static TransactionHistoryResultEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionHistoryResultEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -62,6 +73,7 @@ public static TransactionHistoryResultEntry fromXdrBase64(String xdr) throws IOE public static TransactionHistoryResultEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -91,8 +103,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionHistoryResultEntryExt decode(XdrDataInputStream stream) + public static TransactionHistoryResultEntryExt decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionHistoryResultEntryExt decodedTransactionHistoryResultEntryExt = new TransactionHistoryResultEntryExt(); Integer discriminant = stream.readInt(); @@ -100,10 +116,17 @@ public static TransactionHistoryResultEntryExt decode(XdrDataInputStream stream) switch (decodedTransactionHistoryResultEntryExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionHistoryResultEntryExt; } + public static TransactionHistoryResultEntryExt decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionHistoryResultEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -112,6 +135,7 @@ public static TransactionHistoryResultEntryExt fromXdrBase64(String xdr) throws public static TransactionHistoryResultEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionMeta.java b/src/main/java/org/stellar/sdk/xdr/TransactionMeta.java index 004106394..9361f5598 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionMeta.java @@ -67,34 +67,55 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionMeta decode(XdrDataInputStream stream) throws IOException { + public static TransactionMeta decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionMeta decodedTransactionMeta = new TransactionMeta(); Integer discriminant = stream.readInt(); decodedTransactionMeta.setDiscriminant(discriminant); switch (decodedTransactionMeta.getDiscriminant()) { case 0: int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransactionMeta.operations = new OperationMeta[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransactionMeta.operations[i] = OperationMeta.decode(stream); + decodedTransactionMeta.operations[i] = OperationMeta.decode(stream, maxDepth); } break; case 1: - decodedTransactionMeta.v1 = TransactionMetaV1.decode(stream); + decodedTransactionMeta.v1 = TransactionMetaV1.decode(stream, maxDepth); break; case 2: - decodedTransactionMeta.v2 = TransactionMetaV2.decode(stream); + decodedTransactionMeta.v2 = TransactionMetaV2.decode(stream, maxDepth); break; case 3: - decodedTransactionMeta.v3 = TransactionMetaV3.decode(stream); + decodedTransactionMeta.v3 = TransactionMetaV3.decode(stream, maxDepth); break; case 4: - decodedTransactionMeta.v4 = TransactionMetaV4.decode(stream); + decodedTransactionMeta.v4 = TransactionMetaV4.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionMeta; } + public static TransactionMeta decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionMeta fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -103,6 +124,7 @@ public static TransactionMeta fromXdrBase64(String xdr) throws IOException { public static TransactionMeta fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV1.java b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV1.java index b0d0a9ad1..4652d7429 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV1.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV1.java @@ -39,17 +39,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionMetaV1 decode(XdrDataInputStream stream) throws IOException { + public static TransactionMetaV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionMetaV1 decodedTransactionMetaV1 = new TransactionMetaV1(); - decodedTransactionMetaV1.txChanges = LedgerEntryChanges.decode(stream); + decodedTransactionMetaV1.txChanges = LedgerEntryChanges.decode(stream, maxDepth); int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransactionMetaV1.operations = new OperationMeta[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransactionMetaV1.operations[i] = OperationMeta.decode(stream); + decodedTransactionMetaV1.operations[i] = OperationMeta.decode(stream, maxDepth); } return decodedTransactionMetaV1; } + public static TransactionMetaV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionMetaV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +78,7 @@ public static TransactionMetaV1 fromXdrBase64(String xdr) throws IOException { public static TransactionMetaV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV2.java b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV2.java index 64f8de366..5627542ea 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV2.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV2.java @@ -44,18 +44,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { txChangesAfter.encode(stream); } - public static TransactionMetaV2 decode(XdrDataInputStream stream) throws IOException { + public static TransactionMetaV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionMetaV2 decodedTransactionMetaV2 = new TransactionMetaV2(); - decodedTransactionMetaV2.txChangesBefore = LedgerEntryChanges.decode(stream); + decodedTransactionMetaV2.txChangesBefore = LedgerEntryChanges.decode(stream, maxDepth); int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransactionMetaV2.operations = new OperationMeta[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransactionMetaV2.operations[i] = OperationMeta.decode(stream); + decodedTransactionMetaV2.operations[i] = OperationMeta.decode(stream, maxDepth); } - decodedTransactionMetaV2.txChangesAfter = LedgerEntryChanges.decode(stream); + decodedTransactionMetaV2.txChangesAfter = LedgerEntryChanges.decode(stream, maxDepth); return decodedTransactionMetaV2; } + public static TransactionMetaV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionMetaV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -64,6 +84,7 @@ public static TransactionMetaV2 fromXdrBase64(String xdr) throws IOException { public static TransactionMetaV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV3.java b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV3.java index 022eed89b..fac40c46f 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV3.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV3.java @@ -57,23 +57,43 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionMetaV3 decode(XdrDataInputStream stream) throws IOException { + public static TransactionMetaV3 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionMetaV3 decodedTransactionMetaV3 = new TransactionMetaV3(); - decodedTransactionMetaV3.ext = ExtensionPoint.decode(stream); - decodedTransactionMetaV3.txChangesBefore = LedgerEntryChanges.decode(stream); + decodedTransactionMetaV3.ext = ExtensionPoint.decode(stream, maxDepth); + decodedTransactionMetaV3.txChangesBefore = LedgerEntryChanges.decode(stream, maxDepth); int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransactionMetaV3.operations = new OperationMeta[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransactionMetaV3.operations[i] = OperationMeta.decode(stream); + decodedTransactionMetaV3.operations[i] = OperationMeta.decode(stream, maxDepth); } - decodedTransactionMetaV3.txChangesAfter = LedgerEntryChanges.decode(stream); - int sorobanMetaPresent = stream.readInt(); - if (sorobanMetaPresent != 0) { - decodedTransactionMetaV3.sorobanMeta = SorobanTransactionMeta.decode(stream); + decodedTransactionMetaV3.txChangesAfter = LedgerEntryChanges.decode(stream, maxDepth); + boolean sorobanMetaPresent = stream.readXdrBoolean(); + if (sorobanMetaPresent) { + decodedTransactionMetaV3.sorobanMeta = SorobanTransactionMeta.decode(stream, maxDepth); } return decodedTransactionMetaV3; } + public static TransactionMetaV3 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionMetaV3 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -82,6 +102,7 @@ public static TransactionMetaV3 fromXdrBase64(String xdr) throws IOException { public static TransactionMetaV3 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV4.java b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV4.java index 1a93bc8a7..16d023ad3 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionMetaV4.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionMetaV4.java @@ -72,33 +72,76 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionMetaV4 decode(XdrDataInputStream stream) throws IOException { + public static TransactionMetaV4 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionMetaV4 decodedTransactionMetaV4 = new TransactionMetaV4(); - decodedTransactionMetaV4.ext = ExtensionPoint.decode(stream); - decodedTransactionMetaV4.txChangesBefore = LedgerEntryChanges.decode(stream); + decodedTransactionMetaV4.ext = ExtensionPoint.decode(stream, maxDepth); + decodedTransactionMetaV4.txChangesBefore = LedgerEntryChanges.decode(stream, maxDepth); int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransactionMetaV4.operations = new OperationMetaV2[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransactionMetaV4.operations[i] = OperationMetaV2.decode(stream); + decodedTransactionMetaV4.operations[i] = OperationMetaV2.decode(stream, maxDepth); } - decodedTransactionMetaV4.txChangesAfter = LedgerEntryChanges.decode(stream); - int sorobanMetaPresent = stream.readInt(); - if (sorobanMetaPresent != 0) { - decodedTransactionMetaV4.sorobanMeta = SorobanTransactionMetaV2.decode(stream); + decodedTransactionMetaV4.txChangesAfter = LedgerEntryChanges.decode(stream, maxDepth); + boolean sorobanMetaPresent = stream.readXdrBoolean(); + if (sorobanMetaPresent) { + decodedTransactionMetaV4.sorobanMeta = SorobanTransactionMetaV2.decode(stream, maxDepth); } int eventsSize = stream.readInt(); + if (eventsSize < 0) { + throw new IOException("events size " + eventsSize + " is negative"); + } + int eventsRemainingInputLen = stream.getRemainingInputLen(); + if (eventsRemainingInputLen >= 0 && eventsRemainingInputLen < eventsSize) { + throw new IOException( + "events size " + + eventsSize + + " exceeds remaining input length " + + eventsRemainingInputLen); + } decodedTransactionMetaV4.events = new TransactionEvent[eventsSize]; for (int i = 0; i < eventsSize; i++) { - decodedTransactionMetaV4.events[i] = TransactionEvent.decode(stream); + decodedTransactionMetaV4.events[i] = TransactionEvent.decode(stream, maxDepth); } int diagnosticEventsSize = stream.readInt(); + if (diagnosticEventsSize < 0) { + throw new IOException("diagnosticEvents size " + diagnosticEventsSize + " is negative"); + } + int diagnosticEventsRemainingInputLen = stream.getRemainingInputLen(); + if (diagnosticEventsRemainingInputLen >= 0 + && diagnosticEventsRemainingInputLen < diagnosticEventsSize) { + throw new IOException( + "diagnosticEvents size " + + diagnosticEventsSize + + " exceeds remaining input length " + + diagnosticEventsRemainingInputLen); + } decodedTransactionMetaV4.diagnosticEvents = new DiagnosticEvent[diagnosticEventsSize]; for (int i = 0; i < diagnosticEventsSize; i++) { - decodedTransactionMetaV4.diagnosticEvents[i] = DiagnosticEvent.decode(stream); + decodedTransactionMetaV4.diagnosticEvents[i] = DiagnosticEvent.decode(stream, maxDepth); } return decodedTransactionMetaV4; } + public static TransactionMetaV4 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionMetaV4 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -107,6 +150,7 @@ public static TransactionMetaV4 fromXdrBase64(String xdr) throws IOException { public static TransactionMetaV4 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionPhase.java b/src/main/java/org/stellar/sdk/xdr/TransactionPhase.java index 77cfc4a9a..60ee8883c 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionPhase.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionPhase.java @@ -49,25 +49,49 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionPhase decode(XdrDataInputStream stream) throws IOException { + public static TransactionPhase decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionPhase decodedTransactionPhase = new TransactionPhase(); Integer discriminant = stream.readInt(); decodedTransactionPhase.setDiscriminant(discriminant); switch (decodedTransactionPhase.getDiscriminant()) { case 0: int v0ComponentsSize = stream.readInt(); + if (v0ComponentsSize < 0) { + throw new IOException("v0Components size " + v0ComponentsSize + " is negative"); + } + int v0ComponentsRemainingInputLen = stream.getRemainingInputLen(); + if (v0ComponentsRemainingInputLen >= 0 + && v0ComponentsRemainingInputLen < v0ComponentsSize) { + throw new IOException( + "v0Components size " + + v0ComponentsSize + + " exceeds remaining input length " + + v0ComponentsRemainingInputLen); + } decodedTransactionPhase.v0Components = new TxSetComponent[v0ComponentsSize]; for (int i = 0; i < v0ComponentsSize; i++) { - decodedTransactionPhase.v0Components[i] = TxSetComponent.decode(stream); + decodedTransactionPhase.v0Components[i] = TxSetComponent.decode(stream, maxDepth); } break; case 1: - decodedTransactionPhase.parallelTxsComponent = ParallelTxsComponent.decode(stream); + decodedTransactionPhase.parallelTxsComponent = + ParallelTxsComponent.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionPhase; } + public static TransactionPhase decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionPhase fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -76,6 +100,7 @@ public static TransactionPhase fromXdrBase64(String xdr) throws IOException { public static TransactionPhase fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResult.java b/src/main/java/org/stellar/sdk/xdr/TransactionResult.java index 8c0eeb5c5..5fa3a4ef8 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResult.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResult.java @@ -72,14 +72,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TransactionResult decode(XdrDataInputStream stream) throws IOException { + public static TransactionResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResult decodedTransactionResult = new TransactionResult(); - decodedTransactionResult.feeCharged = Int64.decode(stream); - decodedTransactionResult.result = TransactionResultResult.decode(stream); - decodedTransactionResult.ext = TransactionResultExt.decode(stream); + decodedTransactionResult.feeCharged = Int64.decode(stream, maxDepth); + decodedTransactionResult.result = TransactionResultResult.decode(stream, maxDepth); + decodedTransactionResult.ext = TransactionResultExt.decode(stream, maxDepth); return decodedTransactionResult; } + public static TransactionResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -88,6 +97,7 @@ public static TransactionResult fromXdrBase64(String xdr) throws IOException { public static TransactionResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -166,22 +176,38 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionResultResult decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultResult decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResultResult decodedTransactionResultResult = new TransactionResultResult(); - TransactionResultCode discriminant = TransactionResultCode.decode(stream); + TransactionResultCode discriminant = TransactionResultCode.decode(stream, maxDepth); decodedTransactionResultResult.setDiscriminant(discriminant); switch (decodedTransactionResultResult.getDiscriminant()) { case txFEE_BUMP_INNER_SUCCESS: case txFEE_BUMP_INNER_FAILED: decodedTransactionResultResult.innerResultPair = - InnerTransactionResultPair.decode(stream); + InnerTransactionResultPair.decode(stream, maxDepth); break; case txSUCCESS: case txFAILED: int resultsSize = stream.readInt(); + if (resultsSize < 0) { + throw new IOException("results size " + resultsSize + " is negative"); + } + int resultsRemainingInputLen = stream.getRemainingInputLen(); + if (resultsRemainingInputLen >= 0 && resultsRemainingInputLen < resultsSize) { + throw new IOException( + "results size " + + resultsSize + + " exceeds remaining input length " + + resultsRemainingInputLen); + } decodedTransactionResultResult.results = new OperationResult[resultsSize]; for (int i = 0; i < resultsSize; i++) { - decodedTransactionResultResult.results[i] = OperationResult.decode(stream); + decodedTransactionResultResult.results[i] = OperationResult.decode(stream, maxDepth); } break; case txTOO_EARLY: @@ -200,10 +226,16 @@ public static TransactionResultResult decode(XdrDataInputStream stream) throws I case txMALFORMED: case txSOROBAN_INVALID: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionResultResult; } + public static TransactionResultResult decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResultResult fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -212,6 +244,7 @@ public static TransactionResultResult fromXdrBase64(String xdr) throws IOExcepti public static TransactionResultResult fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } @@ -242,17 +275,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionResultExt decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResultExt decodedTransactionResultExt = new TransactionResultExt(); Integer discriminant = stream.readInt(); decodedTransactionResultExt.setDiscriminant(discriminant); switch (decodedTransactionResultExt.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionResultExt; } + public static TransactionResultExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResultExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -261,6 +305,7 @@ public static TransactionResultExt fromXdrBase64(String xdr) throws IOException public static TransactionResultExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java index b30115725..8bb3239ed 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultCode.java @@ -70,7 +70,9 @@ public int getValue() { return value; } - public static TransactionResultCode decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultCode decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -116,6 +118,10 @@ public static TransactionResultCode decode(XdrDataInputStream stream) throws IOE } } + public static TransactionResultCode decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -128,6 +134,7 @@ public static TransactionResultCode fromXdrBase64(String xdr) throws IOException public static TransactionResultCode fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultMeta.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultMeta.java index bce7477a4..b55621500 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultMeta.java @@ -38,14 +38,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { txApplyProcessing.encode(stream); } - public static TransactionResultMeta decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultMeta decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResultMeta decodedTransactionResultMeta = new TransactionResultMeta(); - decodedTransactionResultMeta.result = TransactionResultPair.decode(stream); - decodedTransactionResultMeta.feeProcessing = LedgerEntryChanges.decode(stream); - decodedTransactionResultMeta.txApplyProcessing = TransactionMeta.decode(stream); + decodedTransactionResultMeta.result = TransactionResultPair.decode(stream, maxDepth); + decodedTransactionResultMeta.feeProcessing = LedgerEntryChanges.decode(stream, maxDepth); + decodedTransactionResultMeta.txApplyProcessing = TransactionMeta.decode(stream, maxDepth); return decodedTransactionResultMeta; } + public static TransactionResultMeta decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResultMeta fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +63,7 @@ public static TransactionResultMeta fromXdrBase64(String xdr) throws IOException public static TransactionResultMeta fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultMetaV1.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultMetaV1.java index 00b29daee..b49089bc5 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultMetaV1.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultMetaV1.java @@ -46,16 +46,26 @@ public void encode(XdrDataOutputStream stream) throws IOException { postTxApplyFeeProcessing.encode(stream); } - public static TransactionResultMetaV1 decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultMetaV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResultMetaV1 decodedTransactionResultMetaV1 = new TransactionResultMetaV1(); - decodedTransactionResultMetaV1.ext = ExtensionPoint.decode(stream); - decodedTransactionResultMetaV1.result = TransactionResultPair.decode(stream); - decodedTransactionResultMetaV1.feeProcessing = LedgerEntryChanges.decode(stream); - decodedTransactionResultMetaV1.txApplyProcessing = TransactionMeta.decode(stream); - decodedTransactionResultMetaV1.postTxApplyFeeProcessing = LedgerEntryChanges.decode(stream); + decodedTransactionResultMetaV1.ext = ExtensionPoint.decode(stream, maxDepth); + decodedTransactionResultMetaV1.result = TransactionResultPair.decode(stream, maxDepth); + decodedTransactionResultMetaV1.feeProcessing = LedgerEntryChanges.decode(stream, maxDepth); + decodedTransactionResultMetaV1.txApplyProcessing = TransactionMeta.decode(stream, maxDepth); + decodedTransactionResultMetaV1.postTxApplyFeeProcessing = + LedgerEntryChanges.decode(stream, maxDepth); return decodedTransactionResultMetaV1; } + public static TransactionResultMetaV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResultMetaV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -64,6 +74,7 @@ public static TransactionResultMetaV1 fromXdrBase64(String xdr) throws IOExcepti public static TransactionResultMetaV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultPair.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultPair.java index 5d85c7139..149cb74d4 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultPair.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultPair.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { result.encode(stream); } - public static TransactionResultPair decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultPair decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResultPair decodedTransactionResultPair = new TransactionResultPair(); - decodedTransactionResultPair.transactionHash = Hash.decode(stream); - decodedTransactionResultPair.result = TransactionResult.decode(stream); + decodedTransactionResultPair.transactionHash = Hash.decode(stream, maxDepth); + decodedTransactionResultPair.result = TransactionResult.decode(stream, maxDepth); return decodedTransactionResultPair; } + public static TransactionResultPair decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResultPair fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static TransactionResultPair fromXdrBase64(String xdr) throws IOException public static TransactionResultPair fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionResultSet.java b/src/main/java/org/stellar/sdk/xdr/TransactionResultSet.java index b81c7de24..000cf03cb 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionResultSet.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionResultSet.java @@ -36,16 +36,36 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionResultSet decode(XdrDataInputStream stream) throws IOException { + public static TransactionResultSet decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionResultSet decodedTransactionResultSet = new TransactionResultSet(); int resultsSize = stream.readInt(); + if (resultsSize < 0) { + throw new IOException("results size " + resultsSize + " is negative"); + } + int resultsRemainingInputLen = stream.getRemainingInputLen(); + if (resultsRemainingInputLen >= 0 && resultsRemainingInputLen < resultsSize) { + throw new IOException( + "results size " + + resultsSize + + " exceeds remaining input length " + + resultsRemainingInputLen); + } decodedTransactionResultSet.results = new TransactionResultPair[resultsSize]; for (int i = 0; i < resultsSize; i++) { - decodedTransactionResultSet.results[i] = TransactionResultPair.decode(stream); + decodedTransactionResultSet.results[i] = TransactionResultPair.decode(stream, maxDepth); } return decodedTransactionResultSet; } + public static TransactionResultSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionResultSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -54,6 +74,7 @@ public static TransactionResultSet fromXdrBase64(String xdr) throws IOException public static TransactionResultSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionSet.java b/src/main/java/org/stellar/sdk/xdr/TransactionSet.java index 06d84587c..d875526c1 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionSet.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionSet.java @@ -39,17 +39,33 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionSet decode(XdrDataInputStream stream) throws IOException { + public static TransactionSet decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionSet decodedTransactionSet = new TransactionSet(); - decodedTransactionSet.previousLedgerHash = Hash.decode(stream); + decodedTransactionSet.previousLedgerHash = Hash.decode(stream, maxDepth); int txsSize = stream.readInt(); + if (txsSize < 0) { + throw new IOException("txs size " + txsSize + " is negative"); + } + int txsRemainingInputLen = stream.getRemainingInputLen(); + if (txsRemainingInputLen >= 0 && txsRemainingInputLen < txsSize) { + throw new IOException( + "txs size " + txsSize + " exceeds remaining input length " + txsRemainingInputLen); + } decodedTransactionSet.txs = new TransactionEnvelope[txsSize]; for (int i = 0; i < txsSize; i++) { - decodedTransactionSet.txs[i] = TransactionEnvelope.decode(stream); + decodedTransactionSet.txs[i] = TransactionEnvelope.decode(stream, maxDepth); } return decodedTransactionSet; } + public static TransactionSet decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionSet fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +74,7 @@ public static TransactionSet fromXdrBase64(String xdr) throws IOException { public static TransactionSet fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionSetV1.java b/src/main/java/org/stellar/sdk/xdr/TransactionSetV1.java index 490827820..163c7cccb 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionSetV1.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionSetV1.java @@ -39,17 +39,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionSetV1 decode(XdrDataInputStream stream) throws IOException { + public static TransactionSetV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionSetV1 decodedTransactionSetV1 = new TransactionSetV1(); - decodedTransactionSetV1.previousLedgerHash = Hash.decode(stream); + decodedTransactionSetV1.previousLedgerHash = Hash.decode(stream, maxDepth); int phasesSize = stream.readInt(); + if (phasesSize < 0) { + throw new IOException("phases size " + phasesSize + " is negative"); + } + int phasesRemainingInputLen = stream.getRemainingInputLen(); + if (phasesRemainingInputLen >= 0 && phasesRemainingInputLen < phasesSize) { + throw new IOException( + "phases size " + + phasesSize + + " exceeds remaining input length " + + phasesRemainingInputLen); + } decodedTransactionSetV1.phases = new TransactionPhase[phasesSize]; for (int i = 0; i < phasesSize; i++) { - decodedTransactionSetV1.phases[i] = TransactionPhase.decode(stream); + decodedTransactionSetV1.phases[i] = TransactionPhase.decode(stream, maxDepth); } return decodedTransactionSetV1; } + public static TransactionSetV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionSetV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -58,6 +78,7 @@ public static TransactionSetV1 fromXdrBase64(String xdr) throws IOException { public static TransactionSetV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java b/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java index 95c4f9aa8..df5a43df9 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionSignaturePayload.java @@ -43,15 +43,24 @@ public void encode(XdrDataOutputStream stream) throws IOException { taggedTransaction.encode(stream); } - public static TransactionSignaturePayload decode(XdrDataInputStream stream) throws IOException { + public static TransactionSignaturePayload decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionSignaturePayload decodedTransactionSignaturePayload = new TransactionSignaturePayload(); - decodedTransactionSignaturePayload.networkId = Hash.decode(stream); + decodedTransactionSignaturePayload.networkId = Hash.decode(stream, maxDepth); decodedTransactionSignaturePayload.taggedTransaction = - TransactionSignaturePayloadTaggedTransaction.decode(stream); + TransactionSignaturePayloadTaggedTransaction.decode(stream, maxDepth); return decodedTransactionSignaturePayload; } + public static TransactionSignaturePayload decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionSignaturePayload fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +69,7 @@ public static TransactionSignaturePayload fromXdrBase64(String xdr) throws IOExc public static TransactionSignaturePayload fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -98,25 +108,37 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionSignaturePayloadTaggedTransaction decode(XdrDataInputStream stream) - throws IOException { + public static TransactionSignaturePayloadTaggedTransaction decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionSignaturePayloadTaggedTransaction decodedTransactionSignaturePayloadTaggedTransaction = new TransactionSignaturePayloadTaggedTransaction(); - EnvelopeType discriminant = EnvelopeType.decode(stream); + EnvelopeType discriminant = EnvelopeType.decode(stream, maxDepth); decodedTransactionSignaturePayloadTaggedTransaction.setDiscriminant(discriminant); switch (decodedTransactionSignaturePayloadTaggedTransaction.getDiscriminant()) { case ENVELOPE_TYPE_TX: - decodedTransactionSignaturePayloadTaggedTransaction.tx = Transaction.decode(stream); + decodedTransactionSignaturePayloadTaggedTransaction.tx = + Transaction.decode(stream, maxDepth); break; case ENVELOPE_TYPE_TX_FEE_BUMP: decodedTransactionSignaturePayloadTaggedTransaction.feeBump = - FeeBumpTransaction.decode(stream); + FeeBumpTransaction.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionSignaturePayloadTaggedTransaction; } + public static TransactionSignaturePayloadTaggedTransaction decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionSignaturePayloadTaggedTransaction fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); @@ -127,6 +149,7 @@ public static TransactionSignaturePayloadTaggedTransaction fromXdrByteArray(byte throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV0.java b/src/main/java/org/stellar/sdk/xdr/TransactionV0.java index ebac82602..80b374678 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionV0.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV0.java @@ -57,6 +57,9 @@ public void encode(XdrDataOutputStream stream) throws IOException { } memo.encode(stream); int operationsSize = getOperations().length; + if (operationsSize > 100) { + throw new IOException("operations size " + operationsSize + " exceeds max size 100"); + } stream.writeInt(operationsSize); for (int i = 0; i < operationsSize; i++) { operations[i].encode(stream); @@ -64,25 +67,47 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TransactionV0 decode(XdrDataInputStream stream) throws IOException { + public static TransactionV0 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionV0 decodedTransactionV0 = new TransactionV0(); - decodedTransactionV0.sourceAccountEd25519 = Uint256.decode(stream); - decodedTransactionV0.fee = Uint32.decode(stream); - decodedTransactionV0.seqNum = SequenceNumber.decode(stream); - int timeBoundsPresent = stream.readInt(); - if (timeBoundsPresent != 0) { - decodedTransactionV0.timeBounds = TimeBounds.decode(stream); + decodedTransactionV0.sourceAccountEd25519 = Uint256.decode(stream, maxDepth); + decodedTransactionV0.fee = Uint32.decode(stream, maxDepth); + decodedTransactionV0.seqNum = SequenceNumber.decode(stream, maxDepth); + boolean timeBoundsPresent = stream.readXdrBoolean(); + if (timeBoundsPresent) { + decodedTransactionV0.timeBounds = TimeBounds.decode(stream, maxDepth); } - decodedTransactionV0.memo = Memo.decode(stream); + decodedTransactionV0.memo = Memo.decode(stream, maxDepth); int operationsSize = stream.readInt(); + if (operationsSize < 0) { + throw new IOException("operations size " + operationsSize + " is negative"); + } + if (operationsSize > 100) { + throw new IOException("operations size " + operationsSize + " exceeds max size 100"); + } + int operationsRemainingInputLen = stream.getRemainingInputLen(); + if (operationsRemainingInputLen >= 0 && operationsRemainingInputLen < operationsSize) { + throw new IOException( + "operations size " + + operationsSize + + " exceeds remaining input length " + + operationsRemainingInputLen); + } decodedTransactionV0.operations = new Operation[operationsSize]; for (int i = 0; i < operationsSize; i++) { - decodedTransactionV0.operations[i] = Operation.decode(stream); + decodedTransactionV0.operations[i] = Operation.decode(stream, maxDepth); } - decodedTransactionV0.ext = TransactionV0Ext.decode(stream); + decodedTransactionV0.ext = TransactionV0Ext.decode(stream, maxDepth); return decodedTransactionV0; } + public static TransactionV0 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionV0 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -91,6 +116,7 @@ public static TransactionV0 fromXdrBase64(String xdr) throws IOException { public static TransactionV0 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -120,17 +146,28 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TransactionV0Ext decode(XdrDataInputStream stream) throws IOException { + public static TransactionV0Ext decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionV0Ext decodedTransactionV0Ext = new TransactionV0Ext(); Integer discriminant = stream.readInt(); decodedTransactionV0Ext.setDiscriminant(discriminant); switch (decodedTransactionV0Ext.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTransactionV0Ext; } + public static TransactionV0Ext decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionV0Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -139,6 +176,7 @@ public static TransactionV0Ext fromXdrBase64(String xdr) throws IOException { public static TransactionV0Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java index 8ad943904..5fdb8f172 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV0Envelope.java @@ -35,23 +35,49 @@ public class TransactionV0Envelope implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { tx.encode(stream); int signaturesSize = getSignatures().length; + if (signaturesSize > 20) { + throw new IOException("signatures size " + signaturesSize + " exceeds max size 20"); + } stream.writeInt(signaturesSize); for (int i = 0; i < signaturesSize; i++) { signatures[i].encode(stream); } } - public static TransactionV0Envelope decode(XdrDataInputStream stream) throws IOException { + public static TransactionV0Envelope decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionV0Envelope decodedTransactionV0Envelope = new TransactionV0Envelope(); - decodedTransactionV0Envelope.tx = TransactionV0.decode(stream); + decodedTransactionV0Envelope.tx = TransactionV0.decode(stream, maxDepth); int signaturesSize = stream.readInt(); + if (signaturesSize < 0) { + throw new IOException("signatures size " + signaturesSize + " is negative"); + } + if (signaturesSize > 20) { + throw new IOException("signatures size " + signaturesSize + " exceeds max size 20"); + } + int signaturesRemainingInputLen = stream.getRemainingInputLen(); + if (signaturesRemainingInputLen >= 0 && signaturesRemainingInputLen < signaturesSize) { + throw new IOException( + "signatures size " + + signaturesSize + + " exceeds remaining input length " + + signaturesRemainingInputLen); + } decodedTransactionV0Envelope.signatures = new DecoratedSignature[signaturesSize]; for (int i = 0; i < signaturesSize; i++) { - decodedTransactionV0Envelope.signatures[i] = DecoratedSignature.decode(stream); + decodedTransactionV0Envelope.signatures[i] = DecoratedSignature.decode(stream, maxDepth); } return decodedTransactionV0Envelope; } + public static TransactionV0Envelope decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionV0Envelope fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +86,7 @@ public static TransactionV0Envelope fromXdrBase64(String xdr) throws IOException public static TransactionV0Envelope fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java b/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java index 9d7fdc763..7f68cd960 100644 --- a/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java +++ b/src/main/java/org/stellar/sdk/xdr/TransactionV1Envelope.java @@ -35,23 +35,49 @@ public class TransactionV1Envelope implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { tx.encode(stream); int signaturesSize = getSignatures().length; + if (signaturesSize > 20) { + throw new IOException("signatures size " + signaturesSize + " exceeds max size 20"); + } stream.writeInt(signaturesSize); for (int i = 0; i < signaturesSize; i++) { signatures[i].encode(stream); } } - public static TransactionV1Envelope decode(XdrDataInputStream stream) throws IOException { + public static TransactionV1Envelope decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TransactionV1Envelope decodedTransactionV1Envelope = new TransactionV1Envelope(); - decodedTransactionV1Envelope.tx = Transaction.decode(stream); + decodedTransactionV1Envelope.tx = Transaction.decode(stream, maxDepth); int signaturesSize = stream.readInt(); + if (signaturesSize < 0) { + throw new IOException("signatures size " + signaturesSize + " is negative"); + } + if (signaturesSize > 20) { + throw new IOException("signatures size " + signaturesSize + " exceeds max size 20"); + } + int signaturesRemainingInputLen = stream.getRemainingInputLen(); + if (signaturesRemainingInputLen >= 0 && signaturesRemainingInputLen < signaturesSize) { + throw new IOException( + "signatures size " + + signaturesSize + + " exceeds remaining input length " + + signaturesRemainingInputLen); + } decodedTransactionV1Envelope.signatures = new DecoratedSignature[signaturesSize]; for (int i = 0; i < signaturesSize; i++) { - decodedTransactionV1Envelope.signatures[i] = DecoratedSignature.decode(stream); + decodedTransactionV1Envelope.signatures[i] = DecoratedSignature.decode(stream, maxDepth); } return decodedTransactionV1Envelope; } + public static TransactionV1Envelope decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TransactionV1Envelope fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -60,6 +86,7 @@ public static TransactionV1Envelope fromXdrBase64(String xdr) throws IOException public static TransactionV1Envelope fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TrustLineAsset.java b/src/main/java/org/stellar/sdk/xdr/TrustLineAsset.java index 846a3e2e4..c62086758 100644 --- a/src/main/java/org/stellar/sdk/xdr/TrustLineAsset.java +++ b/src/main/java/org/stellar/sdk/xdr/TrustLineAsset.java @@ -60,26 +60,36 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TrustLineAsset decode(XdrDataInputStream stream) throws IOException { + public static TrustLineAsset decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineAsset decodedTrustLineAsset = new TrustLineAsset(); - AssetType discriminant = AssetType.decode(stream); + AssetType discriminant = AssetType.decode(stream, maxDepth); decodedTrustLineAsset.setDiscriminant(discriminant); switch (decodedTrustLineAsset.getDiscriminant()) { case ASSET_TYPE_NATIVE: break; case ASSET_TYPE_CREDIT_ALPHANUM4: - decodedTrustLineAsset.alphaNum4 = AlphaNum4.decode(stream); + decodedTrustLineAsset.alphaNum4 = AlphaNum4.decode(stream, maxDepth); break; case ASSET_TYPE_CREDIT_ALPHANUM12: - decodedTrustLineAsset.alphaNum12 = AlphaNum12.decode(stream); + decodedTrustLineAsset.alphaNum12 = AlphaNum12.decode(stream, maxDepth); break; case ASSET_TYPE_POOL_SHARE: - decodedTrustLineAsset.liquidityPoolID = PoolID.decode(stream); + decodedTrustLineAsset.liquidityPoolID = PoolID.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTrustLineAsset; } + public static TrustLineAsset decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineAsset fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -88,6 +98,7 @@ public static TrustLineAsset fromXdrBase64(String xdr) throws IOException { public static TrustLineAsset fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TrustLineEntry.java b/src/main/java/org/stellar/sdk/xdr/TrustLineEntry.java index 4f8366ac3..0b81d17cf 100644 --- a/src/main/java/org/stellar/sdk/xdr/TrustLineEntry.java +++ b/src/main/java/org/stellar/sdk/xdr/TrustLineEntry.java @@ -70,17 +70,25 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TrustLineEntry decode(XdrDataInputStream stream) throws IOException { + public static TrustLineEntry decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineEntry decodedTrustLineEntry = new TrustLineEntry(); - decodedTrustLineEntry.accountID = AccountID.decode(stream); - decodedTrustLineEntry.asset = TrustLineAsset.decode(stream); - decodedTrustLineEntry.balance = Int64.decode(stream); - decodedTrustLineEntry.limit = Int64.decode(stream); - decodedTrustLineEntry.flags = Uint32.decode(stream); - decodedTrustLineEntry.ext = TrustLineEntryExt.decode(stream); + decodedTrustLineEntry.accountID = AccountID.decode(stream, maxDepth); + decodedTrustLineEntry.asset = TrustLineAsset.decode(stream, maxDepth); + decodedTrustLineEntry.balance = Int64.decode(stream, maxDepth); + decodedTrustLineEntry.limit = Int64.decode(stream, maxDepth); + decodedTrustLineEntry.flags = Uint32.decode(stream, maxDepth); + decodedTrustLineEntry.ext = TrustLineEntryExt.decode(stream, maxDepth); return decodedTrustLineEntry; } + public static TrustLineEntry decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineEntry fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -89,6 +97,7 @@ public static TrustLineEntry fromXdrBase64(String xdr) throws IOException { public static TrustLineEntry fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -136,7 +145,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TrustLineEntryExt decode(XdrDataInputStream stream) throws IOException { + public static TrustLineEntryExt decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineEntryExt decodedTrustLineEntryExt = new TrustLineEntryExt(); Integer discriminant = stream.readInt(); decodedTrustLineEntryExt.setDiscriminant(discriminant); @@ -144,12 +158,18 @@ public static TrustLineEntryExt decode(XdrDataInputStream stream) throws IOExcep case 0: break; case 1: - decodedTrustLineEntryExt.v1 = TrustLineEntryV1.decode(stream); + decodedTrustLineEntryExt.v1 = TrustLineEntryV1.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTrustLineEntryExt; } + public static TrustLineEntryExt decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineEntryExt fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -158,6 +178,7 @@ public static TrustLineEntryExt fromXdrBase64(String xdr) throws IOException { public static TrustLineEntryExt fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -193,13 +214,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TrustLineEntryV1 decode(XdrDataInputStream stream) throws IOException { + public static TrustLineEntryV1 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineEntryV1 decodedTrustLineEntryV1 = new TrustLineEntryV1(); - decodedTrustLineEntryV1.liabilities = Liabilities.decode(stream); - decodedTrustLineEntryV1.ext = TrustLineEntryV1Ext.decode(stream); + decodedTrustLineEntryV1.liabilities = Liabilities.decode(stream, maxDepth); + decodedTrustLineEntryV1.ext = TrustLineEntryV1Ext.decode(stream, maxDepth); return decodedTrustLineEntryV1; } + public static TrustLineEntryV1 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineEntryV1 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -208,6 +238,7 @@ public static TrustLineEntryV1 fromXdrBase64(String xdr) throws IOException { public static TrustLineEntryV1 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -243,7 +274,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TrustLineEntryV1Ext decode(XdrDataInputStream stream) throws IOException { + public static TrustLineEntryV1Ext decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineEntryV1Ext decodedTrustLineEntryV1Ext = new TrustLineEntryV1Ext(); Integer discriminant = stream.readInt(); decodedTrustLineEntryV1Ext.setDiscriminant(discriminant); @@ -251,12 +287,18 @@ public static TrustLineEntryV1Ext decode(XdrDataInputStream stream) throws IOExc case 0: break; case 2: - decodedTrustLineEntryV1Ext.v2 = TrustLineEntryExtensionV2.decode(stream); + decodedTrustLineEntryV1Ext.v2 = TrustLineEntryExtensionV2.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTrustLineEntryV1Ext; } + public static TrustLineEntryV1Ext decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineEntryV1Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -265,6 +307,7 @@ public static TrustLineEntryV1Ext fromXdrBase64(String xdr) throws IOException { public static TrustLineEntryV1Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TrustLineEntryExtensionV2.java b/src/main/java/org/stellar/sdk/xdr/TrustLineEntryExtensionV2.java index 691da0dc1..f7e3e90e3 100644 --- a/src/main/java/org/stellar/sdk/xdr/TrustLineEntryExtensionV2.java +++ b/src/main/java/org/stellar/sdk/xdr/TrustLineEntryExtensionV2.java @@ -41,13 +41,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { ext.encode(stream); } - public static TrustLineEntryExtensionV2 decode(XdrDataInputStream stream) throws IOException { + public static TrustLineEntryExtensionV2 decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineEntryExtensionV2 decodedTrustLineEntryExtensionV2 = new TrustLineEntryExtensionV2(); - decodedTrustLineEntryExtensionV2.liquidityPoolUseCount = Int32.decode(stream); - decodedTrustLineEntryExtensionV2.ext = TrustLineEntryExtensionV2Ext.decode(stream); + decodedTrustLineEntryExtensionV2.liquidityPoolUseCount = Int32.decode(stream, maxDepth); + decodedTrustLineEntryExtensionV2.ext = TrustLineEntryExtensionV2Ext.decode(stream, maxDepth); return decodedTrustLineEntryExtensionV2; } + public static TrustLineEntryExtensionV2 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineEntryExtensionV2 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -56,6 +65,7 @@ public static TrustLineEntryExtensionV2 fromXdrBase64(String xdr) throws IOExcep public static TrustLineEntryExtensionV2 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -85,8 +95,12 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TrustLineEntryExtensionV2Ext decode(XdrDataInputStream stream) + public static TrustLineEntryExtensionV2Ext decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TrustLineEntryExtensionV2Ext decodedTrustLineEntryExtensionV2Ext = new TrustLineEntryExtensionV2Ext(); Integer discriminant = stream.readInt(); @@ -94,10 +108,17 @@ public static TrustLineEntryExtensionV2Ext decode(XdrDataInputStream stream) switch (decodedTrustLineEntryExtensionV2Ext.getDiscriminant()) { case 0: break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTrustLineEntryExtensionV2Ext; } + public static TrustLineEntryExtensionV2Ext decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TrustLineEntryExtensionV2Ext fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -106,6 +127,7 @@ public static TrustLineEntryExtensionV2Ext fromXdrBase64(String xdr) throws IOEx public static TrustLineEntryExtensionV2Ext fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java b/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java index 8d3a61364..4747808ea 100644 --- a/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java +++ b/src/main/java/org/stellar/sdk/xdr/TrustLineFlags.java @@ -39,7 +39,8 @@ public int getValue() { return value; } - public static TrustLineFlags decode(XdrDataInputStream stream) throws IOException { + public static TrustLineFlags decode(XdrDataInputStream stream, int maxDepth) throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 1: @@ -53,6 +54,10 @@ public static TrustLineFlags decode(XdrDataInputStream stream) throws IOExceptio } } + public static TrustLineFlags decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -65,6 +70,7 @@ public static TrustLineFlags fromXdrBase64(String xdr) throws IOException { public static TrustLineFlags fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TxAdvertVector.java b/src/main/java/org/stellar/sdk/xdr/TxAdvertVector.java index c974ab632..0b612223f 100644 --- a/src/main/java/org/stellar/sdk/xdr/TxAdvertVector.java +++ b/src/main/java/org/stellar/sdk/xdr/TxAdvertVector.java @@ -25,22 +25,48 @@ public class TxAdvertVector implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int TxAdvertVectorSize = getTxAdvertVector().length; + if (TxAdvertVectorSize > 1000) { + throw new IOException("TxAdvertVector size " + TxAdvertVectorSize + " exceeds max size 1000"); + } stream.writeInt(TxAdvertVectorSize); for (int i = 0; i < TxAdvertVectorSize; i++) { TxAdvertVector[i].encode(stream); } } - public static TxAdvertVector decode(XdrDataInputStream stream) throws IOException { + public static TxAdvertVector decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TxAdvertVector decodedTxAdvertVector = new TxAdvertVector(); int TxAdvertVectorSize = stream.readInt(); + if (TxAdvertVectorSize < 0) { + throw new IOException("TxAdvertVector size " + TxAdvertVectorSize + " is negative"); + } + if (TxAdvertVectorSize > 1000) { + throw new IOException("TxAdvertVector size " + TxAdvertVectorSize + " exceeds max size 1000"); + } + int TxAdvertVectorRemainingInputLen = stream.getRemainingInputLen(); + if (TxAdvertVectorRemainingInputLen >= 0 + && TxAdvertVectorRemainingInputLen < TxAdvertVectorSize) { + throw new IOException( + "TxAdvertVector size " + + TxAdvertVectorSize + + " exceeds remaining input length " + + TxAdvertVectorRemainingInputLen); + } decodedTxAdvertVector.TxAdvertVector = new Hash[TxAdvertVectorSize]; for (int i = 0; i < TxAdvertVectorSize; i++) { - decodedTxAdvertVector.TxAdvertVector[i] = Hash.decode(stream); + decodedTxAdvertVector.TxAdvertVector[i] = Hash.decode(stream, maxDepth); } return decodedTxAdvertVector; } + public static TxAdvertVector decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TxAdvertVector fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +75,7 @@ public static TxAdvertVector fromXdrBase64(String xdr) throws IOException { public static TxAdvertVector fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TxDemandVector.java b/src/main/java/org/stellar/sdk/xdr/TxDemandVector.java index 0dec57ad9..677fa0b4e 100644 --- a/src/main/java/org/stellar/sdk/xdr/TxDemandVector.java +++ b/src/main/java/org/stellar/sdk/xdr/TxDemandVector.java @@ -25,22 +25,48 @@ public class TxDemandVector implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int TxDemandVectorSize = getTxDemandVector().length; + if (TxDemandVectorSize > 1000) { + throw new IOException("TxDemandVector size " + TxDemandVectorSize + " exceeds max size 1000"); + } stream.writeInt(TxDemandVectorSize); for (int i = 0; i < TxDemandVectorSize; i++) { TxDemandVector[i].encode(stream); } } - public static TxDemandVector decode(XdrDataInputStream stream) throws IOException { + public static TxDemandVector decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TxDemandVector decodedTxDemandVector = new TxDemandVector(); int TxDemandVectorSize = stream.readInt(); + if (TxDemandVectorSize < 0) { + throw new IOException("TxDemandVector size " + TxDemandVectorSize + " is negative"); + } + if (TxDemandVectorSize > 1000) { + throw new IOException("TxDemandVector size " + TxDemandVectorSize + " exceeds max size 1000"); + } + int TxDemandVectorRemainingInputLen = stream.getRemainingInputLen(); + if (TxDemandVectorRemainingInputLen >= 0 + && TxDemandVectorRemainingInputLen < TxDemandVectorSize) { + throw new IOException( + "TxDemandVector size " + + TxDemandVectorSize + + " exceeds remaining input length " + + TxDemandVectorRemainingInputLen); + } decodedTxDemandVector.TxDemandVector = new Hash[TxDemandVectorSize]; for (int i = 0; i < TxDemandVectorSize; i++) { - decodedTxDemandVector.TxDemandVector[i] = Hash.decode(stream); + decodedTxDemandVector.TxDemandVector[i] = Hash.decode(stream, maxDepth); } return decodedTxDemandVector; } + public static TxDemandVector decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TxDemandVector fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +75,7 @@ public static TxDemandVector fromXdrBase64(String xdr) throws IOException { public static TxDemandVector fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TxSetComponent.java b/src/main/java/org/stellar/sdk/xdr/TxSetComponent.java index 39eab3672..9d8c925cf 100644 --- a/src/main/java/org/stellar/sdk/xdr/TxSetComponent.java +++ b/src/main/java/org/stellar/sdk/xdr/TxSetComponent.java @@ -43,19 +43,29 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TxSetComponent decode(XdrDataInputStream stream) throws IOException { + public static TxSetComponent decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TxSetComponent decodedTxSetComponent = new TxSetComponent(); - TxSetComponentType discriminant = TxSetComponentType.decode(stream); + TxSetComponentType discriminant = TxSetComponentType.decode(stream, maxDepth); decodedTxSetComponent.setDiscriminant(discriminant); switch (decodedTxSetComponent.getDiscriminant()) { case TXSET_COMP_TXS_MAYBE_DISCOUNTED_FEE: decodedTxSetComponent.txsMaybeDiscountedFee = - TxSetComponentTxsMaybeDiscountedFee.decode(stream); + TxSetComponentTxsMaybeDiscountedFee.decode(stream, maxDepth); break; + default: + throw new IOException("Unknown discriminant value: " + discriminant); } return decodedTxSetComponent; } + public static TxSetComponent decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TxSetComponent fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -64,6 +74,7 @@ public static TxSetComponent fromXdrBase64(String xdr) throws IOException { public static TxSetComponent fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } @@ -100,22 +111,40 @@ public void encode(XdrDataOutputStream stream) throws IOException { } } - public static TxSetComponentTxsMaybeDiscountedFee decode(XdrDataInputStream stream) - throws IOException { + public static TxSetComponentTxsMaybeDiscountedFee decode( + XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; TxSetComponentTxsMaybeDiscountedFee decodedTxSetComponentTxsMaybeDiscountedFee = new TxSetComponentTxsMaybeDiscountedFee(); - int baseFeePresent = stream.readInt(); - if (baseFeePresent != 0) { - decodedTxSetComponentTxsMaybeDiscountedFee.baseFee = Int64.decode(stream); + boolean baseFeePresent = stream.readXdrBoolean(); + if (baseFeePresent) { + decodedTxSetComponentTxsMaybeDiscountedFee.baseFee = Int64.decode(stream, maxDepth); } int txsSize = stream.readInt(); + if (txsSize < 0) { + throw new IOException("txs size " + txsSize + " is negative"); + } + int txsRemainingInputLen = stream.getRemainingInputLen(); + if (txsRemainingInputLen >= 0 && txsRemainingInputLen < txsSize) { + throw new IOException( + "txs size " + txsSize + " exceeds remaining input length " + txsRemainingInputLen); + } decodedTxSetComponentTxsMaybeDiscountedFee.txs = new TransactionEnvelope[txsSize]; for (int i = 0; i < txsSize; i++) { - decodedTxSetComponentTxsMaybeDiscountedFee.txs[i] = TransactionEnvelope.decode(stream); + decodedTxSetComponentTxsMaybeDiscountedFee.txs[i] = + TransactionEnvelope.decode(stream, maxDepth); } return decodedTxSetComponentTxsMaybeDiscountedFee; } + public static TxSetComponentTxsMaybeDiscountedFee decode(XdrDataInputStream stream) + throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static TxSetComponentTxsMaybeDiscountedFee fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -125,6 +154,7 @@ public static TxSetComponentTxsMaybeDiscountedFee fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/TxSetComponentType.java b/src/main/java/org/stellar/sdk/xdr/TxSetComponentType.java index 0b20772f1..5d7cae203 100644 --- a/src/main/java/org/stellar/sdk/xdr/TxSetComponentType.java +++ b/src/main/java/org/stellar/sdk/xdr/TxSetComponentType.java @@ -32,7 +32,9 @@ public int getValue() { return value; } - public static TxSetComponentType decode(XdrDataInputStream stream) throws IOException { + public static TxSetComponentType decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - enums are leaf types with no recursive decoding int value = stream.readInt(); switch (value) { case 0: @@ -42,6 +44,10 @@ public static TxSetComponentType decode(XdrDataInputStream stream) throws IOExce } } + public static TxSetComponentType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(value); } @@ -54,6 +60,7 @@ public static TxSetComponentType fromXdrBase64(String xdr) throws IOException { public static TxSetComponentType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/UInt128Parts.java b/src/main/java/org/stellar/sdk/xdr/UInt128Parts.java index 1e8e277c0..c267bbd31 100644 --- a/src/main/java/org/stellar/sdk/xdr/UInt128Parts.java +++ b/src/main/java/org/stellar/sdk/xdr/UInt128Parts.java @@ -34,13 +34,21 @@ public void encode(XdrDataOutputStream stream) throws IOException { lo.encode(stream); } - public static UInt128Parts decode(XdrDataInputStream stream) throws IOException { + public static UInt128Parts decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; UInt128Parts decodedUInt128Parts = new UInt128Parts(); - decodedUInt128Parts.hi = Uint64.decode(stream); - decodedUInt128Parts.lo = Uint64.decode(stream); + decodedUInt128Parts.hi = Uint64.decode(stream, maxDepth); + decodedUInt128Parts.lo = Uint64.decode(stream, maxDepth); return decodedUInt128Parts; } + public static UInt128Parts decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static UInt128Parts fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -49,6 +57,7 @@ public static UInt128Parts fromXdrBase64(String xdr) throws IOException { public static UInt128Parts fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/UInt256Parts.java b/src/main/java/org/stellar/sdk/xdr/UInt256Parts.java index c7e7e42b3..70b7513c2 100644 --- a/src/main/java/org/stellar/sdk/xdr/UInt256Parts.java +++ b/src/main/java/org/stellar/sdk/xdr/UInt256Parts.java @@ -40,15 +40,23 @@ public void encode(XdrDataOutputStream stream) throws IOException { lo_lo.encode(stream); } - public static UInt256Parts decode(XdrDataInputStream stream) throws IOException { + public static UInt256Parts decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; UInt256Parts decodedUInt256Parts = new UInt256Parts(); - decodedUInt256Parts.hi_hi = Uint64.decode(stream); - decodedUInt256Parts.hi_lo = Uint64.decode(stream); - decodedUInt256Parts.lo_hi = Uint64.decode(stream); - decodedUInt256Parts.lo_lo = Uint64.decode(stream); + decodedUInt256Parts.hi_hi = Uint64.decode(stream, maxDepth); + decodedUInt256Parts.hi_lo = Uint64.decode(stream, maxDepth); + decodedUInt256Parts.lo_hi = Uint64.decode(stream, maxDepth); + decodedUInt256Parts.lo_lo = Uint64.decode(stream, maxDepth); return decodedUInt256Parts; } + public static UInt256Parts decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static UInt256Parts fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -57,6 +65,7 @@ public static UInt256Parts fromXdrBase64(String xdr) throws IOException { public static UInt256Parts fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Uint256.java b/src/main/java/org/stellar/sdk/xdr/Uint256.java index 13348bd7f..2fbcc09a7 100644 --- a/src/main/java/org/stellar/sdk/xdr/Uint256.java +++ b/src/main/java/org/stellar/sdk/xdr/Uint256.java @@ -25,17 +25,28 @@ public class Uint256 implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int uint256Size = uint256.length; + if (uint256Size != 32) { + throw new IOException("uint256 size " + uint256Size + " does not match fixed size 32"); + } stream.write(getUint256(), 0, uint256Size); } - public static Uint256 decode(XdrDataInputStream stream) throws IOException { + public static Uint256 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Uint256 decodedUint256 = new Uint256(); int uint256Size = 32; decodedUint256.uint256 = new byte[uint256Size]; - stream.read(decodedUint256.uint256, 0, uint256Size); + stream.readPaddedData(decodedUint256.uint256, 0, uint256Size); return decodedUint256; } + public static Uint256 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Uint256 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -44,6 +55,7 @@ public static Uint256 fromXdrBase64(String xdr) throws IOException { public static Uint256 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Uint32.java b/src/main/java/org/stellar/sdk/xdr/Uint32.java index 52a4a8bb5..2bd8fe32c 100644 --- a/src/main/java/org/stellar/sdk/xdr/Uint32.java +++ b/src/main/java/org/stellar/sdk/xdr/Uint32.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { uint32.encode(stream); } - public static Uint32 decode(XdrDataInputStream stream) throws IOException { + public static Uint32 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Uint32 decodedUint32 = new Uint32(); - decodedUint32.uint32 = XdrUnsignedInteger.decode(stream); + decodedUint32.uint32 = XdrUnsignedInteger.decode(stream, maxDepth); return decodedUint32; } + public static Uint32 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Uint32 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static Uint32 fromXdrBase64(String xdr) throws IOException { public static Uint32 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Uint64.java b/src/main/java/org/stellar/sdk/xdr/Uint64.java index 5eb751397..647861ef9 100644 --- a/src/main/java/org/stellar/sdk/xdr/Uint64.java +++ b/src/main/java/org/stellar/sdk/xdr/Uint64.java @@ -27,12 +27,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { uint64.encode(stream); } - public static Uint64 decode(XdrDataInputStream stream) throws IOException { + public static Uint64 decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Uint64 decodedUint64 = new Uint64(); - decodedUint64.uint64 = XdrUnsignedHyperInteger.decode(stream); + decodedUint64.uint64 = XdrUnsignedHyperInteger.decode(stream, maxDepth); return decodedUint64; } + public static Uint64 decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Uint64 fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -41,6 +49,7 @@ public static Uint64 fromXdrBase64(String xdr) throws IOException { public static Uint64 fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/UpgradeEntryMeta.java b/src/main/java/org/stellar/sdk/xdr/UpgradeEntryMeta.java index 21a68d979..a16657c3a 100644 --- a/src/main/java/org/stellar/sdk/xdr/UpgradeEntryMeta.java +++ b/src/main/java/org/stellar/sdk/xdr/UpgradeEntryMeta.java @@ -35,13 +35,22 @@ public void encode(XdrDataOutputStream stream) throws IOException { changes.encode(stream); } - public static UpgradeEntryMeta decode(XdrDataInputStream stream) throws IOException { + public static UpgradeEntryMeta decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; UpgradeEntryMeta decodedUpgradeEntryMeta = new UpgradeEntryMeta(); - decodedUpgradeEntryMeta.upgrade = LedgerUpgrade.decode(stream); - decodedUpgradeEntryMeta.changes = LedgerEntryChanges.decode(stream); + decodedUpgradeEntryMeta.upgrade = LedgerUpgrade.decode(stream, maxDepth); + decodedUpgradeEntryMeta.changes = LedgerEntryChanges.decode(stream, maxDepth); return decodedUpgradeEntryMeta; } + public static UpgradeEntryMeta decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static UpgradeEntryMeta fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -50,6 +59,7 @@ public static UpgradeEntryMeta fromXdrBase64(String xdr) throws IOException { public static UpgradeEntryMeta fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/UpgradeType.java b/src/main/java/org/stellar/sdk/xdr/UpgradeType.java index ec9e30b2e..77490b3ed 100644 --- a/src/main/java/org/stellar/sdk/xdr/UpgradeType.java +++ b/src/main/java/org/stellar/sdk/xdr/UpgradeType.java @@ -25,18 +25,43 @@ public class UpgradeType implements XdrElement { public void encode(XdrDataOutputStream stream) throws IOException { int UpgradeTypeSize = UpgradeType.length; + if (UpgradeTypeSize > 128) { + throw new IOException("UpgradeType size " + UpgradeTypeSize + " exceeds max size 128"); + } stream.writeInt(UpgradeTypeSize); stream.write(getUpgradeType(), 0, UpgradeTypeSize); } - public static UpgradeType decode(XdrDataInputStream stream) throws IOException { + public static UpgradeType decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; UpgradeType decodedUpgradeType = new UpgradeType(); int UpgradeTypeSize = stream.readInt(); + if (UpgradeTypeSize < 0) { + throw new IOException("UpgradeType size " + UpgradeTypeSize + " is negative"); + } + if (UpgradeTypeSize > 128) { + throw new IOException("UpgradeType size " + UpgradeTypeSize + " exceeds max size 128"); + } + int UpgradeTypeRemainingInputLen = stream.getRemainingInputLen(); + if (UpgradeTypeRemainingInputLen >= 0 && UpgradeTypeRemainingInputLen < UpgradeTypeSize) { + throw new IOException( + "UpgradeType size " + + UpgradeTypeSize + + " exceeds remaining input length " + + UpgradeTypeRemainingInputLen); + } decodedUpgradeType.UpgradeType = new byte[UpgradeTypeSize]; - stream.read(decodedUpgradeType.UpgradeType, 0, UpgradeTypeSize); + stream.readPaddedData(decodedUpgradeType.UpgradeType, 0, UpgradeTypeSize); return decodedUpgradeType; } + public static UpgradeType decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static UpgradeType fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +70,7 @@ public static UpgradeType fromXdrBase64(String xdr) throws IOException { public static UpgradeType fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/Value.java b/src/main/java/org/stellar/sdk/xdr/Value.java index f384fde07..eb9d699bf 100644 --- a/src/main/java/org/stellar/sdk/xdr/Value.java +++ b/src/main/java/org/stellar/sdk/xdr/Value.java @@ -29,14 +29,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(getValue(), 0, ValueSize); } - public static Value decode(XdrDataInputStream stream) throws IOException { + public static Value decode(XdrDataInputStream stream, int maxDepth) throws IOException { + if (maxDepth <= 0) { + throw new IOException("Maximum decoding depth reached"); + } + maxDepth -= 1; Value decodedValue = new Value(); int ValueSize = stream.readInt(); + if (ValueSize < 0) { + throw new IOException("Value size " + ValueSize + " is negative"); + } + int ValueRemainingInputLen = stream.getRemainingInputLen(); + if (ValueRemainingInputLen >= 0 && ValueRemainingInputLen < ValueSize) { + throw new IOException( + "Value size " + ValueSize + " exceeds remaining input length " + ValueRemainingInputLen); + } decodedValue.Value = new byte[ValueSize]; - stream.read(decodedValue.Value, 0, ValueSize); + stream.readPaddedData(decodedValue.Value, 0, ValueSize); return decodedValue; } + public static Value decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + public static Value fromXdrBase64(String xdr) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes); @@ -45,6 +61,7 @@ public static Value fromXdrBase64(String xdr) throws IOException { public static Value fromXdrByteArray(byte[] xdr) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream); } } diff --git a/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java b/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java index dea04430d..4a2b773b3 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrDataInputStream.java @@ -1,14 +1,26 @@ package org.stellar.sdk.xdr; import java.io.DataInputStream; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import lombok.Setter; public class XdrDataInputStream extends DataInputStream { + /** Default maximum decoding depth to prevent stack overflow from deeply nested structures. */ + public static final int DEFAULT_MAX_DEPTH = 200; + // The underlying input stream private final XdrInputStream mIn; + /** + * Maximum input length, -1 if unknown. This is used to validate that the declared size of + * variable-length arrays/opaques doesn't exceed the remaining input length, preventing DoS + * attacks. + */ + @Setter private int maxInputLen = -1; + /** * Creates a XdrDataInputStream that uses the specified underlying InputStream. * @@ -19,6 +31,43 @@ public XdrDataInputStream(InputStream in) { mIn = (XdrInputStream) super.in; } + /** + * Returns the remaining input length if known, -1 otherwise. This can be used to validate sizes + * before allocating memory. + * + * @return remaining input length, or -1 if unknown + */ + public int getRemainingInputLen() { + if (maxInputLen < 0) { + return -1; + } + return maxInputLen - mIn.getCount(); + } + + /** + * Reads an XDR boolean value from the stream. Per RFC 4506, a boolean is encoded as an integer + * that must be 0 (FALSE) or 1 (TRUE). + * + * @return the boolean value + * @throws IOException if the value is not 0 or 1, or if an I/O error occurs + */ + public boolean readXdrBoolean() throws IOException { + int value = readInt(); + if (value == 0) { + return false; + } else if (value == 1) { + return true; + } else { + throw new IOException("Invalid boolean value: " + value + ", must be 0 or 1 per RFC 4506"); + } + } + + /** + * @deprecated This method does not validate the array length and may cause OutOfMemoryError or + * NegativeArraySizeException with untrusted input. Use generated XDR type decoders instead + * which include proper validation. + */ + @Deprecated public int[] readIntArray() throws IOException { int l = readInt(); return readIntArray(l); @@ -32,6 +81,12 @@ private int[] readIntArray(int l) throws IOException { return arr; } + /** + * @deprecated This method does not validate the array length and may cause OutOfMemoryError or + * NegativeArraySizeException with untrusted input. Use generated XDR type decoders instead + * which include proper validation. + */ + @Deprecated public float[] readFloatArray() throws IOException { int l = readInt(); return readFloatArray(l); @@ -45,6 +100,12 @@ private float[] readFloatArray(int l) throws IOException { return arr; } + /** + * @deprecated This method does not validate the array length and may cause OutOfMemoryError or + * NegativeArraySizeException with untrusted input. Use generated XDR type decoders instead + * which include proper validation. + */ + @Deprecated public double[] readDoubleArray() throws IOException { int l = readInt(); return readDoubleArray(l); @@ -63,6 +124,22 @@ public int read() throws IOException { return super.read(); } + /** + * Reads exactly len bytes of XDR opaque/string data, handling short reads, then reads and + * validates padding bytes to maintain 4-byte alignment. This method must be used instead of + * read(byte[], int, int) for opaque data to correctly handle short reads from the underlying + * stream. + * + * @param b the buffer into which the data is read + * @param off the start offset in array b at which the data is written + * @param len the number of bytes to read + * @throws IOException if an I/O error occurs or EOF is reached before reading len bytes + */ + public void readPaddedData(byte[] b, int off, int len) throws IOException { + mIn.readFullyNoPad(b, off, len); + mIn.pad(); + } + /** * Need to provide a custom impl of InputStream as DataInputStream's read methods are final and we * need to keep track of the count for padding purposes. @@ -80,6 +157,10 @@ public XdrInputStream(InputStream in) { mCount = 0; } + public int getCount() { + return mCount; + } + @Override public int read() throws IOException { int read = mIn.read(); @@ -99,7 +180,11 @@ public int read(byte[] b, int off, int len) throws IOException { int read = mIn.read(b, off, len); if (read > 0) { mCount += read; - pad(); + // Note: padding is NOT automatically applied here. + // For opaque/string data, use XdrDataInputStream.readPaddedData() which + // handles short reads correctly and applies padding after all data is read. + // Primitive types (int, long, float, double) are naturally 4/8-byte aligned + // and don't need padding between reads. } return read; } @@ -118,5 +203,21 @@ public void pad() throws IOException { } } } + + /** + * Reads exactly len bytes into the buffer, handling short reads. Does not apply padding - + * caller must call pad() after this. + */ + void readFullyNoPad(byte[] b, int off, int len) throws IOException { + int totalRead = 0; + while (totalRead < len) { + int read = mIn.read(b, off + totalRead, len - totalRead); + if (read < 0) { + throw new EOFException("Unexpected end of stream while reading XDR data"); + } + mCount += read; + totalRead += read; + } + } } } diff --git a/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java b/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java index 815c0169d..7c56d9767 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrDataOutputStream.java @@ -77,10 +77,8 @@ public void write(byte[] b) throws IOException { public void write(byte[] b, int offset, int length) throws IOException { mOut.write(b, offset, length); - if (length > 0) { - mCount += length; - pad(); - } + mCount += length; + pad(); } public void pad() throws IOException { diff --git a/src/main/java/org/stellar/sdk/xdr/XdrString.java b/src/main/java/org/stellar/sdk/xdr/XdrString.java index 52c589f0e..f090108e7 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrString.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrString.java @@ -24,16 +24,30 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(this.bytes, 0, this.bytes.length); } - public static XdrString decode(XdrDataInputStream stream, int maxSize) throws IOException { + public static XdrString decode(XdrDataInputStream stream, int maxDepth, int maxSize) + throws IOException { + // maxDepth is intentionally not checked - XdrString is a leaf type with no recursive decoding int size = stream.readInt(); + if (size < 0) { + throw new IOException("String length " + size + " is negative"); + } if (size > maxSize) { - throw new IllegalArgumentException("String length " + size + " exceeds max size " + maxSize); + throw new IOException("String length " + size + " exceeds max size " + maxSize); + } + int remainingInputLen = stream.getRemainingInputLen(); + if (remainingInputLen >= 0 && remainingInputLen < size) { + throw new IOException( + "String length " + size + " exceeds remaining input length " + remainingInputLen); } byte[] bytes = new byte[size]; - stream.read(bytes); + stream.readPaddedData(bytes, 0, size); return new XdrString(bytes); } + public static XdrString decode(XdrDataInputStream stream, int maxSize) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH, maxSize); + } + public static XdrString fromXdrBase64(String xdr, int maxSize) throws IOException { byte[] bytes = Base64Factory.getInstance().decode(xdr); return fromXdrByteArray(bytes, maxSize); @@ -46,11 +60,15 @@ public static XdrString fromXdrBase64(String xdr) throws IOException { public static XdrString fromXdrByteArray(byte[] xdr, int maxSize) throws IOException { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); return decode(xdrDataInputStream, maxSize); } public static XdrString fromXdrByteArray(byte[] xdr) throws IOException { - return fromXdrByteArray(xdr, Integer.MAX_VALUE); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xdr); + XdrDataInputStream xdrDataInputStream = new XdrDataInputStream(byteArrayInputStream); + xdrDataInputStream.setMaxInputLen(xdr.length); + return decode(xdrDataInputStream, Integer.MAX_VALUE); } @Override diff --git a/src/main/java/org/stellar/sdk/xdr/XdrUnsignedHyperInteger.java b/src/main/java/org/stellar/sdk/xdr/XdrUnsignedHyperInteger.java index c5617d5f2..c1e3e04e3 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrUnsignedHyperInteger.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrUnsignedHyperInteger.java @@ -38,13 +38,20 @@ public void encode(XdrDataOutputStream stream) throws IOException { stream.write(getBytes()); } - public static XdrUnsignedHyperInteger decode(XdrDataInputStream stream) throws IOException { + public static XdrUnsignedHyperInteger decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - XdrUnsignedHyperInteger is a leaf type with no + // recursive decoding byte[] bytes = new byte[8]; stream.readFully(bytes); BigInteger uint64 = new BigInteger(1, bytes); return new XdrUnsignedHyperInteger(uint64); } + public static XdrUnsignedHyperInteger decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + private byte[] getBytes() { byte[] bytes = number.toByteArray(); byte[] paddedBytes = new byte[8]; diff --git a/src/main/java/org/stellar/sdk/xdr/XdrUnsignedInteger.java b/src/main/java/org/stellar/sdk/xdr/XdrUnsignedInteger.java index 13c8fcecf..c7c2347fa 100644 --- a/src/main/java/org/stellar/sdk/xdr/XdrUnsignedInteger.java +++ b/src/main/java/org/stellar/sdk/xdr/XdrUnsignedInteger.java @@ -32,12 +32,19 @@ public XdrUnsignedInteger(Integer number) { this.number = number.longValue(); } - public static XdrUnsignedInteger decode(XdrDataInputStream stream) throws IOException { + public static XdrUnsignedInteger decode(XdrDataInputStream stream, int maxDepth) + throws IOException { + // maxDepth is intentionally not checked - XdrUnsignedInteger is a leaf type with no recursive + // decoding int intValue = stream.readInt(); long uint32Value = Integer.toUnsignedLong(intValue); return new XdrUnsignedInteger(uint32Value); } + public static XdrUnsignedInteger decode(XdrDataInputStream stream) throws IOException { + return decode(stream, XdrDataInputStream.DEFAULT_MAX_DEPTH); + } + @Override public void encode(XdrDataOutputStream stream) throws IOException { stream.writeInt(number.intValue()); diff --git a/src/test/java/org/stellar/sdk/TransactionPreconditionsTest.java b/src/test/java/org/stellar/sdk/TransactionPreconditionsTest.java index d219a82bf..b8470b45d 100644 --- a/src/test/java/org/stellar/sdk/TransactionPreconditionsTest.java +++ b/src/test/java/org/stellar/sdk/TransactionPreconditionsTest.java @@ -113,7 +113,7 @@ public void itConvertsToV2Xdr() throws IOException { TransactionPreconditions.builder() .timeBounds(new TimeBounds(1, 2)) .minSeqNumber(3L) - .extraSigners(Arrays.asList(signerKey, signerKey, signerKey)) + .extraSigners(Arrays.asList(signerKey, signerKey)) .build(); Preconditions xdr = preconditions.toXdr(); @@ -136,7 +136,7 @@ public void itConvertsToV2Xdr() throws IOException { assertEquals(xdr.getV2().getMinSeqLedgerGap().getUint32().getNumber().intValue(), 0); // xdr encoding requires non-null for min seq age assertEquals(xdr.getV2().getMinSeqAge().getDuration().getUint64().getNumber().longValue(), 0); - assertEquals(xdr.getV2().getExtraSigners().length, 3); + assertEquals(xdr.getV2().getExtraSigners().length, 2); } @Test diff --git a/src/test/java/org/stellar/sdk/xdr/AccountEntryDecodeTest.java b/src/test/java/org/stellar/sdk/xdr/AccountEntryDecodeTest.java index 49cec2fc6..a87cf4a32 100644 --- a/src/test/java/org/stellar/sdk/xdr/AccountEntryDecodeTest.java +++ b/src/test/java/org/stellar/sdk/xdr/AccountEntryDecodeTest.java @@ -31,7 +31,7 @@ public void testDecodeSignerPayload() throws IOException { bldr.numSubEntries(new Uint32(new XdrUnsignedInteger(0))); bldr.flags(new Uint32(new XdrUnsignedInteger(0))); bldr.homeDomain(new String32(new XdrString(""))); - bldr.thresholds(new Thresholds(new byte[3])); + bldr.thresholds(new Thresholds(new byte[4])); bldr.ext( AccountEntry.AccountEntryExt.builder() .discriminant(1)