From 8f092528964a748fb8f310c24652c5c65ef0cdd4 Mon Sep 17 00:00:00 2001 From: Paul Gregoire Date: Tue, 22 Mar 2022 13:01:43 -0700 Subject: [PATCH] Updated for alias support in 1.2.16 --- pom.xml | 2 +- src/main/java/org/red5/server/api/Red5.java | 4 +- .../api/stream/IClientBroadcastStream.java | 33 ++- .../red5/server/net/rtmp/RTMPHandshake.java | 208 ++++++++++++------ .../java/org/red5/server/scope/Scope.java | 15 +- .../server/stream/ClientBroadcastStream.java | 32 ++- .../org/red5/server/stream/StreamService.java | 20 +- 7 files changed, 234 insertions(+), 80 deletions(-) diff --git a/pom.xml b/pom.xml index 93f12179..eebe9875 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.red5 red5-parent - 1.2.13 + 1.2.16 4.0.0 red5-server-common diff --git a/src/main/java/org/red5/server/api/Red5.java b/src/main/java/org/red5/server/api/Red5.java index 5de26d52..adc32245 100644 --- a/src/main/java/org/red5/server/api/Red5.java +++ b/src/main/java/org/red5/server/api/Red5.java @@ -55,12 +55,12 @@ public final class Red5 { /** * Server version with revision */ - public static final String VERSION = "Red5 Server 1.2.10"; + public static final String VERSION = "Red5 Server 1.2.15"; /** * Server version for fmsVer requests */ - public static final String FMS_VERSION = "RED5/1,2,10,0"; + public static final String FMS_VERSION = "RED5/1,2,15,0"; /** * Server capabilities diff --git a/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java b/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java index fa725e59..10c42819 100644 --- a/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java +++ b/src/main/java/org/red5/server/api/stream/IClientBroadcastStream.java @@ -49,7 +49,7 @@ public interface IClientBroadcastStream extends IClientStream, IBroadcastStream Map getParameters(); /** - * Adds a stream name alias. + * Adds a stream name subscribe-side alias. * * @param alias * @return true if added to the aliases, false otherwise @@ -57,21 +57,21 @@ public interface IClientBroadcastStream extends IClientStream, IBroadcastStream boolean addAlias(String alias); /** - * Returns whether or not an alias for this stream exists. + * Returns whether or not an subscribe-side alias for this stream exists. * * @return true if an alias has been added and false otherwise */ boolean hasAlias(); /** - * Returns an alias. + * Returns an subscribe-side alias. * * @return alias if at least one exists or null when there are none */ String getAlias(); /** - * Returns whether or not a given alias exists. + * Returns whether or not a given subscribe-side alias exists. * * @param alias * @return true if found and false otherwise @@ -79,10 +79,33 @@ public interface IClientBroadcastStream extends IClientStream, IBroadcastStream boolean containsAlias(String alias); /** - * Returns all the aliases. + * Returns all the subscribe-side aliases. * * @return all aliases for this instance or an empty set */ Set getAliases(); + /** + * Sets the publish-side alias for the streams name. Subsequent calls will replace an existing alias if already set. + * + * @param nameAlias alias to set for the streams name or null to clear it + */ + void setNameAlias(String nameAlias); + + /** + * Returns the publish-side alias, if configured. + * + * @return alias for the streams name or null if not set + */ + String getNameAlias(); + + /** + * Returns whether or not an alias is in-use / registered already; this includes both publish and subscribe aliases + * within a server instance. + * + * @param alias the name to check against publish and subscribe aliases + * @return true if registered and false otherwise + */ + boolean aliasRegistered(String alias); + } diff --git a/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java b/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java index 1b9cfc07..3d3e2348 100644 --- a/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java +++ b/src/main/java/org/red5/server/net/rtmp/RTMPHandshake.java @@ -30,6 +30,7 @@ import org.apache.commons.codec.binary.Hex; import org.apache.mina.core.buffer.IoBuffer; import org.bouncycastle.crypto.engines.BlowfishEngine; +import org.bouncycastle.crypto.engines.XTEAEngine; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.BigIntegers; @@ -39,8 +40,8 @@ import org.slf4j.LoggerFactory; /** - * Generates and validates the RTMP handshake response for Flash Players. Client versions equal to or greater than Flash 9,0,124,0 require - * a nonzero value as the fifth byte of the handshake request. + * Generates and validates the RTMP handshake response for Flash Players. Client versions equal to or greater than Flash 9,0,124,0 require a + * nonzero value as the fifth byte of the handshake request. * * @author Jacinto Shy II (jacinto.m.shy@ieee.org) * @author Steven Zimmer (stevenlzimmer@gmail.com) @@ -51,7 +52,7 @@ */ public abstract class RTMPHandshake implements IHandshake { - protected Logger log = LoggerFactory.getLogger(RTMPHandshake.class); + protected Logger log = LoggerFactory.getLogger(getClass()); public final static String[] HANDSHAKE_TYPES = { "Undefined0", "Undefined1", "Undefined2", "RTMP", "Undefined4", "Undefined5", "RTMPE", "Undefined7", "RTMPE XTEA", "RTMPE BLOWFISH" }; @@ -72,10 +73,23 @@ public abstract class RTMPHandshake implements IHandshake { (byte) 0x5c, (byte) 0xb6, (byte) 0xf4, (byte) 0x06, (byte) 0xb7, (byte) 0xed, (byte) 0xee, (byte) 0x38, (byte) 0x6b, (byte) 0xfb, (byte) 0x5a, (byte) 0x89, (byte) 0x9f, (byte) 0xa5, (byte) 0xae, (byte) 0x9f, (byte) 0x24, (byte) 0x11, (byte) 0x7c, (byte) 0x4b, (byte) 0x1f, (byte) 0xe6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, (byte) 0xec, (byte) 0xe6, (byte) 0x53, (byte) 0x81, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; - /** XTEA keys for RTMPE (RTMP type 0x08) - 16 x 4 */ - protected static final int[][] XTEA_KEYS = { { 0xbff034b2, 0x11d9081f, 0xccdfb795, 0x748de732 }, { 0x086a5eb6, 0x1743090e, 0x6ef05ab8, 0xfe5a39e2 }, { 0x7b10956f, 0x76ce0521, 0x2388a73a, 0x440149a1 }, { 0xa943f317, 0xebf11bb2, 0xa691a5ee, 0x17f36339 }, { 0x7a30e00a, 0xb529e22c, 0xa087aea5, 0xc0cb79ac }, { 0xbdce0c23, 0x2febdeff, 0x1cfaae16, 0x1123239d }, { 0x55dd3f7b, 0x77e7e62e, 0x9bb8c499, 0xc9481ee4 }, - { 0x407bb6b4, 0x71e89136, 0xa7aebf55, 0xca33b839 }, { 0xfcf6bdc3, 0xb63c3697, 0x7ce4f825, 0x04d959b2 }, { 0x28e091fd, 0x41954c4c, 0x7fb7db00, 0xe3a066f8 }, { 0x57845b76, 0x4f251b03, 0x46d45bcd, 0xa2c30d29 }, { 0x0acceef8, 0xda55b546, 0x03473452, 0x5863713b }, { 0xb82075dc, 0xa75f1fee, 0xd84268e8, 0xa72a44cc }, { 0x07cf6e9e, 0xa16d7b25, 0x9fa7ae6c, 0xd92f5629 }, - { 0xfeb1eae4, 0x8c8c3ce1, 0x4e0064a7, 0x6a387c2a }, { 0x893a9427, 0xcc3013a2, 0xf106385b, 0xa829f927 } }; + /** XTEA keys for RTMPE (RTMP type 0x08) - 16 x 16 (little endian) */ + protected static final byte[][] XTEA_KEYS = { { (byte) 0xb2, (byte) 0x34, (byte) 0xf0, (byte) 0xbf, (byte) 0x1f, (byte) 0x08, (byte) 0xd9, (byte) 0x11, (byte) 0x95, (byte) 0xb7, (byte) 0xdf, (byte) 0xcc, (byte) 0x32, (byte) 0xe7, (byte) 0x8d, (byte) 0x74 }, + { (byte) 0xb6, (byte) 0x5e, (byte) 0x6a, (byte) 0x08, (byte) 0x0e, (byte) 0x09, (byte) 0x43, (byte) 0x17, (byte) 0xb8, (byte) 0x5a, (byte) 0xf0, (byte) 0x6e, (byte) 0xe2, (byte) 0x39, (byte) 0x5a, (byte) 0xfe }, + { (byte) 0x6f, (byte) 0x95, (byte) 0x10, (byte) 0x7b, (byte) 0x21, (byte) 0x05, (byte) 0xce, (byte) 0x76, (byte) 0x3a, (byte) 0xa7, (byte) 0x88, (byte) 0x23, (byte) 0xa1, (byte) 0x49, (byte) 0x01, (byte) 0x44 }, + { (byte) 0x17, (byte) 0xf3, (byte) 0x43, (byte) 0xa9, (byte) 0xb2, (byte) 0x1b, (byte) 0xf1, (byte) 0xeb, (byte) 0xee, (byte) 0xa5, (byte) 0x91, (byte) 0xa6, (byte) 0x39, (byte) 0x63, (byte) 0xf3, (byte) 0x17 }, + { (byte) 0x0a, (byte) 0xe0, (byte) 0x30, (byte) 0x7a, (byte) 0x2c, (byte) 0xe2, (byte) 0x29, (byte) 0xb5, (byte) 0xa5, (byte) 0xae, (byte) 0x87, (byte) 0xa0, (byte) 0xac, (byte) 0x79, (byte) 0xcb, (byte) 0xc0 }, + { (byte) 0x23, (byte) 0x0c, (byte) 0xce, (byte) 0xbd, (byte) 0xff, (byte) 0xde, (byte) 0xeb, (byte) 0x2f, (byte) 0x16, (byte) 0xae, (byte) 0xfa, (byte) 0x1c, (byte) 0x9d, (byte) 0x23, (byte) 0x23, (byte) 0x11 }, + { (byte) 0x7b, (byte) 0x3f, (byte) 0xdd, (byte) 0x55, (byte) 0x2e, (byte) 0xe6, (byte) 0xe7, (byte) 0x77, (byte) 0x99, (byte) 0xc4, (byte) 0xb8, (byte) 0x9b, (byte) 0xe4, (byte) 0x1e, (byte) 0x48, (byte) 0xc9 }, + { (byte) 0xb4, (byte) 0xb6, (byte) 0x7b, (byte) 0x40, (byte) 0x36, (byte) 0x91, (byte) 0xe8, (byte) 0x71, (byte) 0x55, (byte) 0xbf, (byte) 0xae, (byte) 0xa7, (byte) 0x39, (byte) 0xb8, (byte) 0x33, (byte) 0xca }, + { (byte) 0xc3, (byte) 0xbd, (byte) 0xf6, (byte) 0xfc, (byte) 0x97, (byte) 0x36, (byte) 0x3c, (byte) 0xb6, (byte) 0x25, (byte) 0xf8, (byte) 0xe4, (byte) 0x7c, (byte) 0xb2, (byte) 0x59, (byte) 0xd9, (byte) 0x04 }, + { (byte) 0xfd, (byte) 0x91, (byte) 0xe0, (byte) 0x28, (byte) 0x4c, (byte) 0x4c, (byte) 0x95, (byte) 0x41, (byte) 0x00, (byte) 0xdb, (byte) 0xb7, (byte) 0x7f, (byte) 0xf8, (byte) 0x66, (byte) 0xa0, (byte) 0xe3 }, + { (byte) 0x76, (byte) 0x5b, (byte) 0x84, (byte) 0x57, (byte) 0x03, (byte) 0x1b, (byte) 0x25, (byte) 0x4f, (byte) 0xcd, (byte) 0x5b, (byte) 0xd4, (byte) 0x46, (byte) 0x29, (byte) 0x0d, (byte) 0xc3, (byte) 0xa2 }, + { (byte) 0xf8, (byte) 0xee, (byte) 0xcc, (byte) 0x0a, (byte) 0x46, (byte) 0xb5, (byte) 0x55, (byte) 0xda, (byte) 0x52, (byte) 0x34, (byte) 0x47, (byte) 0x03, (byte) 0x3b, (byte) 0x71, (byte) 0x63, (byte) 0x58 }, + { (byte) 0xdc, (byte) 0x75, (byte) 0x20, (byte) 0xb8, (byte) 0xee, (byte) 0x1f, (byte) 0x5f, (byte) 0xa7, (byte) 0xe8, (byte) 0x68, (byte) 0x42, (byte) 0xd8, (byte) 0xcc, (byte) 0x44, (byte) 0x2a, (byte) 0xa7 }, + { (byte) 0x9e, (byte) 0x6e, (byte) 0xcf, (byte) 0x07, (byte) 0x25, (byte) 0x7b, (byte) 0x6d, (byte) 0xa1, (byte) 0x6c, (byte) 0xae, (byte) 0xa7, (byte) 0x9f, (byte) 0x29, (byte) 0x56, (byte) 0x2f, (byte) 0xd9 }, + { (byte) 0xe4, (byte) 0xea, (byte) 0xb1, (byte) 0xfe, (byte) 0xe1, (byte) 0x3c, (byte) 0x8c, (byte) 0x8c, (byte) 0xa7, (byte) 0x64, (byte) 0x00, (byte) 0x4e, (byte) 0x2a, (byte) 0x7c, (byte) 0x38, (byte) 0x6a }, + { (byte) 0x27, (byte) 0x94, (byte) 0x3a, (byte) 0x89, (byte) 0xa2, (byte) 0x13, (byte) 0x30, (byte) 0xcc, (byte) 0x5b, (byte) 0x38, (byte) 0x06, (byte) 0xf1, (byte) 0x27, (byte) 0xf9, (byte) 0x29, (byte) 0xa8 } }; /** Blowfish keys for RTMPE (RTMP type 0x09) - 16 x 24 */ protected static final byte[][] BLOWFISH_KEYS = { { (byte) 0x79, (byte) 0x34, (byte) 0x77, (byte) 0x4c, (byte) 0x67, (byte) 0xd1, (byte) 0x38, (byte) 0x3a, (byte) 0xdf, (byte) 0xb3, (byte) 0x56, (byte) 0xbe, (byte) 0x8b, (byte) 0x7b, (byte) 0xd0, (byte) 0x24, (byte) 0x38, (byte) 0xe0, (byte) 0x73, (byte) 0x58, (byte) 0x41, (byte) 0x5d, (byte) 0x69, (byte) 0x67 }, @@ -111,6 +125,12 @@ public abstract class RTMPHandshake implements IHandshake { protected Cipher cipherIn; + // handles encrypt and / or decrypt using Xtea + protected XTEAEngine xtea; + + // handles encrypt and / or decrypt using Blowfish + protected BlowfishEngine blowfish; + protected byte handshakeType; protected byte[] handshakeBytes; @@ -161,7 +181,8 @@ public RTMPHandshake(byte handshakeType) { /** * Prepare the ciphers. * - * @param sharedSecret shared secret byte sequence + * @param sharedSecret + * shared secret byte sequence */ protected void initRC4Encryption(byte[] sharedSecret) { log.debug("Shared secret: {}", Hex.encodeHexString(sharedSecret)); @@ -191,6 +212,16 @@ protected void initRC4Encryption(byte[] sharedSecret) { } } + protected void initXteaEncryption(int keyId) { + xtea = new XTEAEngine(); + xtea.init(true, new KeyParameter(XTEA_KEYS[keyId])); + } + + protected void initBlowfishEncryption(int keyId) { + blowfish = new BlowfishEngine(); + blowfish.init(true, new KeyParameter(BLOWFISH_KEYS[keyId])); + } + /** * Creates a Diffie-Hellman key pair. * @@ -215,7 +246,8 @@ protected KeyPair generateKeyPair() { /** * Returns the public key for a given key pair. * - * @param keyPair key pair + * @param keyPair + * key pair * @return public key */ protected byte[] getPublicKey(KeyPair keyPair) { @@ -230,8 +262,10 @@ protected byte[] getPublicKey(KeyPair keyPair) { /** * Determines the validation scheme for given input. * - * @param publicKeyBytes public key bytes - * @param agreement key agreement + * @param publicKeyBytes + * public key bytes + * @param agreement + * key agreement * @return shared secret bytes if client used a supported validation scheme */ protected byte[] getSharedSecret(byte[] publicKeyBytes, KeyAgreement agreement) { @@ -257,7 +291,8 @@ protected byte[] getSharedSecret(byte[] publicKeyBytes, KeyAgreement agreement) /** * Determines the validation scheme for given input. * - * @param handshake handshake byte sequence + * @param handshake + * handshake byte sequence * @return true if its a supported validation scheme, false if unsupported */ public abstract boolean validate(byte[] handshake); @@ -265,13 +300,20 @@ protected byte[] getSharedSecret(byte[] publicKeyBytes, KeyAgreement agreement) /** * Calculates the digest given the its offset in the handshake data. * - * @param digestPos digest position - * @param handshakeMessage handshake message - * @param handshakeOffset handshake message offset - * @param key contains the key - * @param keyLen the length of the key - * @param digest contains the calculated digest - * @param digestOffset digest offset + * @param digestPos + * digest position + * @param handshakeMessage + * handshake message + * @param handshakeOffset + * handshake message offset + * @param key + * contains the key + * @param keyLen + * the length of the key + * @param digest + * contains the calculated digest + * @param digestOffset + * digest offset */ public void calculateDigest(int digestPos, byte[] handshakeMessage, int handshakeOffset, byte[] key, int keyLen, byte[] digest, int digestOffset) { if (log.isTraceEnabled()) { @@ -290,10 +332,14 @@ public void calculateDigest(int digestPos, byte[] handshakeMessage, int handshak /** * Verifies the digest. * - * @param digestPos digest position - * @param handshakeMessage handshake message - * @param key contains the key - * @param keyLen the length of the key + * @param digestPos + * digest position + * @param handshakeMessage + * handshake message + * @param key + * contains the key + * @param keyLen + * the length of the key * @return true if valid and false otherwise */ public boolean verifyDigest(int digestPos, byte[] handshakeMessage, byte[] key, int keyLen) { @@ -311,13 +357,20 @@ public boolean verifyDigest(int digestPos, byte[] handshakeMessage, byte[] key, /** * Calculates an HMAC SHA256 hash into the digest at the given offset. * - * @param message incoming bytes - * @param messageOffset message offset - * @param messageLen message length - * @param key incoming key bytes - * @param keyLen the length of the key - * @param digest contains the calculated digest - * @param digestOffset digest offset + * @param message + * incoming bytes + * @param messageOffset + * message offset + * @param messageLen + * message length + * @param key + * incoming key bytes + * @param keyLen + * the length of the key + * @param digest + * contains the calculated digest + * @param digestOffset + * digest offset */ public void calculateHMAC_SHA256(byte[] message, int messageOffset, int messageLen, byte[] key, int keyLen, byte[] digest, int digestOffset) { if (log.isTraceEnabled()) { @@ -346,9 +399,12 @@ public void calculateHMAC_SHA256(byte[] message, int messageOffset, int messageL /** * Calculates the swf verification token. * - * @param handshakeMessage servers handshake bytes - * @param swfHash hash of swf - * @param swfSize size of swf + * @param handshakeMessage + * servers handshake bytes + * @param swfHash + * hash of swf + * @param swfSize + * size of swf */ public void calculateSwfVerification(byte[] handshakeMessage, byte[] swfHash, int swfSize) { // SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake @@ -372,9 +428,12 @@ public void calculateSwfVerification(byte[] handshakeMessage, byte[] swfHash, in /** * Returns the DH offset from an array of bytes. * - * @param algorithm validation algorithm - * @param handshake handshake sequence - * @param bufferOffset buffer offset + * @param algorithm + * validation algorithm + * @param handshake + * handshake sequence + * @param bufferOffset + * buffer offset * @return DH offset */ public int getDHOffset(int algorithm, byte[] handshake, int bufferOffset) { @@ -390,8 +449,10 @@ public int getDHOffset(int algorithm, byte[] handshake, int bufferOffset) { /** * Returns the DH byte offset. * - * @param handshake handshake sequence - * @param bufferOffset buffer offset + * @param handshake + * handshake sequence + * @param bufferOffset + * buffer offset * @return dh offset */ protected int getDHOffset1(byte[] handshake, int bufferOffset) { @@ -413,8 +474,10 @@ protected int getDHOffset1(byte[] handshake, int bufferOffset) { /** * Returns the DH byte offset. * - * @param handshake handshake sequence - * @param bufferOffset buffer offset + * @param handshake + * handshake sequence + * @param bufferOffset + * buffer offset * @return dh offset */ protected int getDHOffset2(byte[] handshake, int bufferOffset) { @@ -436,9 +499,12 @@ protected int getDHOffset2(byte[] handshake, int bufferOffset) { /** * Returns the digest offset using current validation scheme. * - * @param algorithm validation algorithm - * @param handshake handshake sequence - * @param bufferOffset buffer offset + * @param algorithm + * validation algorithm + * @param handshake + * handshake sequence + * @param bufferOffset + * buffer offset * @return digest offset */ public int getDigestOffset(int algorithm, byte[] handshake, int bufferOffset) { @@ -454,8 +520,10 @@ public int getDigestOffset(int algorithm, byte[] handshake, int bufferOffset) { /** * Returns a digest byte offset. * - * @param handshake handshake sequence - * @param bufferOffset buffer offset + * @param handshake + * handshake sequence + * @param bufferOffset + * buffer offset * @return digest offset */ protected int getDigestOffset1(byte[] handshake, int bufferOffset) { @@ -477,8 +545,10 @@ protected int getDigestOffset1(byte[] handshake, int bufferOffset) { /** * Returns a digest byte offset. * - * @param handshake handshake sequence - * @param bufferOffset buffer offset + * @param handshake + * handshake sequence + * @param bufferOffset + * buffer offset * @return digest offset */ protected int getDigestOffset2(byte[] handshake, int bufferOffset) { @@ -500,14 +570,17 @@ protected int getDigestOffset2(byte[] handshake, int bufferOffset) { /** * RTMPE type 8 uses XTEA on the regular signature http://en.wikipedia.org/wiki/XTEA * - * @param array array to get signature - * @param offset offset to start from - * @param keyid ID of XTEA key + * @param array + * array to get signature + * @param offset + * offset to start from + * @param keyid + * index of XTEA key */ - public final static void getXteaSignature(byte[] array, int offset, int keyid) { + public void getXteaSignature(byte[] array, int offset, int keyId) { int num_rounds = 32; int v0, v1, sum = 0, delta = 0x9E3779B9; - int[] k = XTEA_KEYS[keyid]; + byte[] k = XTEA_KEYS[keyId]; v0 = ByteBuffer.wrap(array, offset, 4).getInt(); v1 = ByteBuffer.wrap(array, offset + 4, 4).getInt(); for (int i = 0; i < num_rounds; i++) { @@ -528,23 +601,27 @@ public final static void getXteaSignature(byte[] array, int offset, int keyid) { /** * RTMPE type 9 uses Blowfish on the regular signature http://en.wikipedia.org/wiki/Blowfish_(cipher) * - * @param array array to get signature - * @param offset offset to start from - * @param keyid ID of XTEA key + * @param array + * array to get signature + * @param offset + * offset to start from + * @param keyId + * index of Blowfish key */ - public final static void getBlowfishSignature(byte[] array, int offset, int keyid) { - BlowfishEngine bf = new BlowfishEngine(); - // need to use little endian - bf.init(true, new KeyParameter(BLOWFISH_KEYS[keyid])); + public void getBlowfishSignature(byte[] array, int offset, int keyId) { + if (blowfish == null) { + initBlowfishEncryption(keyId); + } byte[] output = new byte[8]; - bf.processBlock(array, offset, output, 0); + blowfish.processBlock(array, offset, output, 0); System.arraycopy(output, 0, array, offset, 8); } /** * Returns whether or not a given handshake type is valid. * - * @param handshakeType the type of handshake + * @param handshakeType + * the type of handshake * @return true if valid and supported, false otherwise */ public final static boolean validHandshakeType(byte handshakeType) { @@ -577,7 +654,8 @@ public boolean useEncryption() { /** * Sets the handshake type. Currently only two types are supported, plain and encrypted. * - * @param handshakeType handshake type + * @param handshakeType + * handshake type */ public void setHandshakeType(byte handshakeType) { if (log.isTraceEnabled()) { @@ -638,7 +716,8 @@ public int getBufferSize() { /** * Add a byte array to the buffer. * - * @param in incoming bytes + * @param in + * incoming bytes */ public void addBuffer(byte[] in) { buffer.put(in); @@ -647,7 +726,8 @@ public void addBuffer(byte[] in) { /** * Add a IoBuffer to the buffer. * - * @param in incoming IoBuffer + * @param in + * incoming IoBuffer */ public void addBuffer(IoBuffer in) { byte[] tmp = new byte[in.remaining()]; diff --git a/src/main/java/org/red5/server/scope/Scope.java b/src/main/java/org/red5/server/scope/Scope.java index 46635466..9dffd570 100644 --- a/src/main/java/org/red5/server/scope/Scope.java +++ b/src/main/java/org/red5/server/scope/Scope.java @@ -491,8 +491,16 @@ public Set getBasicScopeNames(ScopeType type) { broadcastNames.add(bs.getName()); // add any aliases IClientBroadcastStream stream = ((IBroadcastScope) bs).getClientBroadcastStream(); - if (stream != null && stream.hasAlias()) { - broadcastNames.addAll(stream.getAliases()); + if (stream != null) { + // publish alias if it exists + String nameAlias = stream.getAlias(); + if (nameAlias != null) { + broadcastNames.add(nameAlias); + } + // subscribe aliases + if (stream.hasAlias()) { + broadcastNames.addAll(stream.getAliases()); + } } }); return broadcastNames; @@ -1437,7 +1445,8 @@ public IBasicScope getBasicScope(ScopeType type, String name) { } else { // if its broadcast type then allow an alias match in addition to the name match if (type == ScopeType.BROADCAST) { - scope = stream().filter(child -> child.getType().equals(type) && (name.equals(child.getName()) || ((IBroadcastScope) child).getClientBroadcastStream().containsAlias(name))).findFirst(); + // checks publish and subscribe aliases + scope = stream().filter(child -> child.getType().equals(type) && (name.equals(child.getName()) || name.equals(((IBroadcastScope) child).getClientBroadcastStream().getAlias()) || ((IBroadcastScope) child).getClientBroadcastStream().containsAlias(name))).findFirst(); } else { scope = stream().filter(child -> child.getType().equals(type) && name.equals(child.getName())).findFirst(); } diff --git a/src/main/java/org/red5/server/stream/ClientBroadcastStream.java b/src/main/java/org/red5/server/stream/ClientBroadcastStream.java index cab9f090..74ef54d7 100644 --- a/src/main/java/org/red5/server/stream/ClientBroadcastStream.java +++ b/src/main/java/org/red5/server/stream/ClientBroadcastStream.java @@ -185,7 +185,12 @@ public class ClientBroadcastStream extends AbstractClientStream implements IClie protected static CopyOnWriteArraySet localAliases = new CopyOnWriteArraySet<>(); /** - * Stream name aliases for this instance. + * Publish alias for the stream name. + */ + protected String nameAlias; + + /** + * Subscribe aliases for this instance. */ protected CopyOnWriteArraySet aliases; @@ -238,6 +243,11 @@ public void close() { localAliases.removeAll(aliases); aliases.clear(); } + // remove publish alias + if (nameAlias != null) { + localAliases.remove(nameAlias); + nameAlias = null; + } } } @@ -1041,4 +1051,24 @@ public Set getAliases() { return Collections.emptySet(); } + @Override + public void setNameAlias(String nameAlias) { + // remove any existing registration + if (this.nameAlias != null && nameAlias != null) { + localAliases.remove(this.nameAlias); + } + // this will overwrite any existing value + this.nameAlias = nameAlias; + } + + @Override + public String getNameAlias() { + return nameAlias; + } + + @Override + public boolean aliasRegistered(String alias) { + return localAliases.contains(alias); + } + } diff --git a/src/main/java/org/red5/server/stream/StreamService.java b/src/main/java/org/red5/server/stream/StreamService.java index e466ad53..4f09bb8a 100644 --- a/src/main/java/org/red5/server/stream/StreamService.java +++ b/src/main/java/org/red5/server/stream/StreamService.java @@ -679,9 +679,17 @@ public void publish(String name, String mode) { return; } IClientStream stream = streamConn.getStreamById(streamId); - if (stream != null && !(stream instanceof IClientBroadcastStream)) { - log.error("Stream not found or is not instance of IClientBroadcastStream, name: {}, streamId: {}", name, streamId); - return; + if (stream != null) { + if (!(stream instanceof IClientBroadcastStream)) { + log.error("Stream not found or is not instance of IClientBroadcastStream, name: {}, streamId: {}", name, streamId); + return; + } + // verify that the name is not in-use for aliasing + if (nameAliasingEnabled && ((IClientBroadcastStream) stream).aliasRegistered(name)) { + sendNSFailed(streamConn, StatusCodes.NS_PUBLISH_BADNAME, "You are not allowed to publish the stream, alias exists.", name, streamId); + log.error("Bad name {} (alias in-use)", name); + return; + } } boolean created = false; if (stream == null) { @@ -697,7 +705,7 @@ public void publish(String name, String mode) { bs.setParameters(params); } if (nameAliasingEnabled) { - // if aliasing, check for requested aliases before generating random names + // if aliasing, check for requested subscribe-side aliases before generating random names if (params != null && params.containsKey("aliases")) { // comma separated Stream.of(params.get("aliases").split(",")).forEach(alias -> bs.addAlias(alias)); @@ -709,6 +717,10 @@ public void publish(String name, String mode) { bs.addAlias(RandomStringUtils.randomAlphanumeric(8, 16)); } } + // if aliasing, check for publish-side alias + if (params != null && params.containsKey("nameAlias")) { + bs.setNameAlias(params.get("nameAlias")); + } } IContext context = conn.getScope().getContext(); IProviderService providerService = (IProviderService) context.getBean(IProviderService.BEAN_NAME);