Correct envelope types for certain iOS builds
This commit is contained in:
parent
11f1cf80bd
commit
1461bcc2c2
|
@ -14,6 +14,7 @@ import com.codahale.metrics.Timer;
|
||||||
import com.codahale.metrics.annotation.Timed;
|
import com.codahale.metrics.annotation.Timed;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.vdurmont.semver4j.Semver;
|
||||||
import io.dropwizard.auth.Auth;
|
import io.dropwizard.auth.Auth;
|
||||||
import io.dropwizard.util.DataSize;
|
import io.dropwizard.util.DataSize;
|
||||||
import io.micrometer.core.instrument.Counter;
|
import io.micrometer.core.instrument.Counter;
|
||||||
|
@ -42,6 +43,7 @@ import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -96,7 +98,9 @@ import org.whispersystems.textsecuregcm.storage.ReportMessageManager;
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
import org.whispersystems.textsecuregcm.util.Pair;
|
import org.whispersystems.textsecuregcm.util.Pair;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
import org.whispersystems.textsecuregcm.util.ua.ClientPlatform;
|
||||||
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
|
import org.whispersystems.textsecuregcm.util.ua.UnrecognizedUserAgentException;
|
||||||
|
import org.whispersystems.textsecuregcm.util.ua.UserAgent;
|
||||||
import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
|
import org.whispersystems.textsecuregcm.util.ua.UserAgentUtil;
|
||||||
import org.whispersystems.textsecuregcm.websocket.WebSocketConnection;
|
import org.whispersystems.textsecuregcm.websocket.WebSocketConnection;
|
||||||
|
|
||||||
|
@ -124,6 +128,12 @@ public class MessageController {
|
||||||
private final ReportMessageManager reportMessageManager;
|
private final ReportMessageManager reportMessageManager;
|
||||||
private final ExecutorService multiRecipientMessageExecutor;
|
private final ExecutorService multiRecipientMessageExecutor;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final Semver FIRST_IOS_VERSION_WITH_INCORRECT_ENVELOPE_TYPE = new Semver("5.22.0.32");
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final Semver IOS_VERSION_WITH_FIXED_ENVELOPE_TYPE = new Semver("5.25.0.0");
|
||||||
|
|
||||||
private static final String LEGACY_MESSAGE_SENT_COUNTER = name(MessageController.class, "legacyMessageSent");
|
private static final String LEGACY_MESSAGE_SENT_COUNTER = name(MessageController.class, "legacyMessageSent");
|
||||||
private static final String SENT_MESSAGE_COUNTER_NAME = name(MessageController.class, "sentMessages");
|
private static final String SENT_MESSAGE_COUNTER_NAME = name(MessageController.class, "sentMessages");
|
||||||
private static final String CONTENT_SIZE_DISTRIBUTION_NAME = name(MessageController.class, "messageContentSize");
|
private static final String CONTENT_SIZE_DISTRIBUTION_NAME = name(MessageController.class, "messageContentSize");
|
||||||
|
@ -269,7 +279,7 @@ public class MessageController {
|
||||||
if (destinationDevice.isPresent()) {
|
if (destinationDevice.isPresent()) {
|
||||||
Metrics.counter(SENT_MESSAGE_COUNTER_NAME, tags).increment();
|
Metrics.counter(SENT_MESSAGE_COUNTER_NAME, tags).increment();
|
||||||
sendMessage(source, destination.get(), destinationDevice.get(), messages.getTimestamp(), messages.isOnline(),
|
sendMessage(source, destination.get(), destinationDevice.get(), messages.getTimestamp(), messages.isOnline(),
|
||||||
incomingMessage);
|
incomingMessage, userAgent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,14 +524,38 @@ public class MessageController {
|
||||||
Device destinationDevice,
|
Device destinationDevice,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
boolean online,
|
boolean online,
|
||||||
IncomingMessage incomingMessage)
|
IncomingMessage incomingMessage,
|
||||||
|
String userAgentString)
|
||||||
throws NoSuchUserException {
|
throws NoSuchUserException {
|
||||||
try (final Timer.Context ignored = sendMessageInternalTimer.time()) {
|
try (final Timer.Context ignored = sendMessageInternalTimer.time()) {
|
||||||
Optional<byte[]> messageBody = getMessageBody(incomingMessage);
|
Optional<byte[]> messageBody = getMessageBody(incomingMessage);
|
||||||
Optional<byte[]> messageContent = getMessageContent(incomingMessage);
|
Optional<byte[]> messageContent = getMessageContent(incomingMessage);
|
||||||
Envelope.Builder messageBuilder = Envelope.newBuilder();
|
Envelope.Builder messageBuilder = Envelope.newBuilder();
|
||||||
|
|
||||||
messageBuilder.setType(Envelope.Type.forNumber(incomingMessage.getType()))
|
int envelopeTypeNumber = incomingMessage.getType();
|
||||||
|
|
||||||
|
// Some versions of the iOS app incorrectly use the reserved envelope type 7 for PLAINTEXT_CONTENT instead of type
|
||||||
|
// 8. This check can be removed safely after 2022-03-01.
|
||||||
|
if (envelopeTypeNumber == 7) {
|
||||||
|
try {
|
||||||
|
final UserAgent userAgent = UserAgentUtil.parseUserAgentString(userAgentString);
|
||||||
|
if (userAgent.getPlatform() == ClientPlatform.IOS &&
|
||||||
|
userAgent.getVersion().isGreaterThanOrEqualTo(FIRST_IOS_VERSION_WITH_INCORRECT_ENVELOPE_TYPE) &&
|
||||||
|
userAgent.getVersion().isLowerThan(IOS_VERSION_WITH_FIXED_ENVELOPE_TYPE)) {
|
||||||
|
envelopeTypeNumber = Type.PLAINTEXT_CONTENT.getNumber();
|
||||||
|
}
|
||||||
|
} catch (final UnrecognizedUserAgentException ignored2) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Envelope.Type envelopeType = Envelope.Type.forNumber(envelopeTypeNumber);
|
||||||
|
|
||||||
|
if (envelopeType == null) {
|
||||||
|
logger.warn("Received bad envelope type {} from {}", incomingMessage.getType(), userAgentString);
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
|
messageBuilder.setType(envelopeType)
|
||||||
.setTimestamp(timestamp == 0 ? System.currentTimeMillis() : timestamp)
|
.setTimestamp(timestamp == 0 ? System.currentTimeMillis() : timestamp)
|
||||||
.setServerTimestamp(System.currentTimeMillis());
|
.setServerTimestamp(System.currentTimeMillis());
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,38 @@ class MessageControllerTest {
|
||||||
assertTrue(captor.getValue().hasSourceDevice());
|
assertTrue(captor.getValue().hasSourceDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource
|
||||||
|
void testSingleDeviceCurrentBadType(final String userAgentString, final boolean expectAcceptMessage) throws Exception {
|
||||||
|
Response response =
|
||||||
|
resources.getJerseyTest()
|
||||||
|
.target(String.format("/v1/messages/%s", SINGLE_DEVICE_UUID))
|
||||||
|
.request()
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
|
||||||
|
.header("User-Agent", userAgentString)
|
||||||
|
.put(Entity.entity(mapper.readValue(jsonFixture("fixtures/current_message_single_device_bad_type.json"), IncomingMessageList.class),
|
||||||
|
MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
|
if (expectAcceptMessage) {
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
|
||||||
|
final ArgumentCaptor<Envelope> captor = ArgumentCaptor.forClass(Envelope.class);
|
||||||
|
verify(messageSender).sendMessage(any(Account.class), any(Device.class), captor.capture(), eq(false));
|
||||||
|
} else {
|
||||||
|
assertEquals(400, response.getStatus());
|
||||||
|
verify(messageSender, never()).sendMessage(any(), any(), any(), anyBoolean());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> testSingleDeviceCurrentBadType() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(String.format("Signal-iOS/%s iOS/14.2", MessageController.FIRST_IOS_VERSION_WITH_INCORRECT_ENVELOPE_TYPE), true),
|
||||||
|
Arguments.of(String.format("Signal-iOS/%s iOS/14.2", MessageController.FIRST_IOS_VERSION_WITH_INCORRECT_ENVELOPE_TYPE.nextPatch()), true),
|
||||||
|
Arguments.of(String.format("Signal-iOS/%s iOS/14.2", MessageController.IOS_VERSION_WITH_FIXED_ENVELOPE_TYPE.withIncMinor(-1)), true),
|
||||||
|
Arguments.of(String.format("Signal-iOS/%s iOS/14.2", MessageController.IOS_VERSION_WITH_FIXED_ENVELOPE_TYPE), false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testNullMessageInList() throws Exception {
|
void testNullMessageInList() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"messages" : [{
|
||||||
|
"type" : 7,
|
||||||
|
"destinationDeviceId" : 1,
|
||||||
|
"body" : "Zm9vYmFyego",
|
||||||
|
"timestamp" : 1234
|
||||||
|
}]
|
||||||
|
}
|
Loading…
Reference in New Issue