/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 java.net; import dalvik.system.CloseGuard; import java.io.FileDescriptor; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocketImpl; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import libcore.io.ErrnoException; import libcore.io.IoBridge; import libcore.io.Libcore; import libcore.io.StructGroupReq; import libcore.util.EmptyArray; import static libcore.io.OsConstants.*; /** * @hide used in java.nio. */ public class PlainDatagramSocketImpl extends DatagramSocketImpl { private volatile boolean isNativeConnected; private final CloseGuard guard = CloseGuard.get(); /** * used to keep address to which the socket was connected to at the native * level */ private InetAddress connectedAddress; private int connectedPort = -1; public PlainDatagramSocketImpl(FileDescriptor fd, int localPort) { this.fd = fd; this.localPort = localPort; if (fd.valid()) { guard.open("close"); } } public PlainDatagramSocketImpl() { fd = new FileDescriptor(); } @Override public void bind(int port, InetAddress address) throws SocketException { IoBridge.bind(fd, address, port); if (port != 0) { localPort = port; } else { localPort = IoBridge.getSocketLocalPort(fd); } try { setOption(SocketOptions.SO_BROADCAST, Boolean.TRUE); } catch (IOException ignored) { } } @Override public synchronized void close() { guard.close(); try { IoBridge.closeSocket(fd); } catch (IOException ignored) { } } @Override public void create() throws SocketException { this.fd = IoBridge.socket(false); } @Override protected void finalize() throws Throwable { try { if (guard != null) { guard.warnIfOpen(); } close(); } finally { super.finalize(); } } @Override public Object getOption(int option) throws SocketException { return IoBridge.getSocketOption(fd, option); } @Override public int getTimeToLive() throws IOException { return (Integer) getOption(IoBridge.JAVA_IP_MULTICAST_TTL); } @Override public byte getTTL() throws IOException { return (byte) getTimeToLive(); } private static StructGroupReq makeGroupReq(InetAddress gr_group, NetworkInterface networkInterface) { int gr_interface = (networkInterface != null) ? networkInterface.getIndex() : 0; return new StructGroupReq(gr_interface, gr_group); } @Override public void join(InetAddress addr) throws IOException { setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null)); } @Override public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException { if (addr instanceof InetSocketAddress) { InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface)); } } @Override public void leave(InetAddress addr) throws IOException { setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null)); } @Override public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException { if (addr instanceof InetSocketAddress) { InetAddress groupAddr = ((InetSocketAddress) addr).getAddress(); setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface)); } } @Override protected int peek(InetAddress sender) throws IOException { // We don't actually want the data: we just want the DatagramPacket's filled-in address. DatagramPacket packet = new DatagramPacket(EmptyArray.BYTE, 0); int result = peekData(packet); // Note: evil side-effect on InetAddress! This method should have returned InetSocketAddress! sender.ipaddress = packet.getAddress().getAddress(); return result; } private void doRecv(DatagramPacket pack, int flags) throws IOException { IoBridge.recvfrom(false, fd, pack.getData(), pack.getOffset(), pack.getLength(), flags, pack, isNativeConnected); if (isNativeConnected) { updatePacketRecvAddress(pack); } } @Override public void receive(DatagramPacket pack) throws IOException { doRecv(pack, 0); } @Override public int peekData(DatagramPacket pack) throws IOException { doRecv(pack, MSG_PEEK); return pack.getPort(); } @Override public void send(DatagramPacket packet) throws IOException { int port = isNativeConnected ? 0 : packet.getPort(); InetAddress address = isNativeConnected ? null : packet.getAddress(); IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port); } public void setOption(int option, Object value) throws SocketException { IoBridge.setSocketOption(fd, option, value); } @Override public void setTimeToLive(int ttl) throws IOException { setOption(IoBridge.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl)); } @Override public void setTTL(byte ttl) throws IOException { setTimeToLive((int) ttl & 0xff); // Avoid sign extension. } @Override public void connect(InetAddress inetAddr, int port) throws SocketException { IoBridge.connect(fd, inetAddr, port); // Throws on failure. try { connectedAddress = InetAddress.getByAddress(inetAddr.getAddress()); } catch (UnknownHostException e) { // this is never expected to happen as we should not have gotten // here if the address is not resolvable throw new SocketException("Host is unresolved: " + inetAddr.getHostName()); } connectedPort = port; isNativeConnected = true; } @Override public void disconnect() { try { Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0); } catch (ErrnoException errnoException) { throw new AssertionError(errnoException); } connectedPort = -1; connectedAddress = null; isNativeConnected = false; } /** * Set the received address and port in the packet. We do this when the * Datagram socket is connected at the native level and the * recvConnnectedDatagramImpl does not update the packet with address from * which the packet was received * * @param packet * the packet to be updated */ private void updatePacketRecvAddress(DatagramPacket packet) { packet.setAddress(connectedAddress); packet.setPort(connectedPort); } }