Home | History | Annotate | Download | only in net
      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