package org.jitsi.videobridge.openfire; import org.ice4j.StackProperties; import org.ice4j.ice.harvest.MappingCandidateHarvesters; import org.jitsi.service.libjitsi.LibJitsi; import org.jitsi.service.neomedia.DefaultStreamConnector; import org.jitsi.videobridge.IceUdpTransportManager; import org.jivesoftware.util.JiveGlobals; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; /** * Exposes various bits of Jitsi configuration. * * Various Jitsi Components use different mechanisms to get their configuration. Notably, LibJitsi uses a configuration * service implementation, that's typically used in a OSGi environment, while ice4j uses system properties. * * This class wraps each of these mechanisms. * * This implementation preserves the configured values when initially used. This allows the implementation to detect * configuration changes, that are not yet applied. * * @author Guus der Kinderen, [email protected] */ public class RuntimeConfiguration { private static final Logger Log = LoggerFactory.getLogger( RuntimeConfiguration.class ); /** * The default UDP port value used when multiplexing multiple media streams. */ public static final int SINGLE_PORT_DEFAULT_VALUE = 10000; // should be equal to org.jitsi.videobridge.IceUdpTransportManager.SINGLE_PORT_DEFAULT_VALUE /** * The minimum port number default value. */ public static final int MIN_PORT_DEFAULT_VALUE = 10001; /** * The maximum port number default value. */ public static final int MAX_PORT_DEFAULT_VALUE = 20000; /** * The default setting for _disabling_ the TCP connectivity. */ public static final boolean DISABLE_TCP_HARVESTER_DEFAULT_VALUE = false; // should be equal to the default behavior as implemented in org.jitsi.videobridge.IceUdpTransportManager /** * The default value for SslTcp wrapping. */ public static final boolean SSLTCP_TCP_HARVESTER_DEFAULT_VALUE = true; // should be equal to org.jitsi.videobridge.IceUdpTransportManager.TCP_HARVESTER_SSLTCP_DEFAULT /** * Changes to the allowed network interfaces require a restart of the plugin to take effect. * The value that is currently in use is equal to the value that was configured when this plugin got initialized, * which is what is stored in this field. */ private static final String ALLOWED_INTERFACES_AT_STARTUP = RuntimeConfiguration.getAllowedInterfaces(); /** * Changes to the blocked network interfaces require a restart of the plugin to take effect. * The value that is currently in use is equal to the value that was configured when this plugin got initialized, * which is what is stored in this field. */ private static final String BLOCKED_INTERFACES_AT_STARTUP = RuntimeConfiguration.getBlockedInterfaces(); /** * Changes to the allowed network addresses require a restart of the plugin to take effect. * The value that is currently in use is equal to the value that was configured when this plugin got initialized, * which is what is stored in this field. */ private static final String ALLOWED_ADDRESSES_AT_STARTUP = RuntimeConfiguration.getAllowedAddresses(); /** * Changes to the blocked network addresses require a restart of the plugin to take effect. * The value that is currently in use is equal to the value that was configured when this plugin got initialized, * which is what is stored in this field. */ private static final String BLOCKED_ADDRESSES_AT_STARTUP = RuntimeConfiguration.getBlockedAddresses(); /** * Changes to availability of single-port multiplexing of UDP data require a restart of the plugin to take effect. * The value that is currently in use is equal to the value that was configured when this plugin got initialized, * which is what is stored in this field. */ private static final boolean WAS_SINGLE_PORT_ENABLED_AT_STARTUP = RuntimeConfiguration.isSinglePortEnabled(); /** * Changes to port number configuration require a restart of the plugin to take effect. * The single port number value that is currently in use is equal to the port number * that was configured when this plugin got initialized, which is what is stored in this field. */ private static final int SINGLE_PORT_AT_STARTUP = RuntimeConfiguration.getSinglePort(); /** * Changes to availability of dynamic port usage of UDP data require a restart of the plugin to take effect. * The value that is currently in use is equal to the value that was configured when this plugin got initialized, * which is what is stored in this field. */ private static final boolean WAS_MINMAX_PORT_ENABLED_AT_STARTUP = RuntimeConfiguration.isMinMaxPortEnabled(); /** * Changes to port number configuration require a restart of the plugin to take effect. * The minimum port number value that is currently in use is equal to the port number * that was configured when this plugin got initialized, which is what is stored in this field. */ private static final int MIN_PORT_AT_STARTUP = RuntimeConfiguration.getMinPort(); /** * Changes to port number configuration require a restart of the plugin to take effect. * The maximum port number value that is currently in use is equal to the port number * that was configured when this plugin got initialized, which is what is stored in this field. */ private static final int MAX_PORT_AT_STARTUP = RuntimeConfiguration.getMaxPort(); /** * Changes to TCP harvester availability require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. */ private static final boolean WAS_TCP_PORT_ENABLED_AT_STARTUP = RuntimeConfiguration.isTcpEnabled(); /** * Changes to TCP harvester availability require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. Note: can be null. */ private static final Integer TCP_PORT_AT_STARTUP = RuntimeConfiguration.getTcpPort(); /** * Changes to TCP harvester availability require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. Note: can be null. */ private static final Integer TCP_MAPPED_PORT_AT_STARTUP = RuntimeConfiguration.getTcpMappedPort(); /** * Changes to TCP harvester availability require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. */ private static final boolean SSLTCP_ENABLED_AT_STARTUP = RuntimeConfiguration.isSslTcpEnabled(); /** * Changes to AWS harvester availability require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. */ private static final boolean WAS_AWS_MAPPING_HARVESTER_ENABLED_AT_STARTUP = RuntimeConfiguration.isAWSMappingHarvesterEnabled(); /** * Changes to AWS harvester forced usage require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. */ private static final boolean WAS_AWS_MAPPING_HARVESTER_FORCED_AT_STARTUP = RuntimeConfiguration.isAWSMappingHarvesterForced(); /** * Changes to collection of STUN servers used for network address mapping require a restart of the plugin to take effect. * The list currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. */ private static final List<InetSocketAddress> STUN_MAPPING_HARVESTER_ADDRESSES_AT_STARTUP = RuntimeConfiguration.getSTUNMappingHarvesterAddresses(); /** * Changes to manually provided local network address value require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. Note: can be null. */ private static final String MANUAL_MAPPED_LOCAL_ADDRESS_AT_STARTUP = RuntimeConfiguration.getManualMappedLocalAddress(); /** * Changes to manually provided public network address value require a restart of the plugin to take effect. * The value currently used is equal to the value that was configured when this * plugin got initialized, which is what is stored in this field. Note: can be null. */ private static final String MANUAL_MAPPED_PUBLIC_ADDRESS_AT_STARTUP = RuntimeConfiguration.getManualMappedPublicAddress(); /** * Returns the (;-separated) string of interfaces that are allowed to be used, or null if all of them are allowed. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A string of interface names, possibly null. */ public static String getAllowedInterfaces() { return StackProperties.getString( StackProperties.ALLOWED_INTERFACES ); } /** * Returns the (;-separated) string of interfaces that are blocked from usage. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A string of interface names, possibly null. */ public static String getBlockedInterfaces() { return StackProperties.getString( StackProperties.BLOCKED_INTERFACES ); } /** * Returns the (;-separated) string of addresses that are allowed to be used, or null if all of them are allowed. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A string of interface names, possibly null. */ public static String getAllowedAddresses() { return StackProperties.getString( StackProperties.ALLOWED_ADDRESSES ); } /** * Returns the (;-separated) string of addresses that are blocked from usage. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A string of interface names, possibly null. */ public static String getBlockedAddresses() { return StackProperties.getString( StackProperties.BLOCKED_ADDRESSES ); } /** * Verifies if Jitsi Videobridge allows to multiplex media data over a single UDP port. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A boolean value that indicates if the videobridge is configured to allow single-port UDP multiplexing.. */ public static boolean isSinglePortEnabled() { return getSinglePort() != -1; } /** * Returns the UDP port number that is used for multiplexing multiple media streams. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return a UDP port number value. */ public static int getSinglePort() { return LibJitsi.getConfigurationService().getInt( IceUdpTransportManager.SINGLE_PORT_HARVESTER_PORT, SINGLE_PORT_DEFAULT_VALUE ); } /** * Verifies if Jitsi Videobridge allows to use dynamically allocated ports ofr media streaming. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A boolean value that indicates if the videobridge is configured to allow dynamically allocated ports. */ public static boolean isMinMaxPortEnabled() { return StackProperties.getBoolean( StackProperties.USE_DYNAMIC_HOST_HARVESTER,true ); } /** * When multiplexing of media streams is not possible, the videobridge will automatically fallback to using * dynamically allocated UDP ports in a specific range. This method returns the upper-bound of that range. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A UDP port number value. */ public static int getMaxPort() { return LibJitsi.getConfigurationService().getInt( DefaultStreamConnector.MAX_PORT_NUMBER_PROPERTY_NAME, MAX_PORT_DEFAULT_VALUE ); } /** * When multiplexing of media streams is not possible, the videobridge will automatically fallback to using * dynamically allocated UDP ports in a specific range. This method returns the lower-bound of that range. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A UDP port number value. */ public static int getMinPort() { return LibJitsi.getConfigurationService().getInt( DefaultStreamConnector.MIN_PORT_NUMBER_PROPERTY_NAME, MIN_PORT_DEFAULT_VALUE ); } /** * Jitsi Videobridge can accept and route RTP traffic over TCP. If enabled, TCP addresses will automatically be * returned as ICE candidates via COLIBRI. Typically, the point of using TCP instead of UDP is to simulate HTTP * traffic in a number of environments where it is the only allowed form of communication. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return A boolean value that indicates if the videobridge is configured to allow RTP traffic over TCP. */ public static boolean isTcpEnabled() { // Jitsi uses a 'disable' option here. We should negate their setting. return !LibJitsi.getConfigurationService().getBoolean( IceUdpTransportManager.DISABLE_TCP_HARVESTER, DISABLE_TCP_HARVESTER_DEFAULT_VALUE ); } /** * Returns the TCP port number that is used for multiplexing multiple media streams over TCP, or null if the default * is to be used. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return a TCP port number value, possibly null. */ public static Integer getTcpPort() { final int value = LibJitsi.getConfigurationService().getInt( IceUdpTransportManager.TCP_HARVESTER_PORT, -1 ); if ( value == -1 ) { return null; } return value; } /** * Returns the TCP port number mapping that is used for multiplexing multiple media streams over TCP, or null if the * default is to be used. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return a TCP port number value, possibly null. */ public static Integer getTcpMappedPort() { final int value = LibJitsi.getConfigurationService().getInt( IceUdpTransportManager.TCP_HARVESTER_MAPPED_PORT, -1 ); if ( value == -1 ) { return null; } return value; } /** * Indicates if the media stream that is received over TCP is expected to be wrapped in (pseudo) TLS. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return true if pseudo TCP wrapping is enabled, otherwise false. */ public static boolean isSslTcpEnabled() { return LibJitsi.getConfigurationService().getBoolean( IceUdpTransportManager.TCP_HARVESTER_SSLTCP, SSLTCP_TCP_HARVESTER_DEFAULT_VALUE ); } /** * Indicates if the automatic network address mapping for AWS is enabled. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return true if the automatic network address mapping for AWS is enabled, otherwise false. */ public static boolean isAWSMappingHarvesterEnabled() { final boolean disabled = StackProperties.getBoolean( MappingCandidateHarvesters.DISABLE_AWS_HARVESTER_PNAME, false ); return !disabled; } /** * Indicates if the automatic network address mapping for AWS is forced (used even when the bridge does not detect * it's running on AWS). * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return true if the automatic network address mapping for AWS is forced, otherwise false. */ public static boolean isAWSMappingHarvesterForced() { if ( isAWSMappingHarvesterEnabled() ) { return StackProperties.getBoolean( MappingCandidateHarvesters.FORCE_AWS_HARVESTER_PNAME, false ); } else { return false; } } /** * Returns a list of STUN server addresses, used to perform network address mapping. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return a list of address/port pairs, possibly empty, but never null. */ public static List<InetSocketAddress> getSTUNMappingHarvesterAddresses() { final List<InetSocketAddress> results = new ArrayList<>(); final String[] addresses = StackProperties.getStringArray( MappingCandidateHarvesters.STUN_MAPPING_HARVESTER_ADDRESSES_PNAME, "," ); if( addresses == null ) { return results; } for ( String address : addresses ) { if ( address != null ) { try { final String[] parts = address.split( ":" ); final InetSocketAddress result = new InetSocketAddress( parts[ 0 ], Integer.parseInt( parts[ 1 ] ) ); results.add( result ); } catch ( Throwable t ) { Log.warn( "Unable to parse STUN Mappping Harvester address '{}'. This value will be ignored.", address ); } } } return results; } /** * Returns a manually provided network address that represents the 'local' (or 'private') address. Used for network * address mapping. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return a network address value, possibly null. */ public static String getManualMappedLocalAddress() { return StackProperties.getString( MappingCandidateHarvesters.NAT_HARVESTER_LOCAL_ADDRESS_PNAME ); } /** * Returns a manually provided network address that represents the 'public' address. Used for network * address mapping. * * Note that this method returns the configured value, which might differ from the configuration that is * in effect (as configuration changes require a restart to be taken into effect). * * @return a network address value, possibly null. */ public static String getManualMappedPublicAddress() { return StackProperties.getString( MappingCandidateHarvesters.NAT_HARVESTER_PUBLIC_ADDRESS_PNAME ); } /** * Checks if the plugin requires a restart to apply pending configuration changes. * * @return true if a restart is needed to apply pending changes, otherwise false. */ public static boolean restartNeeded() { return ( ALLOWED_INTERFACES_AT_STARTUP == null && RuntimeConfiguration.getAllowedInterfaces() != null ) || ( ALLOWED_INTERFACES_AT_STARTUP != null && !ALLOWED_INTERFACES_AT_STARTUP.equals( RuntimeConfiguration.getAllowedInterfaces() ) ) || ( BLOCKED_INTERFACES_AT_STARTUP == null && RuntimeConfiguration.getBlockedInterfaces() != null ) || ( BLOCKED_INTERFACES_AT_STARTUP != null && !BLOCKED_INTERFACES_AT_STARTUP.equals( RuntimeConfiguration.getBlockedInterfaces() ) ) || ( ALLOWED_ADDRESSES_AT_STARTUP == null && RuntimeConfiguration.getAllowedAddresses() != null ) || ( ALLOWED_ADDRESSES_AT_STARTUP != null && !ALLOWED_ADDRESSES_AT_STARTUP.equals( RuntimeConfiguration.getAllowedAddresses() ) ) || ( BLOCKED_ADDRESSES_AT_STARTUP == null && RuntimeConfiguration.getBlockedAddresses() != null ) || ( BLOCKED_ADDRESSES_AT_STARTUP != null && !BLOCKED_ADDRESSES_AT_STARTUP.equals( RuntimeConfiguration.getBlockedAddresses() ) ) || WAS_SINGLE_PORT_ENABLED_AT_STARTUP != RuntimeConfiguration.isSinglePortEnabled() || SINGLE_PORT_AT_STARTUP != RuntimeConfiguration.getSinglePort() || WAS_MINMAX_PORT_ENABLED_AT_STARTUP != RuntimeConfiguration.isMinMaxPortEnabled() || MAX_PORT_AT_STARTUP != RuntimeConfiguration.getMaxPort() || MIN_PORT_AT_STARTUP != RuntimeConfiguration.getMinPort() || WAS_TCP_PORT_ENABLED_AT_STARTUP != RuntimeConfiguration.isTcpEnabled() || ( TCP_PORT_AT_STARTUP == null && RuntimeConfiguration.getTcpPort() != null) || ( TCP_PORT_AT_STARTUP != null && !TCP_PORT_AT_STARTUP.equals( RuntimeConfiguration.getTcpPort() ) ) || ( TCP_MAPPED_PORT_AT_STARTUP == null && RuntimeConfiguration.getTcpMappedPort() != null) || ( TCP_MAPPED_PORT_AT_STARTUP != null && !TCP_MAPPED_PORT_AT_STARTUP.equals( RuntimeConfiguration.getTcpMappedPort() ) ) || SSLTCP_ENABLED_AT_STARTUP != RuntimeConfiguration.isSslTcpEnabled() || WAS_AWS_MAPPING_HARVESTER_ENABLED_AT_STARTUP != RuntimeConfiguration.isAWSMappingHarvesterEnabled() || WAS_AWS_MAPPING_HARVESTER_FORCED_AT_STARTUP != RuntimeConfiguration.isAWSMappingHarvesterForced() || !STUN_MAPPING_HARVESTER_ADDRESSES_AT_STARTUP.equals( RuntimeConfiguration.getSTUNMappingHarvesterAddresses() ) || (MANUAL_MAPPED_LOCAL_ADDRESS_AT_STARTUP == null && RuntimeConfiguration.getManualMappedLocalAddress() != null) || (MANUAL_MAPPED_LOCAL_ADDRESS_AT_STARTUP != null && !MANUAL_MAPPED_LOCAL_ADDRESS_AT_STARTUP.equals( RuntimeConfiguration.getManualMappedLocalAddress() ) ) || (MANUAL_MAPPED_PUBLIC_ADDRESS_AT_STARTUP == null && RuntimeConfiguration.getManualMappedPublicAddress() != null) || (MANUAL_MAPPED_PUBLIC_ADDRESS_AT_STARTUP != null && !MANUAL_MAPPED_PUBLIC_ADDRESS_AT_STARTUP.equals( RuntimeConfiguration.getManualMappedPublicAddress() ) ); } }