Support QosCallback for UDP socket -Filter
Add matchProtocol() to QosFilter. Add remoteAddress check to validate(). Add exception EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED Don't send ParcelFileDescriptor to QosProvider for security reason Bug: 203146631 Test: atest & verified on LTE test equipment Change-Id: I7102ae9ba7cb9e1cc8d06b252aad4dad75860f3e
This commit is contained in:
parent
82657d70d7
commit
afc22b02e2
7 changed files with 187 additions and 31 deletions
|
|
@ -46,8 +46,10 @@ public final class QosCallbackException extends Exception {
|
|||
EX_TYPE_FILTER_NONE,
|
||||
EX_TYPE_FILTER_NETWORK_RELEASED,
|
||||
EX_TYPE_FILTER_SOCKET_NOT_BOUND,
|
||||
EX_TYPE_FILTER_SOCKET_NOT_CONNECTED,
|
||||
EX_TYPE_FILTER_NOT_SUPPORTED,
|
||||
EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED,
|
||||
EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ExceptionType {}
|
||||
|
|
@ -65,10 +67,16 @@ public final class QosCallbackException extends Exception {
|
|||
public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
|
||||
public static final int EX_TYPE_FILTER_SOCKET_NOT_CONNECTED = 3;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
|
||||
public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 4;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 5;
|
||||
|
||||
/** {@hide} */
|
||||
public static final int EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED = 6;
|
||||
|
||||
/**
|
||||
* Creates exception based off of a type and message. Not all types of exceptions accept a
|
||||
|
|
@ -83,12 +91,17 @@ public final class QosCallbackException extends Exception {
|
|||
return new QosCallbackException(new NetworkReleasedException());
|
||||
case EX_TYPE_FILTER_SOCKET_NOT_BOUND:
|
||||
return new QosCallbackException(new SocketNotBoundException());
|
||||
case EX_TYPE_FILTER_SOCKET_NOT_CONNECTED:
|
||||
return new QosCallbackException(new SocketNotConnectedException());
|
||||
case EX_TYPE_FILTER_NOT_SUPPORTED:
|
||||
return new QosCallbackException(new UnsupportedOperationException(
|
||||
"This device does not support the specified filter"));
|
||||
case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED:
|
||||
return new QosCallbackException(
|
||||
new SocketLocalAddressChangedException());
|
||||
case EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED:
|
||||
return new QosCallbackException(
|
||||
new SocketRemoteAddressChangedException());
|
||||
default:
|
||||
Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'");
|
||||
return new QosCallbackException(
|
||||
|
|
|
|||
|
|
@ -90,5 +90,15 @@ public abstract class QosFilter {
|
|||
*/
|
||||
public abstract boolean matchesRemoteAddress(@NonNull InetAddress address,
|
||||
int startPort, int endPort);
|
||||
|
||||
/**
|
||||
* Determines whether or not the parameter will be matched with this filter.
|
||||
*
|
||||
* @param protocol the protocol such as TCP or UDP included in IP packet filter set of a QoS
|
||||
* flow assigned on {@link Network}.
|
||||
* @return whether the parameters match the socket type of the filter
|
||||
* @hide
|
||||
*/
|
||||
public abstract boolean matchesProtocol(int protocol);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public final class QosFilterParcelable implements Parcelable {
|
|||
if (mQosFilter instanceof QosSocketFilter) {
|
||||
dest.writeInt(QOS_SOCKET_FILTER);
|
||||
final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter;
|
||||
qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0);
|
||||
qosSocketFilter.getQosSocketInfo().writeToParcelWithoutFd(dest, 0);
|
||||
return;
|
||||
}
|
||||
dest.writeInt(NO_FILTER_PRESENT);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ package android.net;
|
|||
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
|
||||
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
|
||||
import static android.system.OsConstants.IPPROTO_TCP;
|
||||
import static android.system.OsConstants.IPPROTO_UDP;
|
||||
import static android.system.OsConstants.SOCK_DGRAM;
|
||||
import static android.system.OsConstants.SOCK_STREAM;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
|
@ -74,19 +81,34 @@ public class QosSocketFilter extends QosFilter {
|
|||
* 2. In the scenario that the socket is now bound to a different local address, which can
|
||||
* happen in the case of UDP, then
|
||||
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned.
|
||||
* 3. In the scenario that the UDP socket changed remote address, then
|
||||
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED} is returned.
|
||||
*
|
||||
* @return validation error code
|
||||
*/
|
||||
@Override
|
||||
public int validate() {
|
||||
final InetSocketAddress sa = getAddressFromFileDescriptor();
|
||||
if (sa == null) {
|
||||
return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
|
||||
final InetSocketAddress sa = getLocalAddressFromFileDescriptor();
|
||||
|
||||
if (sa == null || (sa.getAddress().isAnyLocalAddress() && sa.getPort() == 0)) {
|
||||
return EX_TYPE_FILTER_SOCKET_NOT_BOUND;
|
||||
}
|
||||
|
||||
if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) {
|
||||
return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
|
||||
}
|
||||
|
||||
if (mQosSocketInfo.getRemoteSocketAddress() != null) {
|
||||
final InetSocketAddress da = getRemoteAddressFromFileDescriptor();
|
||||
if (da == null) {
|
||||
return EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (!da.equals(mQosSocketInfo.getRemoteSocketAddress())) {
|
||||
return EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
return EX_TYPE_FILTER_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -98,17 +120,14 @@ public class QosSocketFilter extends QosFilter {
|
|||
* @return the local address
|
||||
*/
|
||||
@Nullable
|
||||
private InetSocketAddress getAddressFromFileDescriptor() {
|
||||
private InetSocketAddress getLocalAddressFromFileDescriptor() {
|
||||
final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
|
||||
if (parcelFileDescriptor == null) return null;
|
||||
|
||||
final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
|
||||
if (fd == null) return null;
|
||||
|
||||
final SocketAddress address;
|
||||
try {
|
||||
address = Os.getsockname(fd);
|
||||
} catch (final ErrnoException e) {
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -118,6 +137,31 @@ public class QosSocketFilter extends QosFilter {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The remote address of the socket's connected.
|
||||
*
|
||||
* <p>Note: If the socket is no longer connected, null is returned.
|
||||
*
|
||||
* @return the remote address
|
||||
*/
|
||||
@Nullable
|
||||
private InetSocketAddress getRemoteAddressFromFileDescriptor() {
|
||||
final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
|
||||
final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
|
||||
|
||||
final SocketAddress address;
|
||||
try {
|
||||
address = Os.getpeername(fd);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "getAddressFromFileDescriptor: getRemoteAddress exception", e);
|
||||
return null;
|
||||
}
|
||||
if (address instanceof InetSocketAddress) {
|
||||
return (InetSocketAddress) address;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The network used with this filter.
|
||||
*
|
||||
|
|
@ -155,6 +199,18 @@ public class QosSocketFilter extends QosFilter {
|
|||
endPort);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public boolean matchesProtocol(final int protocol) {
|
||||
if ((mQosSocketInfo.getSocketType() == SOCK_STREAM && protocol == IPPROTO_TCP)
|
||||
|| (mQosSocketInfo.getSocketType() == SOCK_DGRAM && protocol == IPPROTO_UDP)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)}
|
||||
* and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the
|
||||
|
|
@ -174,6 +230,7 @@ public class QosSocketFilter extends QosFilter {
|
|||
final int startPort, final int endPort) {
|
||||
return startPort <= filterSocketAddress.getPort()
|
||||
&& endPort >= filterSocketAddress.getPort()
|
||||
&& filterSocketAddress.getAddress().equals(address);
|
||||
&& (address.isAnyLocalAddress()
|
||||
|| filterSocketAddress.getAddress().equals(address));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,25 +165,28 @@ public final class QosSocketInfo implements Parcelable {
|
|||
/* Parcelable methods */
|
||||
private QosSocketInfo(final Parcel in) {
|
||||
mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
|
||||
mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
|
||||
final boolean withFd = in.readBoolean();
|
||||
if (withFd) {
|
||||
mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
|
||||
} else {
|
||||
mParcelFileDescriptor = null;
|
||||
}
|
||||
|
||||
final int localAddressLength = in.readInt();
|
||||
mLocalSocketAddress = readSocketAddress(in, localAddressLength);
|
||||
|
||||
final int remoteAddressLength = in.readInt();
|
||||
mRemoteSocketAddress = remoteAddressLength == 0 ? null
|
||||
: readSocketAddress(in, remoteAddressLength);
|
||||
mLocalSocketAddress = readSocketAddress(in);
|
||||
mRemoteSocketAddress = readSocketAddress(in);
|
||||
|
||||
mSocketType = in.readInt();
|
||||
}
|
||||
|
||||
private @NonNull InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
|
||||
final byte[] address = new byte[addressLength];
|
||||
in.readByteArray(address);
|
||||
private InetSocketAddress readSocketAddress(final Parcel in) {
|
||||
final byte[] addrBytes = in.createByteArray();
|
||||
if (addrBytes == null) {
|
||||
return null;
|
||||
}
|
||||
final int port = in.readInt();
|
||||
|
||||
try {
|
||||
return new InetSocketAddress(InetAddress.getByAddress(address), port);
|
||||
return new InetSocketAddress(InetAddress.getByAddress(addrBytes), port);
|
||||
} catch (final UnknownHostException e) {
|
||||
/* This can never happen. UnknownHostException will never be thrown
|
||||
since the address provided is numeric and non-null. */
|
||||
|
|
@ -198,20 +201,35 @@ public final class QosSocketInfo implements Parcelable {
|
|||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
|
||||
mNetwork.writeToParcel(dest, 0);
|
||||
mParcelFileDescriptor.writeToParcel(dest, 0);
|
||||
writeToParcelInternal(dest, flags, /*includeFd=*/ true);
|
||||
}
|
||||
|
||||
final byte[] localAddress = mLocalSocketAddress.getAddress().getAddress();
|
||||
dest.writeInt(localAddress.length);
|
||||
dest.writeByteArray(localAddress);
|
||||
/**
|
||||
* Used when sending QosSocketInfo to telephony, which does not need access to the socket FD.
|
||||
* @hide
|
||||
*/
|
||||
public void writeToParcelWithoutFd(@NonNull final Parcel dest, final int flags) {
|
||||
writeToParcelInternal(dest, flags, /*includeFd=*/ false);
|
||||
}
|
||||
|
||||
private void writeToParcelInternal(
|
||||
@NonNull final Parcel dest, final int flags, boolean includeFd) {
|
||||
mNetwork.writeToParcel(dest, 0);
|
||||
|
||||
if (includeFd) {
|
||||
dest.writeBoolean(true);
|
||||
mParcelFileDescriptor.writeToParcel(dest, 0);
|
||||
} else {
|
||||
dest.writeBoolean(false);
|
||||
}
|
||||
|
||||
dest.writeByteArray(mLocalSocketAddress.getAddress().getAddress());
|
||||
dest.writeInt(mLocalSocketAddress.getPort());
|
||||
|
||||
if (mRemoteSocketAddress == null) {
|
||||
dest.writeInt(0);
|
||||
dest.writeByteArray(null);
|
||||
} else {
|
||||
final byte[] remoteAddress = mRemoteSocketAddress.getAddress().getAddress();
|
||||
dest.writeInt(remoteAddress.length);
|
||||
dest.writeByteArray(remoteAddress);
|
||||
dest.writeByteArray(mRemoteSocketAddress.getAddress().getAddress());
|
||||
dest.writeInt(mRemoteSocketAddress.getPort());
|
||||
}
|
||||
dest.writeInt(mSocketType);
|
||||
|
|
|
|||
29
framework/src/android/net/SocketNotConnectedException.java
Normal file
29
framework/src/android/net/SocketNotConnectedException.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
/**
|
||||
* Thrown when a previously bound socket becomes unbound.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SocketNotConnectedException extends Exception {
|
||||
/** @hide */
|
||||
public SocketNotConnectedException() {
|
||||
super("The socket is not connected");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
/**
|
||||
* Thrown when the local address of the socket has changed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SocketRemoteAddressChangedException extends Exception {
|
||||
/** @hide */
|
||||
public SocketRemoteAddressChangedException() {
|
||||
super("The remote address of the socket changed");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue