Require CDN number match rather than use default CDN

This marks messages as failed if the CDN number does not match a
configured CDN number rather than falling back to the default CDN in
the event the CDN is not recognized.
master
Ehren Kret 2020-04-14 08:33:09 -07:00 committed by Greyson Parrelli
parent f12a9b9ac7
commit 456bcf3d57
11 changed files with 176 additions and 119 deletions

View File

@ -36,17 +36,17 @@ public class ApplicationDependencies {
private static Application application;
private static Provider provider;
private static SignalServiceAccountManager accountManager;
private static SignalServiceMessageSender messageSender;
private static SignalServiceMessageReceiver messageReceiver;
private static IncomingMessageProcessor incomingMessageProcessor;
private static MessageRetriever messageRetriever;
private static LiveRecipientCache recipientCache;
private static JobManager jobManager;
private static FrameRateTracker frameRateTracker;
private static KeyValueStore keyValueStore;
private static MegaphoneRepository megaphoneRepository;
private static GroupsV2Operations groupsV2Operations;
private static SignalServiceAccountManager accountManager;
private static SignalServiceMessageSender messageSender;
private static SignalServiceMessageReceiver messageReceiver;
private static IncomingMessageProcessor incomingMessageProcessor;
private static MessageRetriever messageRetriever;
private static LiveRecipientCache recipientCache;
private static JobManager jobManager;
private static FrameRateTracker frameRateTracker;
private static KeyValueStore keyValueStore;
private static MegaphoneRepository megaphoneRepository;
private static GroupsV2Operations groupsV2Operations;
public static synchronized void init(@NonNull Application application, @NonNull Provider provider) {
if (ApplicationDependencies.application != null || ApplicationDependencies.provider != null) {

View File

@ -30,6 +30,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@ -168,7 +169,7 @@ public class AttachmentDownloadJob extends BaseJob {
InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)));
database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream);
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) {
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException | MissingConfigurationException e) {
Log.w(TAG, "Experienced exception while trying to download an attachment.", e);
markFailed(messageId, attachmentId);
}

View File

