blob: fabed977af5c4c7801fa9dc9f423b528040d8ea5 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed 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 libcore.java.net;
import java.lang.reflect.Field;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.DatagramSocketImpl;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import libcore.junit.junit3.TestCaseWithRules;
import libcore.junit.util.ResourceLeakageDetector;
import org.junit.Rule;
import org.junit.rules.TestRule;
public class DatagramSocketTest extends TestCaseWithRules {
@Rule
public TestRule resourceLeakageDetectorRule = ResourceLeakageDetector.getRule();
public void testInitialState() throws Exception {
DatagramSocket ds = new DatagramSocket();
try {
assertTrue(ds.isBound());
assertTrue(ds.getBroadcast()); // The RI starts DatagramSocket in broadcast mode.
assertFalse(ds.isClosed());
assertFalse(ds.isConnected());
assertTrue(ds.getLocalPort() > 0);
assertTrue(ds.getLocalAddress().isAnyLocalAddress());
InetSocketAddress socketAddress = (InetSocketAddress) ds.getLocalSocketAddress();
assertEquals(ds.getLocalPort(), socketAddress.getPort());
assertEquals(ds.getLocalAddress(), socketAddress.getAddress());
assertNull(ds.getInetAddress());
assertEquals(-1, ds.getPort());
assertNull(ds.getRemoteSocketAddress());
assertFalse(ds.getReuseAddress());
assertNull(ds.getChannel());
} finally {
ds.close();
}
}
public void testStateAfterClose() throws Exception {
DatagramSocket ds = new DatagramSocket();
ds.close();
assertTrue(ds.isBound());
assertTrue(ds.isClosed());
assertFalse(ds.isConnected());
assertNull(ds.getLocalAddress());
assertEquals(-1, ds.getLocalPort());
assertNull(ds.getLocalSocketAddress());
}
public void testPendingException() throws Exception {
final int port = 9999;
try (DatagramSocket s = new DatagramSocket()) {
forceConnectToThrowSocketException(s);
s.connect(InetAddress.getLocalHost(), port);
byte[] data = new byte[100];
DatagramPacket p = new DatagramPacket(data, data.length);
// Confirm send() throws the pendingConnectException.
try {
s.send(p);
fail();
} catch (SocketException expected) {
assertTrue(expected.getMessage().contains("Pending connect failure"));
}
// Confirm receive() throws the pendingConnectException.
try {
s.receive(p);
fail();
} catch (SocketException expected) {
assertTrue(expected.getMessage().contains("Pending connect failure"));
}
// Confirm that disconnect() doesn't throw a runtime exception.
s.disconnect();
}
}
public void test_setTrafficClass() throws Exception {
try (DatagramSocket s = new DatagramSocket()) {
for (int i = 0; i <= 255; ++i) {
s.setTrafficClass(i);
assertEquals(i, s.getTrafficClass());
}
}
}
// DatagramSocket should "become connected" even when impl.connect() fails and throws an
// exception.
public void test_b31218085() throws Exception {
final int port = 9999;
try (DatagramSocket s = new DatagramSocket()) {
forceConnectToThrowSocketException(s);
s.connect(InetAddress.getLocalHost(), port);
assertTrue(s.isConnected());
// Confirm that disconnect() doesn't throw a runtime exception.
s.disconnect();
}
}
public void testForceConnectToThrowSocketException() throws Exception {
// Unlike connect(InetAddress, int), connect(SocketAddress) can (and should) throw an
// exception after a call to forceConnectToThrowSocketException(). The
// forceConnectToThrowSocketException() method is used in various tests for
// connect(InetAddress, int) and this test exists to confirm it stays working.
SocketAddress validAddress = new InetSocketAddress(InetAddress.getLocalHost(), 9999);
try (DatagramSocket s1 = new DatagramSocket()) {
s1.connect(validAddress);
s1.disconnect();
}
try (DatagramSocket s2 = new DatagramSocket()) {
forceConnectToThrowSocketException(s2);
try {
s2.connect(validAddress);
} catch (SocketException expected) {
}
s2.disconnect();
}
}
// DatagramSocket should ignore packets received from other sources prior to connect().
// CVE-2014-6512
// b/31586706
public void testExplicitFilter() throws Exception {
final byte[] data = new byte[]{1, 2, 3, 4};
try (DatagramSocket dgramSocket = new DatagramSocket();
DatagramSocket otherSocket = new DatagramSocket()) {
otherSocket.connect(dgramSocket.getLocalSocketAddress());
otherSocket.send(new DatagramPacket(data, data.length));
dgramSocket.setSoTimeout(100);
dgramSocket.connect(new InetSocketAddress(0));
// Packet from otherSocket was sent to ds before ds is connected, and it was stored in
// dgramSocket's local buffer. After connect(), dgramSocket should discard this packet
// from the buffer since it is not sent from the connected socket address.
try {
DatagramPacket recv = new DatagramPacket(new byte[data.length], data.length);
dgramSocket.receive(recv);
fail();
} catch (SocketTimeoutException expected) { }
}
try (DatagramSocket dgramSocket = new DatagramSocket();
DatagramSocket srcSocket = new DatagramSocket()) {
srcSocket.connect(dgramSocket.getLocalSocketAddress());
srcSocket.send(new DatagramPacket(data, data.length));
dgramSocket.setSoTimeout(100);
dgramSocket.connect(srcSocket.getLocalSocketAddress());
// If the packet is sent from the connected address, even though that is before connect(), it
// should not be dropped and receive() should succeed.
DatagramPacket recv = new DatagramPacket(new byte[data.length], data.length);
dgramSocket.receive(recv);
assertTrue(Arrays.equals(recv.getData(), data));
}
}
private static void forceConnectToThrowSocketException(DatagramSocket s) throws Exception {
// Set fd of DatagramSocketImpl to null, forcing impl.connect() to throw a SocketException
// (Socket closed).
Field f = DatagramSocket.class.getDeclaredField("impl");
f.setAccessible(true);
DatagramSocketImpl impl = (DatagramSocketImpl) f.get(s);
f = DatagramSocketImpl.class.getDeclaredField("fd");
f.setAccessible(true);
f.set(impl, null);
}
public void testAddressSameIfUnchanged() throws Exception {
try (DatagramSocket ds = new DatagramSocket();
DatagramSocket srcDs = new DatagramSocket()) {
ds.setSoTimeout(1000);
srcDs.connect(ds.getLocalSocketAddress());
srcDs.send(new DatagramPacket(new byte[16], 16));
srcDs.send(new DatagramPacket(new byte[16], 16));
DatagramPacket p = new DatagramPacket(new byte[16], 16);
ds.receive(p);
InetAddress packetAddr = p.getAddress();
// This time the packet should have the same address as source address, and it's address
// should remain the same object.
ds.receive(p);
InetAddress newPacketAddr = p.getAddress();
assertTrue(packetAddr.isLoopbackAddress());
assertSame(packetAddr, newPacketAddr);
}
}
}