1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.net; 19 20 import dalvik.system.CloseGuard; 21 import java.io.FileDescriptor; 22 import java.io.IOException; 23 import java.net.DatagramPacket; 24 import java.net.DatagramSocketImpl; 25 import java.net.InetAddress; 26 import java.net.InetSocketAddress; 27 import java.net.NetworkInterface; 28 import java.net.SocketAddress; 29 import java.net.SocketException; 30 import java.net.UnknownHostException; 31 import libcore.io.ErrnoException; 32 import libcore.io.IoBridge; 33 import libcore.io.Libcore; 34 import libcore.io.StructGroupReq; 35 import libcore.util.EmptyArray; 36 import static libcore.io.OsConstants.*; 37 38 /** 39 * @hide used in java.nio. 40 */ 41 public class PlainDatagramSocketImpl extends DatagramSocketImpl { 42 43 private volatile boolean isNativeConnected; 44 45 private final CloseGuard guard = CloseGuard.get(); 46 47 /** 48 * used to keep address to which the socket was connected to at the native 49 * level 50 */ 51 private InetAddress connectedAddress; 52 53 private int connectedPort = -1; 54 55 public PlainDatagramSocketImpl(FileDescriptor fd, int localPort) { 56 this.fd = fd; 57 this.localPort = localPort; 58 if (fd.valid()) { 59 guard.open("close"); 60 } 61 } 62 63 public PlainDatagramSocketImpl() { 64 fd = new FileDescriptor(); 65 } 66 67 @Override public void bind(int port, InetAddress address) throws SocketException { 68 IoBridge.bind(fd, address, port); 69 if (port != 0) { 70 localPort = port; 71 } else { 72 localPort = IoBridge.getSocketLocalPort(fd); 73 } 74 try { 75 setOption(SocketOptions.SO_BROADCAST, Boolean.TRUE); 76 } catch (IOException ignored) { 77 } 78 } 79 80 @Override 81 public synchronized void close() { 82 guard.close(); 83 try { 84 IoBridge.closeSocket(fd); 85 } catch (IOException ignored) { 86 } 87 } 88 89 @Override 90 public void create() throws SocketException { 91 this.fd = IoBridge.socket(false); 92 } 93 94 @Override protected void finalize() throws Throwable { 95 try { 96 if (guard != null) { 97 guard.warnIfOpen(); 98 } 99 close(); 100 } finally { 101 super.finalize(); 102 } 103 } 104 105 @Override public Object getOption(int option) throws SocketException { 106 return IoBridge.getSocketOption(fd, option); 107 } 108 109 @Override 110 public int getTimeToLive() throws IOException { 111 return (Integer) getOption(IoBridge.JAVA_IP_MULTICAST_TTL); 112 } 113 114 @Override 115 public byte getTTL() throws IOException { 116 return (byte) getTimeToLive(); 117 } 118 119 private static StructGroupReq makeGroupReq(InetAddress gr_group, NetworkInterface networkInterface) { 120 int gr_interface = (networkInterface != null) ? networkInterface.getIndex() : 0; 121 return new StructGroupReq(gr_interface, gr_group); 122 } 123 124 @Override 125 public void join(InetAddress addr) throws IOException { 126 setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null)); 127 } 128 129 @Override 130 public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException { 131 if (addr instanceof InetSocketAddress) { 132 InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); 133 setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface)); 134 } 135 } 136 137 @Override 138 public void leave(InetAddress addr) throws IOException { 139 setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null)); 140 } 141 142 @Override 143 public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException { 144 if (addr instanceof InetSocketAddress) { 145 InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); 146 setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface)); 147 } 148 } 149 150 @Override 151 protected int peek(InetAddress sender) throws IOException { 152 // We don't actually want the data: we just want the DatagramPacket's filled-in address. 153 DatagramPacket packet = new DatagramPacket(EmptyArray.BYTE, 0); 154 int result = peekData(packet); 155 // Note: evil side-effect on InetAddress! This method should have returned InetSocketAddress! 156 sender.ipaddress = packet.getAddress().getAddress(); 157 return result; 158 } 159 160 private void doRecv(DatagramPacket pack, int flags) throws IOException { 161 IoBridge.recvfrom(false, fd, pack.getData(), pack.getOffset(), pack.getLength(), flags, pack, isNativeConnected); 162 if (isNativeConnected) { 163 updatePacketRecvAddress(pack); 164 } 165 } 166 167 @Override 168 public void receive(DatagramPacket pack) throws IOException { 169 doRecv(pack, 0); 170 } 171 172 @Override 173 public int peekData(DatagramPacket pack) throws IOException { 174 doRecv(pack, MSG_PEEK); 175 return pack.getPort(); 176 } 177 178 @Override 179 public void send(DatagramPacket packet) throws IOException { 180 int port = isNativeConnected ? 0 : packet.getPort(); 181 InetAddress address = isNativeConnected ? null : packet.getAddress(); 182 IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port); 183 } 184 185 public void setOption(int option, Object value) throws SocketException { 186 IoBridge.setSocketOption(fd, option, value); 187 } 188 189 @Override 190 public void setTimeToLive(int ttl) throws IOException { 191 setOption(IoBridge.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl)); 192 } 193 194 @Override 195 public void setTTL(byte ttl) throws IOException { 196 setTimeToLive((int) ttl & 0xff); // Avoid sign extension. 197 } 198 199 @Override 200 public void connect(InetAddress inetAddr, int port) throws SocketException { 201 IoBridge.connect(fd, inetAddr, port); // Throws on failure. 202 try { 203 connectedAddress = InetAddress.getByAddress(inetAddr.getAddress()); 204 } catch (UnknownHostException e) { 205 // this is never expected to happen as we should not have gotten 206 // here if the address is not resolvable 207 throw new SocketException("Host is unresolved: " + inetAddr.getHostName()); 208 } 209 connectedPort = port; 210 isNativeConnected = true; 211 } 212 213 @Override 214 public void disconnect() { 215 try { 216 Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0); 217 } catch (ErrnoException errnoException) { 218 throw new AssertionError(errnoException); 219 } 220 connectedPort = -1; 221 connectedAddress = null; 222 isNativeConnected = false; 223 } 224 225 /** 226 * Set the received address and port in the packet. We do this when the 227 * Datagram socket is connected at the native level and the 228 * recvConnnectedDatagramImpl does not update the packet with address from 229 * which the packet was received 230 * 231 * @param packet 232 * the packet to be updated 233 */ 234 private void updatePacketRecvAddress(DatagramPacket packet) { 235 packet.setAddress(connectedAddress); 236 packet.setPort(connectedPort); 237 } 238 } 239