@ -18,6 +18,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import java.io.File;
@ -92,7 +93,7 @@ public final class AvatarGroupsV1DownloadJob extends BaseJob {
inputStream.close();
}
} catch (NonSuccessfulResponseCodeException | InvalidMessageException e) {
} catch (NonSuccessfulResponseCodeException | InvalidMessageException | MissingConfigurationException e) {
Log.w(TAG, e);
} finally {
if (attachment != null)

View File

@ -174,8 +174,8 @@ public class SignalServiceNetworkAccess {
this.censorshipConfiguration = new HashMap<String, SignalServiceConfiguration>() {{
put(COUNTRY_CODE_EGYPT, new SignalServiceConfiguration(new SignalServiceUrl[] {egyptGoogleService, baseGoogleService, baseAndroidService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
new SignalCdnUrl[] {egyptGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {egyptGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2, mailAndroidCdn2},
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {egyptGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {egyptGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2, mailAndroidCdn2}),
new SignalContactDiscoveryUrl[] {egyptGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
new SignalKeyBackupServiceUrl[] {egyptGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
new SignalStorageUrl[] {egyptGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
@ -184,8 +184,8 @@ public class SignalServiceNetworkAccess {
zkGroupServerPublicParams));
put(COUNTRY_CODE_UAE, new SignalServiceConfiguration(new SignalServiceUrl[] {uaeGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
new SignalCdnUrl[] {uaeGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {uaeGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2},
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {uaeGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {uaeGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
new SignalContactDiscoveryUrl[] {uaeGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
new SignalKeyBackupServiceUrl[] {uaeGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
new SignalStorageUrl[] {uaeGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
@ -194,8 +194,8 @@ public class SignalServiceNetworkAccess {
zkGroupServerPublicParams));
put(COUNTRY_CODE_OMAN, new SignalServiceConfiguration(new SignalServiceUrl[] {omanGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
new SignalCdnUrl[] {omanGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {omanGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2},
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {omanGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {omanGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
new SignalContactDiscoveryUrl[] {omanGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
new SignalKeyBackupServiceUrl[] {omanGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
new SignalStorageUrl[] {omanGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
@ -205,8 +205,8 @@ public class SignalServiceNetworkAccess {
put(COUNTRY_CODE_QATAR, new SignalServiceConfiguration(new SignalServiceUrl[] {qatarGoogleService, baseAndroidService, baseGoogleService, mapsOneAndroidService, mapsTwoAndroidService, mailAndroidService},
new SignalCdnUrl[] {qatarGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {qatarGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2},
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {qatarGoogleCdn, baseAndroidCdn, baseGoogleCdn, mapsOneAndroidCdn, mapsTwoAndroidCdn, mailAndroidCdn},
new SignalCdnUrl[] {qatarGoogleCdn2, baseAndroidCdn2, baseGoogleCdn2, mapsOneAndroidCdn2, mapsTwoAndroidCdn2, mailAndroidCdn2}),
new SignalContactDiscoveryUrl[] {qatarGoogleDiscovery, baseGoogleDiscovery, baseAndroidDiscovery, mapsOneAndroidDiscovery, mapsTwoAndroidDiscovery, mailAndroidDiscovery},
new SignalKeyBackupServiceUrl[] {qatarGoogleKbs, baseGoogleKbs, baseAndroidKbs, mapsOneAndroidKbs, mapsTwoAndroidKbs, mailAndroidKbs},
new SignalStorageUrl[] {qatarGoogleStorage, baseGoogleStorage, baseAndroidStorage, mapsOneAndroidStorage, mapsTwoAndroidStorage, mailAndroidStorage},
@ -216,8 +216,8 @@ public class SignalServiceNetworkAccess {
}};
this.uncensoredConfiguration = new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl(BuildConfig.SIGNAL_URL, new SignalServiceTrustStore(context))},
new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, new SignalServiceTrustStore(context))},
new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN2_URL, new SignalServiceTrustStore(context))},
makeSignalCdnUrlMapFor(new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, new SignalServiceTrustStore(context))},
new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN2_URL, new SignalServiceTrustStore(context))}),
new SignalContactDiscoveryUrl[] {new SignalContactDiscoveryUrl(BuildConfig.SIGNAL_CONTACT_DISCOVERY_URL, new SignalServiceTrustStore(context))},
new SignalKeyBackupServiceUrl[] { new SignalKeyBackupServiceUrl(BuildConfig.SIGNAL_KEY_BACKUP_URL, new SignalServiceTrustStore(context)) },
new SignalStorageUrl[] {new SignalStorageUrl(BuildConfig.STORAGE_URL, new SignalServiceTrustStore(context))},
@ -253,4 +253,10 @@ public class SignalServiceNetworkAccess {
return getConfiguration(number) != this.uncensoredConfiguration;
}
private static Map<Integer, SignalCdnUrl[]> makeSignalCdnUrlMapFor(SignalCdnUrl[] cdn0Urls, SignalCdnUrl[] cdn2Urls) {
Map<Integer, SignalCdnUrl[]> result = new HashMap<>();
result.put(0, cdn0Urls);
result.put(2, cdn2Urls);
return Collections.unmodifiableMap(result);
}
}

View File

@ -16,7 +16,6 @@ import org.whispersystems.signalservice.FeatureFlags;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream;
import org.whispersystems.signalservice.api.crypto.ProfileCipherInputStream;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment.ProgressListener;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
@ -25,6 +24,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceStickerManifes
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
@ -118,8 +118,7 @@ public class SignalServiceMessageReceiver {
* @throws InvalidMessageException
*/
public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, File destination, long maxSizeBytes)
throws IOException, InvalidMessageException
{
throws IOException, InvalidMessageException, MissingConfigurationException {
return retrieveAttachment(pointer, destination, maxSizeBytes, null);
}
@ -174,8 +173,7 @@ public class SignalServiceMessageReceiver {
* @throws InvalidMessageException
*/
public InputStream retrieveAttachment(SignalServiceAttachmentPointer pointer, File destination, long maxSizeBytes, ProgressListener listener)
throws IOException, InvalidMessageException
{
throws IOException, InvalidMessageException, MissingConfigurationException {
if (!pointer.getDigest().isPresent()) throw new InvalidMessageException("No attachment digest!");
socket.retrieveAttachment(pointer.getCdnNumber(), pointer.getRemoteId(), destination, maxSizeBytes, listener);

View File

@ -39,6 +39,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException;
import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageProtocolVersionException;
import org.whispersystems.signalservice.internal.serialize.SignalServiceAddressProtobufSerializer;
import org.whispersystems.signalservice.internal.serialize.SignalServiceMetadataProtobufSerializer;
import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto;
@ -268,7 +269,8 @@ public final class SignalServiceContent {
return null;
}
private static SignalServiceDataMessage createSignalServiceMessage(SignalServiceMetadata metadata, SignalServiceProtos.DataMessage content)
private static SignalServiceDataMessage createSignalServiceMessage(SignalServiceMetadata metadata,
SignalServiceProtos.DataMessage content)
throws ProtocolInvalidMessageException, UnsupportedDataMessageException
{
SignalServiceGroup groupInfoV1 = createGroupV1Info(content);
@ -292,12 +294,12 @@ public final class SignalServiceContent {
SignalServiceDataMessage.Sticker sticker = createSticker(content);
SignalServiceDataMessage.Reaction reaction = createReaction(content);
if (content.getRequiredProtocolVersion() > SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT.getNumber()) {
throw new UnsupportedDataMessageException(SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT.getNumber(),
content.getRequiredProtocolVersion(),
metadata.getSender().getIdentifier(),
metadata.getSenderDevice(),
groupContext);
if (content.getRequiredProtocolVersion() > SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT_VALUE) {
throw new UnsupportedDataMessageProtocolVersionException(SignalServiceProtos.DataMessage.ProtocolVersion.CURRENT_VALUE,
content.getRequiredProtocolVersion(),
metadata.getSender().getIdentifier(),
metadata.getSenderDevice(),
groupContext);
}
for (SignalServiceProtos.AttachmentPointer pointer : content.getAttachmentsList()) {
@ -327,7 +329,8 @@ public final class SignalServiceContent {
reaction);
}
private static SignalServiceSyncMessage createSynchronizeMessage(SignalServiceMetadata metadata, SignalServiceProtos.SyncMessage content)
private static SignalServiceSyncMessage createSynchronizeMessage(SignalServiceMetadata metadata,
SignalServiceProtos.SyncMessage content)
throws ProtocolInvalidMessageException, ProtocolInvalidKeyException, UnsupportedDataMessageException
{
if (content.hasSent()) {

View File

@ -0,0 +1,7 @@
package org.whispersystems.signalservice.api.push.exceptions;
public final class MissingConfigurationException extends Exception {
public MissingConfigurationException(String s) {
super(s);
}
}

View File

@ -3,25 +3,24 @@ package org.whispersystems.signalservice.internal.configuration;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.List;
import java.util.Map;
import okhttp3.Dns;
import okhttp3.Interceptor;
public final class SignalServiceConfiguration {
private final SignalServiceUrl[] signalServiceUrls;
private final SignalCdnUrl[] signalCdnUrls;
private final SignalCdnUrl[] signalCdn2Urls;
private final SignalContactDiscoveryUrl[] signalContactDiscoveryUrls;
private final SignalKeyBackupServiceUrl[] signalKeyBackupServiceUrls;
private final SignalStorageUrl[] signalStorageUrls;
private final List<Interceptor> networkInterceptors;
private final Optional<Dns> dns;
private final byte[] zkGroupServerPublicParams;
private final SignalServiceUrl[] signalServiceUrls;
private final Map<Integer, SignalCdnUrl[]> signalCdnUrlMap;
private final SignalContactDiscoveryUrl[] signalContactDiscoveryUrls;
private final SignalKeyBackupServiceUrl[] signalKeyBackupServiceUrls;
private final SignalStorageUrl[] signalStorageUrls;
private final List<Interceptor> networkInterceptors;
private final Optional<Dns> dns;
private final byte[] zkGroupServerPublicParams;
public SignalServiceConfiguration(SignalServiceUrl[] signalServiceUrls,
SignalCdnUrl[] signalCdnUrls,
SignalCdnUrl[] signalCdn2Urls,
Map<Integer, SignalCdnUrl[]> signalCdnUrlMap,
SignalContactDiscoveryUrl[] signalContactDiscoveryUrls,
SignalKeyBackupServiceUrl[] signalKeyBackupServiceUrls,
SignalStorageUrl[] signalStorageUrls,
@ -30,8 +29,7 @@ public final class SignalServiceConfiguration {
byte[] zkGroupServerPublicParams)
{
this.signalServiceUrls = signalServiceUrls;
this.signalCdnUrls = signalCdnUrls;
this.signalCdn2Urls = signalCdn2Urls;
this.signalCdnUrlMap = signalCdnUrlMap;
this.signalContactDiscoveryUrls = signalContactDiscoveryUrls;
this.signalKeyBackupServiceUrls = signalKeyBackupServiceUrls;
this.signalStorageUrls = signalStorageUrls;
@ -44,12 +42,8 @@ public final class SignalServiceConfiguration {
return signalServiceUrls;
}
public SignalCdnUrl[] getSignalCdnUrls() {
return signalCdnUrls;
}
public SignalCdnUrl[] getSignalCdn2Urls() {
return signalCdn2Urls;
public Map<Integer, SignalCdnUrl[]> getSignalCdnUrlMap() {
return signalCdnUrlMap;
}
public SignalContactDiscoveryUrl[] getSignalContactDiscoveryUrls() {

View File

@ -49,6 +49,7 @@ import org.whispersystems.signalservice.api.push.exceptions.CaptchaRequiredExcep
import org.whispersystems.signalservice.api.push.exceptions.ConflictException;
import org.whispersystems.signalservice.api.push.exceptions.ContactManifestMismatchException;
import org.whispersystems.signalservice.api.push.exceptions.ExpectationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
import org.whispersystems.signalservice.api.push.exceptions.NoContentException;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.NotFoundException;
@ -62,6 +63,7 @@ import org.whispersystems.signalservice.api.storage.StorageAuthResponse;
import org.whispersystems.signalservice.api.util.CredentialsProvider;
import org.whispersystems.signalservice.api.util.Tls12SocketFactory;
import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.configuration.SignalUrl;
import org.whispersystems.signalservice.internal.contacts.entities.DiscoveryRequest;
@ -98,6 +100,7 @@ import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@ -191,17 +194,16 @@ public class PushServiceSocket {
private long soTimeoutMillis = TimeUnit.SECONDS.toMillis(30);
private final Set<Call> connections = new HashSet<>();
private final ServiceConnectionHolder[] serviceClients;
private final ConnectionHolder[] cdnClients;
private final ConnectionHolder[] cdn2Clients;
private final ConnectionHolder[] contactDiscoveryClients;
private final ConnectionHolder[] keyBackupServiceClients;
private final ConnectionHolder[] storageClients;
private final ServiceConnectionHolder[] serviceClients;
private final Map<Integer, ConnectionHolder[]> cdnClientsMap;
private final ConnectionHolder[] contactDiscoveryClients;
private final ConnectionHolder[] keyBackupServiceClients;
private final ConnectionHolder[] storageClients;
private final CredentialsProvider credentialsProvider;
private final String signalAgent;
private final SecureRandom random;
private final ClientZkProfileOperations clientZkProfileOperations;
private final CredentialsProvider credentialsProvider;
private final String signalAgent;
private final SecureRandom random;
private final ClientZkProfileOperations clientZkProfileOperations;
public PushServiceSocket(SignalServiceConfiguration configuration,
CredentialsProvider credentialsProvider,
@ -211,8 +213,7 @@ public class PushServiceSocket {
this.credentialsProvider = credentialsProvider;
this.signalAgent = signalAgent;
this.serviceClients = createServiceConnectionHolders(configuration.getSignalServiceUrls(), configuration.getNetworkInterceptors(), configuration.getDns());
this.cdnClients = createConnectionHolders(configuration.getSignalCdnUrls(), configuration.getNetworkInterceptors(), configuration.getDns());
this.cdn2Clients = createConnectionHolders(configuration.getSignalCdn2Urls(), configuration.getNetworkInterceptors(), configuration.getDns());
this.cdnClientsMap = createCdnClientsMap(configuration.getSignalCdnUrlMap(), configuration.getNetworkInterceptors(), configuration.getDns());
this.contactDiscoveryClients = createConnectionHolders(configuration.getSignalContactDiscoveryUrls(), configuration.getNetworkInterceptors(), configuration.getDns());
this.keyBackupServiceClients = createConnectionHolders(configuration.getSignalKeyBackupServiceUrls(), configuration.getNetworkInterceptors(), configuration.getDns());
this.storageClients = createConnectionHolders(configuration.getSignalStorageUrls(), configuration.getNetworkInterceptors(), configuration.getDns());
@ -517,8 +518,7 @@ public class PushServiceSocket {
}
public void retrieveAttachment(int cdnNumber, SignalServiceAttachmentRemoteId cdnPath, File destination, long maxSizeBytes, ProgressListener listener)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
throws NonSuccessfulResponseCodeException, PushNetworkException, MissingConfigurationException {
final String path;
if (cdnPath.getV2().isPresent()) {
path = String.format(Locale.US, ATTACHMENT_ID_DOWNLOAD_PATH, cdnPath.getV2().get());
@ -529,30 +529,35 @@ public class PushServiceSocket {
}
public void retrieveSticker(File destination, byte[] packId, int stickerId)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
throws NonSuccessfulResponseCodeException, PushNetworkException, MissingConfigurationException {
String hexPackId = Hex.toStringCondensed(packId);
downloadFromCdn(destination, 0, String.format(Locale.US, STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null);
}
public byte[] retrieveSticker(byte[] packId, int stickerId)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
throws NonSuccessfulResponseCodeException, PushNetworkException {
String hexPackId = Hex.toStringCondensed(packId);
ByteArrayOutputStream output = new ByteArrayOutputStream();
downloadFromCdn(output, 0, 0, String.format(Locale.US, STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null);
try {
downloadFromCdn(output, 0, 0, String.format(Locale.US, STICKER_PATH, hexPackId, stickerId), 1024 * 1024, null);
} catch (MissingConfigurationException e) {
throw new AssertionError(e);
}
return output.toByteArray();
}
public byte[] retrieveStickerManifest(byte[] packId)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
throws NonSuccessfulResponseCodeException, PushNetworkException {
String hexPackId = Hex.toStringCondensed(packId);
ByteArrayOutputStream output = new ByteArrayOutputStream();
downloadFromCdn(output, 0, 0, String.format(STICKER_MANIFEST_PATH, hexPackId), 1024 * 1024, null);
try {
downloadFromCdn(output, 0, 0, String.format(STICKER_MANIFEST_PATH, hexPackId), 1024 * 1024, null);
} catch (MissingConfigurationException e) {
throw new AssertionError(e);
}
return output.toByteArray();
}
@ -615,9 +620,12 @@ public class PushServiceSocket {
}
public void retrieveProfileAvatar(String path, File destination, long maxSizeBytes)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
downloadFromCdn(destination, 0, path, maxSizeBytes, null);
throws NonSuccessfulResponseCodeException, PushNetworkException {
try {
downloadFromCdn(destination, 0, path, maxSizeBytes, null);
} catch (MissingConfigurationException e) {
throw new AssertionError(e);
}
}
public void setProfileName(String name) throws NonSuccessfulResponseCodeException, PushNetworkException {
@ -646,7 +654,7 @@ public class PushServiceSocket {
}
if (profileAvatar != null) {
uploadToCdn(AVATAR_UPLOAD_PATH, formAttributes.getAcl(), formAttributes.getKey(),
uploadToCdn0(AVATAR_UPLOAD_PATH, formAttributes.getAcl(), formAttributes.getKey(),
formAttributes.getPolicy(), formAttributes.getAlgorithm(),
formAttributes.getCredential(), formAttributes.getDate(),
formAttributes.getSignature(), profileAvatar.getData(),
@ -682,7 +690,7 @@ public class PushServiceSocket {
throw new NonSuccessfulResponseCodeException("Unable to parse entity");
}
uploadToCdn(AVATAR_UPLOAD_PATH, formAttributes.getAcl(), formAttributes.getKey(),
uploadToCdn0(AVATAR_UPLOAD_PATH, formAttributes.getAcl(), formAttributes.getKey(),
formAttributes.getPolicy(), formAttributes.getAlgorithm(),
formAttributes.getCredential(), formAttributes.getDate(),
formAttributes.getSignature(), profileAvatar.getData(),
@ -896,7 +904,7 @@ public class PushServiceSocket {
public byte[] uploadGroupV2Avatar(byte[] avatarCipherText, AvatarUploadAttributes uploadAttributes)
throws IOException
{
return uploadToCdn(AVATAR_UPLOAD_PATH, uploadAttributes.getAcl(), uploadAttributes.getKey(),
return uploadToCdn0(AVATAR_UPLOAD_PATH, uploadAttributes.getAcl(), uploadAttributes.getKey(),
uploadAttributes.getPolicy(), uploadAttributes.getAlgorithm(),
uploadAttributes.getCredential(), uploadAttributes.getDate(),
uploadAttributes.getSignature(),
@ -910,7 +918,7 @@ public class PushServiceSocket {
throws PushNetworkException, NonSuccessfulResponseCodeException
{
long id = Long.parseLong(uploadAttributes.getAttachmentId());
byte[] digest = uploadToCdn(ATTACHMENT_UPLOAD_PATH, uploadAttributes.getAcl(), uploadAttributes.getKey(),
byte[] digest = uploadToCdn0(ATTACHMENT_UPLOAD_PATH, uploadAttributes.getAcl(), uploadAttributes.getKey(),
uploadAttributes.getPolicy(), uploadAttributes.getAlgorithm(),
uploadAttributes.getCredential(), uploadAttributes.getDate(),
uploadAttributes.getSignature(), attachment.getData(),
@ -933,8 +941,7 @@ public class PushServiceSocket {
}
private void downloadFromCdn(File destination, int cdnNumber, String path, long maxSizeBytes, ProgressListener listener)
throws PushNetworkException, NonSuccessfulResponseCodeException
{
throws PushNetworkException, NonSuccessfulResponseCodeException, MissingConfigurationException {
try (FileOutputStream outputStream = new FileOutputStream(destination, true)) {
downloadFromCdn(outputStream, destination.length(), cdnNumber, path, maxSizeBytes, listener);
} catch (IOException e) {
@ -943,14 +950,17 @@ public class PushServiceSocket {
}
private void downloadFromCdn(OutputStream outputStream, long offset, int cdnNumber, String path, long maxSizeBytes, ProgressListener listener)
throws PushNetworkException, NonSuccessfulResponseCodeException
{
ConnectionHolder connectionHolder = getRandom(cdnNumber == 2 ? cdn2Clients : cdnClients, random);
OkHttpClient okHttpClient = connectionHolder.getClient()
.newBuilder()
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
.readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
.build();
throws PushNetworkException, NonSuccessfulResponseCodeException, MissingConfigurationException {
ConnectionHolder[] cdnNumberClients = cdnClientsMap.get(cdnNumber);
if (cdnNumberClients == null) {
throw new MissingConfigurationException("Attempted to download from unsupported CDN number: " + cdnNumber + ", Our configuration supports: " + cdnClientsMap.keySet());
}
ConnectionHolder connectionHolder = getRandom(cdnNumberClients, random);
OkHttpClient okHttpClient = connectionHolder.getClient()
.newBuilder()
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
.readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
.build();
Request.Builder request = new Request.Builder().url(connectionHolder.getUrl() + "/" + path).get();
@ -1012,14 +1022,14 @@ public class PushServiceSocket {
throw new NonSuccessfulResponseCodeException("Response: " + response);
}
private byte[] uploadToCdn(String path, String acl, String key, String policy, String algorithm,
String credential, String date, String signature,
InputStream data, String contentType, long length,
OutputStreamFactory outputStreamFactory, ProgressListener progressListener,
CancelationSignal cancelationSignal)
private byte[] uploadToCdn0(String path, String acl, String key, String policy, String algorithm,
String credential, String date, String signature,
InputStream data, String contentType, long length,
OutputStreamFactory outputStreamFactory, ProgressListener progressListener,
CancelationSignal cancelationSignal)
throws PushNetworkException, NonSuccessfulResponseCodeException
{
ConnectionHolder connectionHolder = getRandom(cdnClients, random);
ConnectionHolder connectionHolder = getRandom(cdnClientsMap.get(0), random);
OkHttpClient okHttpClient = connectionHolder.getClient()
.newBuilder()
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
@ -1074,7 +1084,7 @@ public class PushServiceSocket {
}
private String getResumableUploadUrl(String signedUrl, Map<String, String> headers) throws IOException {
ConnectionHolder connectionHolder = getRandom(cdn2Clients, random);
ConnectionHolder connectionHolder = getRandom(cdnClientsMap.get(2), random);
OkHttpClient okHttpClient = connectionHolder.getClient()
.newBuilder()
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
@ -1135,7 +1145,7 @@ public class PushServiceSocket {
}
private byte[] uploadToCdn2(String resumableUrl, InputStream data, String contentType, long length, OutputStreamFactory outputStreamFactory, ProgressListener progressListener, CancelationSignal cancelationSignal) throws IOException {
ConnectionHolder connectionHolder = getRandom(cdn2Clients, random);
ConnectionHolder connectionHolder = getRandom(cdnClientsMap.get(2), random);
OkHttpClient okHttpClient = connectionHolder.getClient()
.newBuilder()
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
@ -1531,7 +1541,25 @@ public class PushServiceSocket {
return serviceConnectionHolders.toArray(new ServiceConnectionHolder[0]);
}
private ConnectionHolder[] createConnectionHolders(SignalUrl[] urls, List<Interceptor> interceptors, Optional<Dns> dns) {
private static Map<Integer, ConnectionHolder[]> createCdnClientsMap(final Map<Integer, SignalCdnUrl[]> signalCdnUrlMap,
final List<Interceptor> interceptors,
final Optional<Dns> dns) {
validateConfiguration(signalCdnUrlMap);
final Map<Integer, ConnectionHolder[]> result = new HashMap<>();
for (Map.Entry<Integer, SignalCdnUrl[]> entry : signalCdnUrlMap.entrySet()) {
result.put(entry.getKey(),
createConnectionHolders(entry.getValue(), interceptors, dns));
}
return Collections.unmodifiableMap(result);
}
private static void validateConfiguration(Map<Integer, SignalCdnUrl[]> signalCdnUrlMap) {
if (!signalCdnUrlMap.containsKey(0) || !signalCdnUrlMap.containsKey(2)) {
throw new AssertionError("Configuration used to create PushServiceSocket must support CDN 0 and CDN 2");
}
}
private static ConnectionHolder[] createConnectionHolders(SignalUrl[] urls, List<Interceptor> interceptors, Optional<Dns> dns) {
List<ConnectionHolder> connectionHolders = new LinkedList<>();
for (SignalUrl url : urls) {
@ -1541,7 +1569,7 @@ public class PushServiceSocket {
return connectionHolders.toArray(new ConnectionHolder[0]);
}
private OkHttpClient createConnectionClient(SignalUrl url, List<Interceptor> interceptors, Optional<Dns> dns) {
private static OkHttpClient createConnectionClient(SignalUrl url, List<Interceptor> interceptors, Optional<Dns> dns) {
try {
TrustManager[] trustManagers = BlacklistingTrustManager.createFor(url.getTrustStore());

View File

@ -4,33 +4,27 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
/**
* Exception that indicates that the data message has a higher required protocol version than the
* current client is capable of interpreting.
* Exception that indicates that the data message contains something that is not supported by this
* version of the application. Subclasses provide more specific information about what data was
* found that is not supported.
*/
public class UnsupportedDataMessageException extends Exception {
public abstract class UnsupportedDataMessageException extends Exception {
private final int requiredVersion;
private final String sender;
private final int senderDevice;
private final Optional<SignalServiceGroupContext> group;
public UnsupportedDataMessageException(int currentVersion,
int requiredVersion,
String sender,
int senderDevice,
Optional<SignalServiceGroupContext> group)
protected UnsupportedDataMessageException(String message,
String sender,
int senderDevice,
Optional<SignalServiceGroupContext> group)
{
super("Required version: " + requiredVersion + ", Our version: " + currentVersion);
this.requiredVersion = requiredVersion;
super(message);
this.sender = sender;
this.senderDevice = senderDevice;
this.group = group;
}
public int getRequiredVersion() {
return requiredVersion;
}
public String getSender() {
return sender;
}

View File

@ -0,0 +1,25 @@
package org.whispersystems.signalservice.internal.push;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
/**
* Exception that indicates that the data message has a higher required protocol version than the
* current client is capable of interpreting.
*/
public final class UnsupportedDataMessageProtocolVersionException extends UnsupportedDataMessageException {
private final int requiredVersion;
public UnsupportedDataMessageProtocolVersionException(int currentVersion,
int requiredVersion,
String sender,
int senderDevice,
Optional<SignalServiceGroupContext> group) {
super("Required version: " + requiredVersion + ", Our version: " + currentVersion, sender, senderDevice, group);
this.requiredVersion = requiredVersion;
}
public int getRequiredVersion() {
return requiredVersion;
}
}