1/*
2 * Copyright (C) 2010 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
17package libcore.net;
18
19import dalvik.system.CloseGuard;
20import java.io.Closeable;
21import java.io.FileDescriptor;
22import java.io.IOException;
23import java.net.SocketException;
24import java.util.Arrays;
25import libcore.io.IoBridge;
26
27/**
28 * This class allows raw L2 packets to be sent and received via the
29 * specified network interface.  The receive-side implementation is
30 * restricted to UDP packets for efficiency.
31 *
32 * @hide
33 */
34public class RawSocket implements Closeable {
35    /**
36     * Ethernet IP protocol type, part of the L2 header of IP packets.
37     */
38    public static final short ETH_P_IP = (short) 0x0800;
39
40    /**
41     * Ethernet ARP protocol type, part of the L2 header of ARP packets.
42     */
43    public static final short ETH_P_ARP = (short) 0x0806;
44
45    private static native void create(FileDescriptor fd, short
46            protocolType, String interfaceName)
47            throws SocketException;
48    private static native int sendPacket(FileDescriptor fd,
49        String interfaceName, short protocolType, byte[] destMac, byte[] packet,
50        int offset, int byteCount);
51    private static native int recvPacket(FileDescriptor fd, byte[] packet,
52        int offset, int byteCount, int destPort, int timeoutMillis);
53
54    private final FileDescriptor fd;
55    private final String mInterfaceName;
56    private final short mProtocolType;
57    private final CloseGuard guard = CloseGuard.get();
58
59    /**
60     * Creates a socket on the specified interface.
61     */
62    public RawSocket(String interfaceName, short protocolType)
63        throws SocketException {
64        mInterfaceName = interfaceName;
65        mProtocolType = protocolType;
66        fd = new FileDescriptor();
67        create(fd, mProtocolType, mInterfaceName);
68        guard.open("close");
69    }
70
71    /**
72     * Reads a raw packet into the specified buffer, with the
73     * specified timeout.  If the destPort is -1, then the IP
74     * destination port is not verified, otherwise only packets
75     * destined for the specified UDP port are returned.  Returns the
76     * length actually read.  No indication of overflow is signaled.
77     * The packet data will start at the IP header (EthernetII
78     * dest/source/type headers are removed).
79     */
80    public int read(byte[] packet, int offset, int byteCount, int destPort,
81        int timeoutMillis) {
82        if (packet == null) {
83            throw new NullPointerException("packet == null");
84        }
85
86        Arrays.checkOffsetAndCount(packet.length, offset, byteCount);
87
88        if (destPort > 65535) {
89            throw new IllegalArgumentException("Port out of range: "
90                + destPort);
91        }
92
93        return recvPacket(fd, packet, offset, byteCount, destPort,
94            timeoutMillis);
95    }
96
97    /**
98     * Writes a raw packet to the desired interface.  A L2 header will
99     * be added which includes the specified destination address, our
100     * source MAC, and the specified protocol type.  The caller is responsible
101     * for computing correct IP-header and payload checksums.
102     */
103    public int write(byte[] destMac, byte[] packet, int offset, int byteCount) {
104        if (destMac == null) {
105            throw new NullPointerException("destMac == null");
106        }
107
108        if (packet == null) {
109            throw new NullPointerException("packet == null");
110        }
111
112        Arrays.checkOffsetAndCount(packet.length, offset, byteCount);
113
114        if (destMac.length != 6) {
115            throw new IllegalArgumentException("MAC length must be 6: "
116                + destMac.length);
117        }
118
119        return sendPacket(fd, mInterfaceName, mProtocolType, destMac, packet,
120            offset, byteCount);
121    }
122
123    /**
124     * Closes the socket.  After this method is invoked, subsequent
125     * read/write operations will fail.
126     */
127    public void close() throws IOException {
128        guard.close();
129        IoBridge.closeSocket(fd);
130    }
131
132    @Override protected void finalize() throws Throwable {
133        try {
134            if (guard != null) {
135                guard.warnIfOpen();
136            }
137            close();
138        } finally {
139            super.finalize();
140        }
141    }
142}
143