1 /*
2  * Copyright (C) 2017 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 com.android.bips.p2p;
18 
19 import android.net.Uri;
20 
21 import com.android.bips.BuiltInPrintService;
22 import com.android.bips.discovery.DiscoveredPrinter;
23 import com.android.bips.discovery.P2pDiscovery;
24 
25 import java.net.Inet4Address;
26 import java.net.InetAddress;
27 import java.net.InterfaceAddress;
28 import java.net.NetworkInterface;
29 import java.net.SocketException;
30 import java.net.UnknownHostException;
31 import java.util.BitSet;
32 import java.util.regex.Pattern;
33 
34 /**
35  * Provide tools for conversions and querying P2P status
36  */
37 public class P2pUtils {
38     private static final Pattern IPV4_PATTERN =
39             Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}"
40             + "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
41 
42     /** Return true if path is known to be on the named network interface */
isOnInterface(String interfaceName, Uri path)43     static boolean isOnInterface(String interfaceName, Uri path) {
44         NetworkInterface networkInterface = toNetworkInterface(interfaceName);
45         Inet4Address inet4Address = toInet4Address(path);
46         return networkInterface != null && inet4Address != null
47                 && isOnInterface(networkInterface, inet4Address);
48     }
49 
50     /** If possible, find the x.x.x.x host part of path, without performing any name resolution */
toInet4Address(Uri path)51     private static Inet4Address toInet4Address(Uri path) {
52         if (!IPV4_PATTERN.matcher(path.getHost()).find()) {
53             return null;
54         }
55         try {
56             return (Inet4Address) InetAddress.getByName(path.getHost());
57         } catch (UnknownHostException ignored) {
58         }
59         return null;
60     }
61 
62     /** Return {@link NetworkInterface} corresponding to the supplied name, or null */
toNetworkInterface(String name)63     private static NetworkInterface toNetworkInterface(String name) {
64         if (name == null) {
65             return null;
66         }
67         try {
68             return NetworkInterface.getByName(name);
69         } catch (SocketException e) {
70             return null;
71         }
72     }
73 
74     /**
75      * Return true if the printer's path is P2P
76      */
isP2p(DiscoveredPrinter printer)77     public static boolean isP2p(DiscoveredPrinter printer) {
78         return printer.path.getScheme().equals(P2pDiscovery.SCHEME_P2P);
79     }
80 
81     /**
82      * Return true if the printer's path is on the currently connected P2P interface
83      */
isOnConnectedInterface(BuiltInPrintService service, DiscoveredPrinter printer)84     public static boolean isOnConnectedInterface(BuiltInPrintService service,
85             DiscoveredPrinter printer) {
86         String connectedInterface = service.getP2pMonitor().getConnectedInterface();
87         return connectedInterface != null
88                 && P2pUtils.isOnInterface(connectedInterface, printer.path);
89     }
90 
91     /** Return true if the supplied remote address is on the network interface */
isOnInterface(NetworkInterface iface, Inet4Address address)92     static boolean isOnInterface(NetworkInterface iface, Inet4Address address) {
93         long addressLong = toLong(address);
94         for (InterfaceAddress ifaceAddress : iface.getInterfaceAddresses()) {
95             if (!(ifaceAddress.getAddress() instanceof Inet4Address)) {
96                 continue;
97             }
98             Inet4Address networkAddress = (Inet4Address) ifaceAddress.getAddress();
99 
100             BitSet bitSet = new BitSet(32);
101             bitSet.set(32 - ifaceAddress.getNetworkPrefixLength(), 32);
102             long netMask = bitSet.toLongArray()[0];
103 
104             if ((toLong(networkAddress) & netMask) == (addressLong & netMask)) {
105                 return true;
106             }
107         }
108         return false;
109     }
110 
toLong(Inet4Address address)111     private static long toLong(Inet4Address address) {
112         byte[] bytes = address.getAddress();
113         return ((bytes[0] & 0xFFL) << 24) + ((bytes[1] & 0xFFL) << 16)
114                 + ((bytes[2] & 0xFFL) << 8) + (bytes[3] & 0xFFL);
115     }
116 }
117