1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE; 20 import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED; 21 import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND; 22 import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_CONNECTED; 23 import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED; 24 import static android.system.OsConstants.IPPROTO_TCP; 25 import static android.system.OsConstants.IPPROTO_UDP; 26 import static android.system.OsConstants.SOCK_DGRAM; 27 import static android.system.OsConstants.SOCK_STREAM; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.os.ParcelFileDescriptor; 32 import android.system.ErrnoException; 33 import android.system.Os; 34 import android.util.Log; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 38 import java.io.FileDescriptor; 39 import java.net.InetAddress; 40 import java.net.InetSocketAddress; 41 import java.net.Socket; 42 import java.net.SocketAddress; 43 import java.util.Objects; 44 45 /** 46 * Filters a {@link QosSession} according to the binding on the provided {@link Socket}. 47 * 48 * @hide 49 */ 50 public class QosSocketFilter extends QosFilter { 51 52 private static final String TAG = QosSocketFilter.class.getSimpleName(); 53 54 @NonNull 55 private final QosSocketInfo mQosSocketInfo; 56 57 /** 58 * Creates a {@link QosSocketFilter} based off of {@link QosSocketInfo}. 59 * 60 * @param qosSocketInfo the information required to filter and validate 61 */ QosSocketFilter(@onNull final QosSocketInfo qosSocketInfo)62 public QosSocketFilter(@NonNull final QosSocketInfo qosSocketInfo) { 63 Objects.requireNonNull(qosSocketInfo, "qosSocketInfo must be non-null"); 64 mQosSocketInfo = qosSocketInfo; 65 } 66 67 /** 68 * Gets the parcelable qos socket info that was used to create the filter. 69 */ 70 @NonNull getQosSocketInfo()71 public QosSocketInfo getQosSocketInfo() { 72 return mQosSocketInfo; 73 } 74 75 /** 76 * Performs two validations: 77 * 1. If the socket is not bound, then return 78 * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND}. This is detected 79 * by checking the local address on the filter which becomes null when the socket is no 80 * longer bound. 81 * 2. In the scenario that the socket is now bound to a different local address, which can 82 * happen in the case of UDP, then 83 * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned. 84 * 3. In the scenario that the UDP socket changed remote address, then 85 * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED} is returned. 86 * 87 * @return validation error code 88 */ 89 @Override validate()90 public int validate() { 91 final InetSocketAddress sa = getLocalAddressFromFileDescriptor(); 92 93 if (sa == null || (sa.getAddress().isAnyLocalAddress() && sa.getPort() == 0)) { 94 return EX_TYPE_FILTER_SOCKET_NOT_BOUND; 95 } 96 97 if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) { 98 return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED; 99 } 100 101 if (mQosSocketInfo.getRemoteSocketAddress() != null) { 102 final InetSocketAddress da = getRemoteAddressFromFileDescriptor(); 103 if (da == null) { 104 return EX_TYPE_FILTER_SOCKET_NOT_CONNECTED; 105 } 106 107 if (!da.equals(mQosSocketInfo.getRemoteSocketAddress())) { 108 return EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED; 109 } 110 } 111 112 return EX_TYPE_FILTER_NONE; 113 } 114 115 /** 116 * The local address of the socket's binding. 117 * 118 * Note: If the socket is no longer bound, null is returned. 119 * 120 * @return the local address 121 */ 122 @Nullable getLocalAddressFromFileDescriptor()123 private InetSocketAddress getLocalAddressFromFileDescriptor() { 124 final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor(); 125 final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor(); 126 127 final SocketAddress address; 128 try { 129 address = Os.getsockname(fd); 130 } catch (ErrnoException e) { 131 Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e); 132 return null; 133 } 134 if (address instanceof InetSocketAddress) { 135 return (InetSocketAddress) address; 136 } 137 return null; 138 } 139 140 /** 141 * The remote address of the socket's connected. 142 * 143 * <p>Note: If the socket is no longer connected, null is returned. 144 * 145 * @return the remote address 146 */ 147 @Nullable getRemoteAddressFromFileDescriptor()148 private InetSocketAddress getRemoteAddressFromFileDescriptor() { 149 final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor(); 150 final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor(); 151 152 final SocketAddress address; 153 try { 154 address = Os.getpeername(fd); 155 } catch (ErrnoException e) { 156 Log.e(TAG, "getAddressFromFileDescriptor: getRemoteAddress exception", e); 157 return null; 158 } 159 if (address instanceof InetSocketAddress) { 160 return (InetSocketAddress) address; 161 } 162 return null; 163 } 164 165 /** 166 * The network used with this filter. 167 * 168 * @return the registered {@link Network} 169 */ 170 @NonNull 171 @Override getNetwork()172 public Network getNetwork() { 173 return mQosSocketInfo.getNetwork(); 174 } 175 176 /** 177 * @inheritDoc 178 */ 179 @Override matchesLocalAddress(@onNull final InetAddress address, final int startPort, final int endPort)180 public boolean matchesLocalAddress(@NonNull final InetAddress address, final int startPort, 181 final int endPort) { 182 if (mQosSocketInfo.getLocalSocketAddress() == null) { 183 return false; 184 } 185 return matchesAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort, 186 endPort); 187 } 188 189 /** 190 * @inheritDoc 191 */ 192 @Override matchesRemoteAddress(@onNull final InetAddress address, final int startPort, final int endPort)193 public boolean matchesRemoteAddress(@NonNull final InetAddress address, final int startPort, 194 final int endPort) { 195 if (mQosSocketInfo.getRemoteSocketAddress() == null) { 196 return false; 197 } 198 return matchesAddress(mQosSocketInfo.getRemoteSocketAddress(), address, startPort, 199 endPort); 200 } 201 202 /** 203 * @inheritDoc 204 */ 205 @Override matchesProtocol(final int protocol)206 public boolean matchesProtocol(final int protocol) { 207 if ((mQosSocketInfo.getSocketType() == SOCK_STREAM && protocol == IPPROTO_TCP) 208 || (mQosSocketInfo.getSocketType() == SOCK_DGRAM && protocol == IPPROTO_UDP)) { 209 return true; 210 } 211 return false; 212 } 213 214 /** 215 * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} 216 * and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the 217 * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}. 218 * <p> 219 * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked 220 * due to being final. 221 * 222 * @param filterSocketAddress the socket address of the filter 223 * @param address the address to compare the filterSocketAddressWith 224 * @param startPort the start of the port range to check 225 * @param endPort the end of the port range to check 226 */ 227 @VisibleForTesting matchesAddress(@onNull final InetSocketAddress filterSocketAddress, @NonNull final InetAddress address, final int startPort, final int endPort)228 public static boolean matchesAddress(@NonNull final InetSocketAddress filterSocketAddress, 229 @NonNull final InetAddress address, 230 final int startPort, final int endPort) { 231 return startPort <= filterSocketAddress.getPort() 232 && endPort >= filterSocketAddress.getPort() 233 && (address.isAnyLocalAddress() 234 || filterSocketAddress.getAddress().equals(address)); 235 } 236 } 237