1 /* 2 * Copyright (C) 2021 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.server.connectivity.mdns; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.Network; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.net.module.util.SharedLog; 25 26 import java.io.IOException; 27 import java.net.DatagramPacket; 28 import java.net.InetSocketAddress; 29 import java.net.MulticastSocket; 30 import java.net.SocketException; 31 import java.util.List; 32 33 /** 34 * {@link MdnsSocket} provides a similar interface to {@link MulticastSocket} and binds to all 35 * available multi-cast network interfaces. 36 * 37 * @see MulticastSocket for javadoc of each public method. 38 */ 39 public class MdnsSocket { 40 static final int INTERFACE_INDEX_UNSPECIFIED = -1; 41 public static final InetSocketAddress MULTICAST_IPV4_ADDRESS = 42 new InetSocketAddress(MdnsConstants.getMdnsIPv4Address(), MdnsConstants.MDNS_PORT); 43 public static final InetSocketAddress MULTICAST_IPV6_ADDRESS = 44 new InetSocketAddress(MdnsConstants.getMdnsIPv6Address(), MdnsConstants.MDNS_PORT); 45 private final MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider; 46 private final MulticastSocket multicastSocket; 47 private boolean isOnIPv6OnlyNetwork; 48 private final SharedLog sharedLog; 49 MdnsSocket( @onNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, int port, SharedLog sharedLog)50 public MdnsSocket( 51 @NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, int port, 52 SharedLog sharedLog) 53 throws IOException { 54 this(multicastNetworkInterfaceProvider, new MulticastSocket(port), sharedLog); 55 } 56 57 @VisibleForTesting MdnsSocket(@onNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, MulticastSocket multicastSocket, SharedLog sharedLog)58 MdnsSocket(@NonNull MulticastNetworkInterfaceProvider multicastNetworkInterfaceProvider, 59 MulticastSocket multicastSocket, SharedLog sharedLog) throws IOException { 60 this.multicastNetworkInterfaceProvider = multicastNetworkInterfaceProvider; 61 this.multicastSocket = multicastSocket; 62 this.sharedLog = sharedLog; 63 // RFC Spec: https://tools.ietf.org/html/rfc6762 64 // Time to live is set 255, which is similar to the jMDNS implementation. 65 multicastSocket.setTimeToLive(255); 66 67 // TODO (changed when importing code): consider tagging the socket for data usage 68 isOnIPv6OnlyNetwork = false; 69 } 70 send(DatagramPacket packet)71 public void send(DatagramPacket packet) throws IOException { 72 List<NetworkInterfaceWrapper> networkInterfaces = 73 multicastNetworkInterfaceProvider.getMulticastNetworkInterfaces(); 74 for (NetworkInterfaceWrapper networkInterface : networkInterfaces) { 75 multicastSocket.setNetworkInterface(networkInterface.getNetworkInterface()); 76 multicastSocket.send(packet); 77 } 78 } 79 receive(DatagramPacket packet)80 public void receive(DatagramPacket packet) throws IOException { 81 multicastSocket.receive(packet); 82 } 83 joinGroup()84 public void joinGroup() throws IOException { 85 List<NetworkInterfaceWrapper> networkInterfaces = 86 multicastNetworkInterfaceProvider.getMulticastNetworkInterfaces(); 87 InetSocketAddress multicastAddress = MULTICAST_IPV4_ADDRESS; 88 if (multicastNetworkInterfaceProvider.isOnIpV6OnlyNetwork(networkInterfaces)) { 89 isOnIPv6OnlyNetwork = true; 90 multicastAddress = MULTICAST_IPV6_ADDRESS; 91 } else { 92 isOnIPv6OnlyNetwork = false; 93 } 94 for (NetworkInterfaceWrapper networkInterface : networkInterfaces) { 95 multicastSocket.joinGroup(multicastAddress, networkInterface.getNetworkInterface()); 96 if (!isOnIPv6OnlyNetwork) { 97 multicastSocket.joinGroup( 98 MULTICAST_IPV6_ADDRESS, networkInterface.getNetworkInterface()); 99 } 100 } 101 } 102 leaveGroup()103 public void leaveGroup() throws IOException { 104 List<NetworkInterfaceWrapper> networkInterfaces = 105 multicastNetworkInterfaceProvider.getMulticastNetworkInterfaces(); 106 InetSocketAddress multicastAddress = MULTICAST_IPV4_ADDRESS; 107 if (multicastNetworkInterfaceProvider.isOnIpV6OnlyNetwork(networkInterfaces)) { 108 multicastAddress = MULTICAST_IPV6_ADDRESS; 109 } 110 for (NetworkInterfaceWrapper networkInterface : networkInterfaces) { 111 multicastSocket.leaveGroup(multicastAddress, networkInterface.getNetworkInterface()); 112 if (!isOnIPv6OnlyNetwork) { 113 multicastSocket.leaveGroup( 114 MULTICAST_IPV6_ADDRESS, networkInterface.getNetworkInterface()); 115 } 116 } 117 } 118 close()119 public void close() { 120 // This is a race with the use of the file descriptor (b/27403984). 121 multicastSocket.close(); 122 } 123 124 /** 125 * Returns the index of the network interface that this socket is bound to. If the interface 126 * cannot be determined, returns -1. 127 */ getInterfaceIndex()128 public int getInterfaceIndex() { 129 if (multicastSocket.isClosed()) { 130 sharedLog.e("Socket is closed"); 131 return -1; 132 } 133 try { 134 return multicastSocket.getNetworkInterface().getIndex(); 135 } catch (SocketException | NullPointerException e) { 136 sharedLog.e("Failed to retrieve interface index for socket.", e); 137 return -1; 138 } 139 } 140 141 /** 142 * Returns the available network that this socket is used to, or null if the network is unknown. 143 */ 144 @Nullable getNetwork()145 public Network getNetwork() { 146 return multicastNetworkInterfaceProvider.getAvailableNetwork(); 147 } 148 isOnIPv6OnlyNetwork()149 public boolean isOnIPv6OnlyNetwork() { 150 return isOnIPv6OnlyNetwork; 151 } 152 }